Advanced Programming in the UNIX Environment

1 I/O

1.1 文件 I/O

通过文件描述符标识。对于内核而言,所有打开文件都通过文件描述符引用。 其也是有缓冲的,只是其缓冲区在内核空间,不再用户空间。体现在延迟写,只在适当的时候才调用写文件操作, 减少不必要的写操作,增加性能。 函数 open() 打开文件时,指定模式必须有且仅有 O _RDONLY / O _WRONLY / O _RDWR 三者中的一个。 选项 O _APPEND 指定为追加。原来的 UNIX 系统不支持追加,只能先使用 lseek() 先设置文件的偏移量到文件的 结尾,然后再写。但是这在多个进程同时写的时候会出问题,因为调用了两个函数来追加,所以不是一个原子操作。 而追加选项确保设置偏移和写操作为原子操作。

1.1.1 管道读写

写管道的时候不用使用追加选项,内核会自动按写的顺序写入管道。读取后自动将相应的内容从管道清除。 读取一个写端关闭的管道,在读取完全部数据后,read 函数返回 0 ; 写一个读端关闭的管道,产生 SIGPIPE 信号,设置该信号处理函数或者忽略该信号,write 返回 -1,且 errno 设置为 EPIPE。 读管道,如果管道为空,调用线程阻塞,同进程内的其他线程不受影响。

1.1.2 套接字描述符

虽然套接字描述符本质上是一个文件描述符,但不是所有参数为文件描述符的函数都可以接受套接字描述符。 套接字不支持文件偏移量概念,不能使用 lseek 函数,也不可以使用 mmap 函数,不可调用 fchdir 函数。

shutdown 函数可以直接关闭一个套接字的读端或者写端,不管该套接字描述符复制了多少分; close 函数只有在关闭最后一个套接字的时候才会释放该套接字。

1.1.3 改变文件偏移量

lseek

1.2 标准库 I/O

标准 I/O 库的操作围绕流(stream)进行。利用指向 FILE 对象维护,该结构体包含了标准 I/O 库为了维护该流所 需要的信息:文件描述符,指向缓冲区的指针,缓冲区的长度,缓冲区中当前的字符数,出错标志等。不需要关心 FILE 结构的具体形式。 相对于文件 I/O,标准库的 I/O 都是带缓冲的,标准库维护了一个缓冲区,在适当的时候才调用 read、write 函 数,从而减少系统调用的开销。

1.2.1 定位流

ftell、fseek、ftello、fseek、fgetpos、fsetpos

2 IPC

IPC 传统上是 UNIX 中一个杂乱不堪的领域,虽然有了各种各样的解决办法,但没有一个是完美的。可分为四个主 要领域:

  1. 消息传递 : 管道、FIFO、消息队列
  2. 同步 : 互斥锁、条件变量、读写锁、信号量
  3. 共享内存区 : 匿名共享内存区、命令共享内存区
  4. 过程调用 : Solaris 门、Sun RPC

包括单个进程内多个线程的 IPC 和多个进程间的 IPC 。

2.1 消息边界

2.1.1 无边界

管道和 FIFO 是字节流,没有消息边界; TCP 没有记录边界的字节流;

2.1.2 有边界

POSIX 消息和 System V 消息有从发送者向接收者维护的边界; UDP 提供具有边界记录的消息;

2.2 窥探能力

2.2.1 可以窥探

socket recv、recvfrom 函数可以使用标志 MSG _PEEK 从接收队列读取数据,且系统不在读取之后丢弃这些数据;

2.2.2 不可窥探

管道、FIFO、POSIX 消息、System V 消息 只有一个副本递交到一个线程,且消息不能广播或多播到多个接收者(UDP 广播、多播)

2.3 读取顺序

2.3.1 先进先出

管道、FIFO

2.3.2 优先级最高的最早消息

POSIX 消息

2.3.3 指定优先级的消息

System V 消息

2.4 对象持续性

2.4.1 随进程持续 process-persistent

IPC 对象一直存在到打开着该对象的最后一个进程关闭该对象为止。没有 unlink 函数。 管道、FIFO、POSIX 互斥锁、POSIX 条件变量、POSIX 读写锁、POSIX 基于内存的信号量、fcntl 记录锁、TCP 套 接字、UDP 套接字、Unix 域套接字

2.4.2 随内核持续 kernel-persistent

一直存在到内核重新自举或显示删除该 IPC 对象。存在对应的 unlink 函数。 POSIX 消息队列、POSIX 有名信号量、POSIX 共享内存区、System V 消息队列、System V 信号量、System V 共 享内存

2.4.3 随文件系统持续 filesystem-persistent

一直存在到显示删除该 IPC 对象为止。 IPC 的一个基本设计目标是高性能,而具备随文件系统的持续性可能会使其性能降级,而且进程不可能跨越自举继 续存活。

2.5 名字空间 – name space

一种给定的 IPC 类型,其可能名字的集合称为名字空间。名字空间非常重要,因为名字是客户与服务器彼此连接 以交换消息的手段。

IPC 类型 打开或创建 IPC 的名字空间 IPC 打开后的标识
管道 NA 描述符
FIFO 路径名 描述符
POSIX 互斥锁 NA pthread _mutex _t 指针
POSIX 条件变量 NA pthread _cond _t 指针
POSIX 读写锁 NA pthread _rwlock _t 指针
fcntl 记录上锁 路径名 描述符
POSIX 消息队列 POSIX IPC 名字 mqd _t 值
POSIX 命名信号量 POSIX IPC 名字 sem _t 指针
POSIX 共享内存 POSIX IPC 名字 描述符
POSIX 基于内存的信号量 NA sem _t 指针
TCP 套接字 IP 地址与 TCP 端口 描述符
UDP 套接字 IP 地址与 UDP 端口 描述符
Unix 域套接字 路径名 描述符
Sun RPC 程序/版本 RPC 句柄
路径名 描述符
System V 消息队列 key _t 键 System V IPC 标识符
System V 信号量 key _t 键 System V IPC 标识符
System V 共享内存 key _t 键 System V IPC 标识符

2.5.1 打开 IPC 名字空间

  1. 无名 IPC

    管道、POSIX 互斥锁、POSIX 条件变量、POSIX 读写锁、POSIX 基于内存的信号量;

    1. 无名 IPC 也可为进程所共享

      互斥锁、条件变量、读写锁、POSIX 无名信号量都是无名的,也就是说他们是基于内存的。他们很容易为单个进程 内的不同线程所共享;当他们存放在不同进程间所共享的内存区中时,同时设置其进程共享属性,也可以为这些进 程所共享。

  2. 打开路径名 (path) 来打开 IPC

    FIFO、fcntl、Unix 域套接字、门;

  3. 打开 POSIX IPC 名字来打开 IPC

    POSIX 消息队列、POSIX 命令信号量、POSIX 共享内存;

  4. 基于 IP 地址和端口号来打开 IPC

    TCP 套接字、UDP 套接字;

  5. 基于 key _t 键打开 IPC

    System V 消息队列、System V 信号量、System V 共享内存;

2.5.2 打开 IPC 后的标识

  1. 描述符

    管道、FIFO、fcntl、POSIX 共享内存、TCP 套接字、UDP 套接字、Unix 虞域套接字、门

  2. 相应形式的指针
    • POSIX 互斥锁
    • POSIX 条件变量
    • POSIX 读写锁
    • POSIX 命名信号量
    • POSIX 基于内存的信号量

    • POSIX 消息队列
  3. System V IPC 标识符
    • System V 消息队列
    • System V 信号量
    • System V 共享内存
  4. RPC 句柄

    Sun RPC

2.5.3 需要定义结构体变量–指针

除了 Posix 信号量需要定义成指针,其他的都需要定义成变量。

  1. 结构体

    互斥锁、条件变量、读写锁、Posix 无名信号量、Posix 消息队列、 描述符、Posix 共享内存、

  2. 指针

    Posix 命名信号量、mmap、

2.6 fork、exec、exit 对 IPC 的影响

  • 无名同步变量(互斥锁、条件变量、读写锁、基于内存的信号量),从一个具有多线程的进程中调用 fork 将变得 混乱不堪;如果这些变量驻留在共享内存区中,而且创建时设置了进程共享属性,那么对于能访问该共享内存区的 任意进程来说,其任意线程能继续访问这些变量。
  • System V IPC 的三种形式没有打开或关闭的说法,只需知道其标识符即可访问。
IPC 类型 fork exec _exit
管道、FIFO 子进程取得父进程的所有打开着的文件描述符的副本 所有打开着的文件描述符继续打开,除非设置了 FD _CLOEXEC 位 关闭所有打开着的描述符,最后一个关闭时删除管道或 FIFO 中残留的数据
POSIX 消息队列 子进程取得父进程的所有打开着的消息队列描述符的副本 关闭所有打开着消息队列描述符 关闭所有打开着的消息队列描述符
System V 消息队列 -- -- --
POSIX 互斥锁和条件变量 若驻留在共享内存区中而且具有进程间共享属性,则共享 除非在继续打开着的共享内存区中而且具有进程间共享属性,否则消失 除非在继续打开着的共享内存区中而且具有进程间共享属性,否则消失
POSIX 读写锁 若驻留在共享内存区中而且具有进程间共享属性,则共享 除非在继续打开着的共享内存区中而且具有进程间共享属性,否则消失 除非在继续打开着的共享内存区中而且具有进程间共享属性,否则消失
POSIX 基于内存的信号量 若驻留在共享内存区中而且具有进程间共享属性,则共享 除非在继续打开着的共享内存区中而且具有进程间共享属性,否则消失 除非在继续打开着的共享内存区中而且具有进程间共享属性,否则消失
POSIX 命名信号量 父进程中所有打开着的命名信号量在子进程中继续打开着 关闭所有打开着的命名信号量 关闭所有打开着的命名信号量
fcntl 记录锁 子进程不继承父进程持有的锁 只要描述符继续打开着,锁就不变 解开由进程持有的所有未处理的锁
System V 信号量 子进程中所有 semadj 值都置位 0 所有 semadj 值都携入新进程 所有 semadj 值都加到相应的信号量值上
mmap 内存映射 父进程的内存映射存留到子进程中 去除内存映射 去除内存映射
POSIX 共享内存区 父进程中内存映射存留到子进程中 去除内存映射 去除内存映射
System V 共享内存区 附接着的共享内存区在子进程中继续附接着 断开所有附接着的共享内存区 断开所有附接着的共享内存区
子进程取得父进程的所有打开着的描述符,但是客户在门描述符上激活其过程时,只有父进程是服务器 所有门描述符都应关闭,因为它们创建时设置了 FD _CLOEXEC 位 关闭所有打开着的描述符

2.7 锁释放

2.7.1 内核自动释放

持有某个锁的进程没有释放就终止,内核自动释放该锁; fcntl 记录锁、System V 信号量(可选项)

2.7.2 无法释放锁

互斥锁、条件变量、读写锁、POSIX 信号量

2.8 进程、线程与共享信息

没有任何东西限制任何 IPC 技术只适用于两个进程。

  1. 两个进程共享存留于文件系统中某个文件上的某些信息。为了访问这些信息,每个进程都得穿越内核(例如 read、write、lssk 等);需要某种形式的同步,如记录锁。
  2. 两个进程共享驻留于内核的某些信息,访问共享信息的每次操作都涉及对内核的一次系统调用。管道、 System V 消息队列、System V 信号量均是;
  3. 两个进程有一个双方都能访问的共享存储区,需要某种形式的同步(信号量等)。每个进程一旦设置好该共享 内存区,就能根本不涉及内核而访问其中的数据。

3 进程 – 线程

3.1 设计线程的原因:

  1. fork 的开销很大。内存映射要从父进程复制到子进程,所有描述符要在子进程复制一份。尽管存在写时复制 (copy-on-write) 的技术,fork 的开销仍然很大。
  2. fork 子进程后,需要使用 IPC 在父子进程之间传递信息。

3.2 线程共享全局内存空间

一个进程内的所有线程共享同一个全局内存空间。使得线程间很容易共享信息,但这种易用性也带来了同步 (synchronization) 问题。

3.3 线程共享的资源

  • 全局内存空间
  • 进程指令
  • 打开的文件
  • 信号处理程序和信号处置
  • 当前工作目录
  • 用户 ID 和 组 ID

3.4 线程私有资源

  • 线程 ID
  • 寄存器集合,包括程序计数器和栈指针
  • 栈,存放局部变量和返回地址
  • errno
  • 信号掩码
  • 优先级

4 系统开销

执行一般命令 1ns = 1/1,000,000,000s
从 L1 级缓存读 0.5ns
分支误预测 5ns
L2 级缓存读 7ns
加锁、解锁 25ns
主存中读 100ns
1Gbps 网络中发 2k 数据 20,000ns
主存中读 1MB 序列 250,000ns
查找从硬盘中读 8,000,000ns
硬盘读 1MB 序列 20,000,000ns
报文从美国发到欧洲并返回 150ms = 150,000,000ns

5 练习代码

5.1 基本函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
#include <stdio.h>

#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/acct.h>
#include <sys/types.h>
#include <sys/time.h>
#include <pwd.h>
#include <sys/resource.h>
#include <errno.h>
#include <sys/times.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>


int globalNum = 522;

void echoAllID(char *processName);
void forkTest();
void vforkTest();
void exitTest();
void echoExitStatus(int status);
void noZombie();
void accountingRecored();
static unsigned long compt2ulong(comp_t comptime);
void showAcctTime();
void testExecl();
void testSystem();
void testUserloginname();
void checkTime(char *processName,
struct timeval *endtv,
unsigned long long int *count)
;

void testNice();
void showProcessTime(clock_t realtime,
struct tms *tms_start,
struct tms *tms_end)
;

void testCmdTime();
void testZombie();
void printfRevSignal(int signo);
void testSignal();
void testPipe();
void test_mq();

void cleanBuf(void);
void releaseResource(void);

int main(int argc, char **argv)
{

atexit(releaseResource);
atexit(cleanBuf);

setbuf(stdout, NULL);
printf("stdin's fileno \t %d\n", fileno(stdin));
printf("stdout's fileno \t %d\n", fileno(stdout));
printf("stderr's fileno \t %d\n", fileno(stderr));
//echoAllID(argv[0]);
//forkTest();
//vforkTest();
//exitTest();

//noZombie();

//accountingRecored();
//showAcctTime();

//testExecl();

//testSystem();

//testUserloginname();

//testNice();

//testCmdTime();

//testZombie();

//testSignal();

test_mq();

testPipe();
exit(0);
}


void echoAllID(char *processName)
{

pid_t pid;
pid_t ppid;
uid_t uid;
uid_t euid;
gid_t gid;
gid_t egid;

//Note that none of these functions has an error return.
pid = getpid();
ppid = getppid();
uid = getuid();
euid = geteuid();
gid = getgid();
egid = getegid();

printf("The %s process\n"
"pid = %d, ppid = %d\n"
"uid = %d, euid = %d\n"
"gid = %d, egid = %d\n",
processName,
pid, ppid,
uid, euid,
gid, egid);
}


void forkTest()
{

int num = 99;
char buf[] = "Hello Kyle!\n";
pid_t pid;

//The write function is not buffered, its data is written once to
//standard output before fork.
if((sizeof(buf) - 1) !=
write(STDOUT_FILENO, buf, sizeof(buf) - 1))
{
exit(1);
}

//The standard I/O library is buffered. Standard output is line
//buffered if it's connected to a terminal device; otherwise,
//it's fully buffered.
printf("Before fork\n");
//The printf before the fork is called once, but the line remains
//in buffer when fork is called. This is then copied into the child
//when the patent's data space is copied to child. Both parent and
//the child now have a standard I/O buffer with this line in it.
//The second printf, right before the exit, just appends its data
//to the existing buffer. When each process terminates, its copy
//of the buffer is funally flushed.

if((pid = fork()) < 0)
{
exit(1);
}
else if(0 == pid)
{
//changes to variables in a child process do not affect the value
//of the variables in the parent process.
//sleep(2);
num = 1314;
globalNum = 520;
}
else
{
//Let the child process execute, but there is no guarantee.
sleep(2);
//exit(0);
}

printf("pid = %d, ppid = %d: num = %d, globalNum = %d\n",
getpid(), getppid(), num, globalNum);

//exit(0);
}


void vforkTest()
{

int num = 99;
char buf[] = "Hello Kyle!\n";
pid_t pid;


//child will not copy the memory of parent after vfork, just use it
printf("Before vfork\n");

if((pid = vfork()) < 0)
{
exit(1);
}
else if(0 == pid)
{
//modify parent's varables
num = 1314;
globalNum = 520;
_exit(0);//parent will not execute before child call exec or exit
//_exit will not flush the buffer, but exit will
}

printf("pid = %d, ppid = %d: num = %d, globalNum = %d\n",
getpid(), getppid(), num, globalNum);

exit(0);
}


void echoExitStatus(int status)
{

if(WIFEXITED(status))
{
printf("normal termination, exit status = %d\n",
WEXITSTATUS(status));
}

if(WIFSIGNALED(status))
{
printf("abnormal termination, exit status %d.%s\n",
WTERMSIG(status),

#ifdef WCOREDUMP
(WCOREDUMP(status)) ? "(a core file was generated)" : "(no core)");
#else
"");
#endif

}

if(WIFSTOPPED(status))
{
printf("process stopped by signal %d\n", WSTOPSIG(status));
}

if(WIFCONTINUED(status))
{
printf("process continued\n");
}
}



void exitTest()
{

pid_t pid;
int status;

if((pid = fork()) < 0)
exit(1);
else if(0 == pid)
exit(7);

if(wait(&status) != pid)//child exit, variable pid in here is parent's pid variable
exit(1);
else
echoExitStatus(status);

if((pid = fork()) < 0)
exit(1);
else if(0 == pid)
abort();

if(wait(&status) != pid)
exit(1);
else
echoExitStatus(status);

if((pid = fork()) < 0) {
exit(1);
}
else if(0 == pid) {
//status /= 0;
}

if(wait(&status) != pid)
exit(1);
else
echoExitStatus(status);
}


void noZombie()
{

pid_t pid;
int status;

if((pid = fork()) < 0)
exit(1);
else if(0 == pid)
{
if((pid = fork()) < 0)
exit(1);
else if(pid > 0)
{
exit(0);//first child do nothing and exit
}

//it's parent is the first child
//exec the process
sleep(2);
printf("I am the second child. pid=%d,ppid=%d.\n",
getpid(), getppid());
exit(0);
}

//I am not the parent of the second child
printf("I am the parent! And I don't need to see the child.\n");
//if(waitpid(pid, &status, WNOHANG) != pid)//nonblocking
if(waitpid(pid, &status, 0) != pid)
{
exit(1);
}
else
{
echoExitStatus(status);
}

exit(0);
}

//进程统计信息
//进程的各类信息在进程创建时初始化(由內核保存在进程表中),进程终止时写
//一个会计记录。
//a、永不终止的进程没有会计记录;
//b、只知道进程的起始时间,以日历时间来记,精确到秒,在 1s 内可能同时创建
//了很多进程)和运行时间(以 clock ticks 来记,每秒中有 60~128 ticks)
//但仅知道终止顺序(在会计文件中以进程的终止顺序来记录)无法得知进程精确的
//起始时间
//accounting recored 对应着进程而不是程序;一个进程多次调用 exec ,最后只
//会记录最后一个进程的命令名但运行时间是所有 exec 进程之和。fork 多次会记录
//多个 accounting recored
void accountingRecored() {
pid_t pid;

if((pid = fork()) < 0) {
exit(1);
}
else if(pid > 0) { //parent
sleep(25);
exit(2);
}

if((pid = fork()) < 0) {
exit(1);
}
else if(pid > 0) { //first child
sleep(4);
abort();
}

if((pid = fork()) < 0) {
exit(1);
}
else if(pid > 0) { //second child
execl("/bin/dd", "dd", "if=/etc/passwd", "of=/dev/null",
"count=1000", NULL);
}

if((pid = fork()) < 0) {
exit(1);
}
else if(pid > 0) { //third child
sleep(8);
exit(0);
}

sleep(6); //forth child
kill(getpid(), SIGKILL);
exit(6);
}


static unsigned long compt2ulong(comp_t comptime) {
unsigned long val = 0;
int exp = 0;

val = comptime & 0x7fff; //13 位小数部分
exp = (comptime >> 13) & 7; //指数部分
while(exp-- > 0) {
val *= 8;
}

return val;
}

void showAcctTime() {
struct acct acdata;
FILE *fp;

//需要安装 acct 程序。且需要 root 权限才能 accton on/off 以及读取记录文件
if((fp = fopen("/var/log/account/pacct", "r")) == NULL) {
printf("can't open the file\n");
//fflush(STDOUT_FILENO);
//write(STDOUT_FILENO, "can't open the file\n", sizeof("can't open the file\n") -1);
exit(1);
}

while(1 == fread(&acdata, sizeof(acct), 1, fp)) {
printf("%s etime=%ld io=%ld %c %c %c %c\n",
acdata.ac_comm,
compt2ulong(acdata.ac_etime),
compt2ulong(acdata.ac_io),
acdata.ac_flag & AFORK ? 'F' : ' ',
acdata.ac_flag & ASU ? 'S' : ' ',
acdata.ac_flag & ACORE ? 'C' : ' ',
acdata.ac_flag & AXSIG ? 'X' : ' ');
}
}


void testExecl() {
pid_t pid = 0;
int status = -1;

if((pid = fork()) < 0) {
exit(1);
}
else if(0 == pid) {
//exec 函数第一个参数只想可执行文件 execute file
//argv 是可执行文件的参数列表,列表的第一个参数约定写成可执行文件的
//文件名。但测试发现随意写好像并没有什么影响,如下所示
execl("/bin/ls", "qwe", "-l", (char *)NULL);
}

waitpid(pid, &status, 0);
printf("status=%d\n", status);
}

void testSystem() {
//system() 函数用于执行 shell command
//system 会调用fork、exec、waitpid 函数,且进行了必要的错误处理和信号处理

int status = 0;

if((status = system("date")) < 0) {
exit(1);
}
printf("status = %d\n", status);

if((status = system("nosuchcommand")) < 0) {
exit(1);
}
printf("status = %d\n", status);

if((status = system("who; exit 44")) < 0) {
exit(1);
}
printf("status = %d\n", status);

}


void testUserloginname() {
char *name = NULL;
char *envname = NULL;
struct passwd *ppasswd;

if(-1 != setenv("LOGNAME", "yym", 1)) {
printf("set the logname to yym\n");
}

//getlogin 得到用户的登录名。且一个 user ID 可能对应多个登录名,
//因为他们使用不同的 shell 等原因。
//环境变量中的 LOGNAME 由 login(1) 从登录名获取,
//但是用户可以更改,所以不可取。
if(NULL != (name = getlogin())) {
printf("login name = %s\n", name);
envname = getenv("LOGNAME");
if(NULL != envname) {
printf("envname = %s\n", envname);
}
//从文件 passwd 中获取用户的信息
ppasswd = getpwnam(name);
if(NULL != ppasswd) {
printf("name=%s\n", ppasswd->pw_name);
printf("passwd=%s\n", ppasswd->pw_passwd);
printf("user information=%s\n", ppasswd->pw_gecos);
printf("dir=%s\n", ppasswd->pw_dir);
printf("shell=%s\n", ppasswd->pw_shell);
}

}
else {
printf("execute getlogin failed\n");
}

//测试读取其他用户的信息
//可以获取到,但是由于密码并不保存在 passwd 文件中
//所以好像没有安全隐患
ppasswd = getpwnam("boy");
if(NULL != ppasswd) {
printf("name=%s\n", ppasswd->pw_name);
printf("passwd=%s\n", ppasswd->pw_passwd);
printf("user information=%s\n", ppasswd->pw_gecos);
printf("dir=%s\n", ppasswd->pw_dir);
printf("shell=%s\n", ppasswd->pw_shell);
}

}


void checkTime(char *processName,
struct timeval *endtv,
unsigned long long int *count)
{

struct timeval tv;

//获取时间。结构体 timeval 中有两个成员变量 tv_sec、tv_usec
gettimeofday(&tv, NULL);
if(tv.tv_sec >= endtv->tv_sec && tv.tv_usec >= endtv->tv_usec) {
printf("%10s count %lld\n", processName, *count);
exit(0);
}
}

void testNice() {
pid_t pid = -1;
char *processname;
struct timeval endtv;
unsigned long long int count = 0;
int priority = 0;
int niceval = 0;

setbuf(stdout, NULL);
gettimeofday(&endtv, NULL);
endtv.tv_sec += 10; //让进程运行 10s

if((pid = fork()) < 0) {
exit(1);
}
else if(0 == pid) {
processname = "child";
//获取进程的调度优先级
priority = getpriority(PRIO_PROCESS, 0);
printf("child process priority = %d\n", priority);
//进程默认的 nice 值
niceval = sysconf(_SC_NZERO);
printf("process default nice value = %d,"
"child nice value = %d\n",
niceval, nice(0));

//nice 值的范围 -20~19,nice 参数为增加的值。且返回值为新的 nice 值。
//由于有可能为 -1,所以需要先将 errno 清零,然后当 nice 返回 -1 且
//errno 不为 0 时才表示调整失败。
errno = 0;
if(-1 == nice(20) && 0 != errno) {
printf("child faild to change the nice value\n");
}
else {
printf("child new nice val is %d\n", nice(0));
}
}
else {
processname = "parent";

priority = getpriority(PRIO_PROCESS, 0);
printf("parent process priority = %d\n", priority);
niceval = sysconf(_SC_NZERO);
printf("parent process default nice value = %d,", nice(0));
}

while(1) {
++count;
if(0 == count) {
printf("%10.10s count warp\n", processname);
}
checkTime(processname, &endtv, &count);
}

}


void showProcessTime(clock_t realtime,
struct tms *tms_start,
struct tms *tms_end)
{

static long clktck = 0;

if(0 == clktck) {
clktck = sysconf(_SC_CLK_TCK);
if(clktck <= 0) {
printf("get the clock tick failed\n");
exit(1);
}
else {
printf("the clock tick is %d\n", clktck);
}
}

printf(" real: %7.2f\n", realtime / (double)clktck);
printf(" user: %7.2f\n", (tms_end->tms_utime - tms_start->tms_utime) / (double)clktck);
printf(" sys: %7.2f\n", (tms_end->tms_stime - tms_start->tms_stime) / (double)clktck);
printf(" cuser: %7.2f\n", (tms_end->tms_cutime - tms_start->tms_cutime) / (double)clktck);
printf(" csys: %7.2f\n", (tms_end->tms_cstime - tms_start->tms_cstime) / (double)clktck);
printf(" csysstart: %7.2f\n", (tms_start->tms_cstime) / (double)clktck);
printf(" csysend: %7.2f\n", (tms_end->tms_cstime) / (double)clktck);
}

void testCmdTime() {

clock_t walltimestart;
clock_t walltimeend;
struct tms tms_start;
struct tms tms_end;

if(-1 == (walltimestart = time(&tms_start))) {
exit(1);
}
printf(" wallstart: %7.2f\n", (walltimestart) / (double)100);
if(system("dd if=/etc/passwd of=/dev/null count=10000") < 0) {
exit(1);
}
if(-1 == (walltimeend = time(&tms_end))) {
exit(1);
}
printf(" wallend: %7.2f\n", (walltimeend) / (double)100);

showProcessTime(walltimeend - walltimestart, &tms_start, &tms_end);
}


void testZombie() {
pid_t pid = -1;

if((pid = fork()) < 0) {
exit(1);
}
else if(0 == pid) {
exit(0);
}

sleep(4);
system("ps -o pid,ppid,state,tty,command");
}

//解释器文件--将参数重新解释成了新的 shell 语句


void printfRevSignal(int signo) {
if(SIGRTMIN == signo) {
printf("Received SIGUSER1\n");
}
else if(SIGRTMIN+1 == signo) {
printf("Received SIGUSER2\n");
}
else {
printf("Received signal %d\n", signo);
}
}

void testSignal() {
void (*pSig)(int);

//signal 函数返回要设置的信号原来的信号处理函数
if(SIG_ERR == (pSig = signal(SIGRTMIN, printfRevSignal))) {
exit(1);
}
else {
printf("previous signal pointer1 = %p\n", pSig);
}
if(SIG_ERR == (pSig = signal(SIGRTMIN+1, printfRevSignal))) {
exit(1);
}
else {
printf("previous signal pointer = %p\n", pSig);
}

//Linux 上也定义了信号 SIGCLD 信号;其有可能在其他系统上导致进行反复调用
//信号处理函数而导致堆栈溢出,进程终止。
//if(SIG_ERR == (pSig = signal(SIGCLD, printfRevSignal)));
//可以利用 sigaction 函数设置 SA_NOCLDWAIT 来避免产生僵死进程

while(1) {
pause();
}
}

//不可靠信号:(最主要的特点:信号可能在任何时候产生)
//1. 信号可能会丢失;信号产生后进程却无法得到通知
//2. 不具备阻塞信号的能力;进程只能捕捉或者忽略
//3. 信号处理只生效一次,然后恢复系统默认
//4. 系统执行慢速的系统调用,若接收到中断信号,则终止系统调用,并返回出错。
// UNIX 系统后来支持自动重启系统调用:ioctl,read,readv,write,writev,wait
// 否则用户就必须每次进行出错处理,如果是中断则重启系统调用
//5. 信号处理函数中调用的函数要是可重入的,即异步信号安全的
// 不可重入的函数:
// a) 使用静态数据结构;
// b) 调用 malloc,free ;会使用静态变量(链表)来维护内存信息
// c) 调用标准 I/O 函数。标准 I/O 函数大都使用了全局数据结构
//6. 每个线程只有一个 errno ,而中断处理函数可能更改 errno ;
// 所以应当在中断处理函数前保存 errno,处理完成后返回 errno。
//7. 更新全局数据结构时,阻塞可能导致异常的信号
//8. alarm 设置一个定时器,在定时时间到后产生一个 SIGALRM 信号。
// 每个进程只能有一个闹钟时间。新设置的闹钟时间会覆盖原来的。
// 在 SIGALRM 的信号处理函数要检查之前的时间,且要保存返回之前的信号处理函数
//9. 设置 SIGALRM 信号处理函数要在设置定时器之前,否则有可能定时器时间到,然后
// 就会执行默认处理,终止进程。
//10. 设置定时器后接系统调用,可能存在竞争条件(假如想要利用该定时器来结束之后
// 的系统调用)。当系统的负载很重时,可能定时间到后,处理完中断处理函数后,
// 仍然没有运行之后的系统调用。 race condition
// 使用 setjmp,longjmp 来解决时,如果 SIGALRM 中断了其他的中断处理函数,那么
// longjmp 会提早结束其他的中断处理函数,而导致异常。
//11. 在信号产生(generation)和递送(delivery)之间的时间间隔内,称信号是未决的(pending);
//当进程对信号采取某种动作时,我们称向进程递送了一个信号。
//调用 sigprocmask 阻塞某一个信号,之后产生该信号,那么该信号是阻塞不能传递的,
//因而也一定是未决的,可以通过函数 sigpending 来获取所有未决的信号。
//12. 使用 sigaction 来检查、修改指定信号的处理动作。取代 signal 函数。
// 更改一个信号的处理动作后会一直生效,直到下一次显示调用该函数更改。
// 一个信号引发了其处理动作,然后该信号会被阻塞直到处理完成返回。除非设置了
// saflag 的 SA_NODEFER 标志
// sa_handler 和 sa_sigaction 可能使用同一存储区,用户只能使用其中一个
// 默认不重新启动被中断的系统调用,除非设置了 SA_RESTART 标志
//13. 调用 sigprocmask 恢复之前的进程屏蔽信号集,如果有任何未决的、不再阻塞的信号,
// 则在 sigprocmask 返回前,至少将其中之一递送给进程。
// 在线程中使用函数 pthread_sigmask 来设置。
// 每个线程都有一个信号屏蔽字(signal mask),规定了当前要阻塞送到进程的信号集。
// 内核在递送一个原来被阻塞的信号给进程时,而不是在产生信号时,才决定信号的处理方式,
// 于是,进程在信号递送给他之前仍可以改变对该信号的动作。
//14. 原子操作:先恢复信号屏蔽字,然后使进程休眠 -- sigsuspend;
// 函数返回时信号屏蔽字设置成调用之前的值
// 避免信号在恢复屏蔽字和使进程休眠之间丢失。
// a) 保护代码临界区,使其不被特定的信号中断
// b) 等待一个信号处理程序设置一个全局变量



void testPipe() {
pid_t pid;
int fd[2] = {0};
size_t n = 0;
char line[4096];
char *pline = NULL;
FILE *fp;


setbuf(stdin, NULL);
if(NULL == (fp = fopen("/etc/passwd", "r"))) {
printf("%s:%d\n", __func__, __LINE__);
exit(1);
}

if(pipe(fd) < 0) {
printf("%s:%d\n", __func__, __LINE__);
exit(1);
}

if((pid = fork()) < 0) {
printf("%s:%d\n", __func__, __LINE__);
exit(1);
}
else if(pid > 0) {
close(fd[0]);

while(fgets(line, 4094, fp) != NULL) {
pline = line;
n = strlen(pline);
printf("n=%d\n", n);
if(write(fd[1], line, n) != n) {
printf("%s:%d\n", __func__, __LINE__);
exit(1);
}
}

if(ferror(fp)) {
printf("%s:%d\n", __func__, __LINE__);
exit(1);
}
close(fd[1]);

if(waitpid(pid, NULL, 0) < 0) {
printf("%s:%d\n", __func__, __LINE__);
exit(1);
}
exit(0);
}
else {

close(fd[1]);


if(fd[0] != STDIN_FILENO) {
//let STDIN_FILENO be the read side of the pipe
if(dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) {
printf("%s:%d\n", __func__, __LINE__);
exit(1);
}
close(fd[0]);
}

if(execl("/bin/more", "more", (char *)NULL) < 0) {
printf("%s:%d\n", __func__, __LINE__);
exit(1);
}

exit(0);
}
}


//popen()
//pclose()

//mkfifo()
//mkfifoat()
//创建 fifo 之后要使用 open 函数打开,且只读打开会阻塞到某个进程为写而打开该 fifo ,
//反之亦然。一个给定的 fifo 有多个写进程是很常见的,因此应该确保每次写都是原子操作,
//当写的数据大小不大于 PIPE_BUF 时可以确保是原子操作。

//msgget()
//msgctl()
//msgsnd()
//msgrev()
//mq_open

void test_mq() {
mqd_t mqID;
struct mq_attr attr;
int rc = 0;

mqID = mq_open("/msg_default", O_CREAT | O_EXCL,
S_IRUSR | S_IWUSR, NULL);
if(-1 == mqID) {
printf("%d %d %d %d %d %d\n", EACCES, EEXIST, EINVAL,
EMFILE, ENAMETOOLONG, ENFILE);
printf("create msg queue failed. errno = %d\n", errno);
if(EINVAL == errno) {
printf("wrong\n");
}
exit(1);
}

rc = mq_getattr(mqID, &attr);
if(-1 == rc) {
printf("get attr failed\n");
exit(1);
}

printf("Maxmum message on queue: %ld\n", attr.mq_maxmsg);
printf("Maxmum message size: %ld\n", attr.mq_msgsize);

rc = mq_unlink("/msg_default");
if(-1 == rc) {
exit(1);
}

exit(0);
}

//sem_init 匿名信号量
//sem_destory 销毁匿名信号量
//sem_open 命名信号量
//sem_close 关闭命令信号量
//sem_unlink 销毁命名信号量

//shm_open
//mmap
//shm_unlink


void cleanBuf(void)
{

printf("pid:%d,clean all buf. Invoke by atexit\n", getpid());
}
void releaseResource(void)
{

printf("pid:%d,release all resource. Invoke by atexit\n", getpid());
}

5.2 IPC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
#include <stdio.h>

#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <mqueue.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <pthread.h>


#define FIFOPATH "/home/kyle/APUE/test/fifo"

void fifotest();
void opentest();
void writetest();
void mqtest();
void shmtest();
void pthreadtest();
void unlockInAtexit();
void mmaptest();
void fcntltest();



struct {
pthread_mutex_t mutex;
pthread_cond_t cond;
int num;
} var = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER};

int main()
{

//writetest();
//fifotest();
//opentest();
//mqtest();
//shmtest();

//pthreadtest();
//unlockInAtexit();
//mmaptest();

fcntltest();
//sleep(10);
return 0;
}

//打开只读的 fifo 时阻塞等待到某个进程为写而打开这个 fifo ;
//只写打开 fifo 会阻塞等待到某个进程为读而打开这个 fifo ;
void fifotest()
{

pid_t pid;
int rc = 0;

umask(0);
//unlink(FIFOPATH);
//rc = mkfifo(FIFOPATH, 0777);
unlink(FIFOPATH);
//rc = mkfifo(FIFOPATH, O_CREAT | O_EXCL | O_RDWR);//写法错误
if(rc < 0) {
perror("mkfifo");
exit(1);
}

pid = fork();
if(pid < 0) {
perror("fork");
exit(1);
}
else if(0 == pid) {
pid = fork();
if(pid < 0) {
perror("second fork");
exit(1);
}
else if(pid > 0) {
int fd = -1;
fd = open(FIFOPATH, O_RDWR);
//fd = open(FIFOPATH, O_WRONLY | O_APPEND);
//fd = open(FIFOPATH, O_WRONLY);
if(fd < 0) {
perror("open fifo");
exit(1);
}
int num = 0;
num = write(fd, "Hello ", sizeof "Hello ");
if(num != sizeof "Hello " ) {
perror("write fifo");
exit(1);
}
num = write(fd, "I'm kyle", sizeof "I'm kyle");
exit(0);

}
else {
int fd = -1;
fd = open(FIFOPATH, O_RDWR);
//fd = open(FIFOPATH, O_WRONLY | O_APPEND);
//fd = open(FIFOPATH, O_WRONLY);
if(fd < 0) {
perror("open fifo");
exit(1);
}
int num = 0;
num = write(fd, "World", sizeof "World");
if(num != sizeof "World") {
perror("write fifo");
exit(1);
}
exit(0);
}
exit(0);
}
else {
//pid = waitpid(pid, NULL, 0);
//if(pid != wait(NULL)) {
// perror("wait error");
// exit(1);
//}
if(-1 == pid) {
perror("firest parent");
exit(1);
}
int fd = -1;
fd = open(FIFOPATH, O_RDWR);
//fd = open(FIFOPATH, O_RDONLY);
if(fd < 0) {
perror("child open fifo");
exit(1);
}
char s[4096] = {'\0'};
int num = 0;
int len = 0;
while((num = read(fd, s, 4096)) != 0) {
len += num;
printf("read num = %d\n", num);
//puts(s);
write(1, s, num);
if(len == 22)
break;
}

exit(0);
}
}

#define OPENFILE "/home/kyle/APUE/test/opentest.c"
void opentest()
{

int rc = 0;
umask(0);
rc = open(OPENFILE, O_RDWR | O_CREAT, 07777);
rc = open(OPENFILE, O_RDWR | O_CREAT);
if(rc < 0) {
perror("2 arg");
}

}

void writetest()
{

int a = 69;//对应 'E' 的 ASCII 值,结果会在终端上打印出 E ;如果改成 999 那么会打印出一个汉字
write(STDOUT_FILENO, &a, sizeof(int));
}

//MQ_OPEN_MAX 一个进程能够同时打开的消息队列的最大数目
//MQ_PRIO_MAX 消息的最大优先级 +1
void mqtest()
{

pid_t pid;

pid = fork();
if(pid < 0) {
perror("fork");
exit(1);
}
else if(pid > 0) {
sleep(5);
char buf[8192] = {'\0'};
ssize_t recvlen = 0;

mqd_t mqd = mq_open("/kylemq", O_RDONLY);
if(-1 == mqd) {
perror("parent open mq");
exit(1);
}

struct mq_attr mqattr;
if(-1 == mq_getattr(mqd, &mqattr)) {
perror("get mq attr");
}
printf("maxmsg = %ld, mq len = %ld, current msg = %ld\n",
mqattr.mq_maxmsg,
mqattr.mq_msgsize,
mqattr.mq_curmsgs);
unsigned int rcvpri = 0;
//接收消息的 buf 必须大于消息队列的大小,否则无法接收消息
//Posix 消息队列读取时总是返回最高优先级的最早消息,且消息的优先级一起返回
//每次只接收一条消息,消息有边界
//读消息队列,只在消息队列为空的时候,阻塞等待;写消息队列,在队列满的时候,阻塞等待;
//写不用等待读,读也不用等待写;可以让一个进程创建消息队列然后终止、第二个进程写消息队列然后终止、第三个进程读消息队列;
//即进程结束后消息队列仍然存在
//mq_notify 可以异步通知进程,有消息放入了空队列
recvlen = mq_receive(mqd, buf, 8192, &rcvpri);
if(-1 == recvlen) {
perror("parent receive mq");
exit(1);
}
printf("recvpriv=%d\n", rcvpri);
if(recvlen != write(STDOUT_FILENO, buf, recvlen)) {
perror("write to stdin");
exit(1);
}

if(-1 == mq_getattr(mqd, &mqattr)) {
perror("get mq attr");
}
printf("maxmsg = %ld, mq len = %ld, current msg = %ld\n",
mqattr.mq_maxmsg,
mqattr.mq_msgsize,
mqattr.mq_curmsgs);

recvlen = mq_receive(mqd, buf, 8192, &rcvpri);
if(-1 == recvlen) {
perror("parent receive mq");
exit(1);
}
printf("recvpriv=%d\n", rcvpri);

if(recvlen != write(STDOUT_FILENO, buf, recvlen)) {
perror("write to stdin");
exit(1);
}
mq_close(mqd);

if(-1 == mq_unlink("/kylemq")) {
perror("unlink after use");
}
exit(0);
}
else {
pid = fork();
if(pid < 0) {
perror("child fork");
exit(1);
}
else if(pid > 0) {
sleep(2);

char buf[100] = {'\0'};

mqd_t mqd = mq_open("/kylemq", O_WRONLY);
if(-1 == mqd) {
perror("child creat mq");
exit(1);
}

int i = 0;
for(i = 0; i < 100; ++i) {
buf[i] = 'A';
}

if(-1 == mq_send(mqd, buf, 100, 30)) {
perror("child send buf");
exit(1);
}

mq_close(mqd);
//if(-1 == mq_unlink("/kylemq")) {
// perror("unlink after use");
//}

exit(0);
}
else {
char buf[100] = {'\0'};

if(-1 == mq_unlink("/kylemq")) {
perror("unlink before creat");
}
mqd_t mqd = mq_open("/kylemq", O_WRONLY | O_CREAT | O_EXCL, 0777, NULL);
if(-1 == mqd) {
perror("child creat mq");
exit(1);
}

int i = 0;
for(i = 0; i < 100; ++i) {
buf[i] = 'C';
}

if(-1 == mq_send(mqd, buf, 100, 10)) {
perror("child send buf");
exit(1);
}

mq_close(mqd);

exit(0);
}
}
}

struct shm_mutx {
sem_t mutex;
int count;
};
void shmtest()
{

//shm_open 的权限位总是必须指定
//shm_open 新创建的共享内存区对象的大小为零,必须使用 ftruncate 修改大小
int shmd = shm_open("/kyleshm", O_RDWR | O_CREAT, 0777);
if(-1 == shmd) {
perror("creat shm");
exit(1);
}

//普通文件扩展,扩展部分填充为零;
//共享内存扩展部分不一定为零。
if(-1 == ftruncate(shmd, sizeof(int))) {
perror("change shm size");
exit(1);
}

struct stat shmstat;
if(-1 == fstat(shmd, &shmstat)) {
perror("get shm stat");
}
printf("shm size = %ld\n", shmstat.st_size);

int *pshm = NULL;
pshm = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, shmd, 0);
if(MAP_FAILED == pshm) {
perror("mmap shm");
exit(1);
}

sem_t *mutex;
if(SEM_FAILED == (mutex = sem_open("/kylemutex", O_CREAT | O_EXCL, 0777, 1))) {
perror("creat mutex");
exit(1);
}
sem_unlink("kylemutex");

pid_t pid = fork();
if(pid < 0) {
perror("fork");
exit(1);
}
else if(0 == pid) {
*pshm = 6543210;
sem_wait(mutex);
printf("child read shm value = %d\n", *pshm);
*pshm = 6543210;
sem_post(mutex);
exit(0);
}
else {
//sleep(1);

sem_wait(mutex);
printf("parent read shm value = %d\n", *pshm);
*pshm = 123456;
sem_post(mutex);

shm_unlink("kyleshm");

exit(0);
}
}


//mmap 测试
//mmap 映射到内存的大小小于文件的大小,仍然可以使用指针设置未映射到内存的空间但还在文件长度范围内的值
//
void mmaptest()
{

//int fd = open("foo", O_RDWR | O_CREAT | O_TRUNC, 0777);
int fd = shm_open("kyleshm", O_RDWR | O_CREAT, 0777);
if(-1 == fd ) {
perror("open foo");
}

ftruncate(fd, 10);
char *string = "YYYYYYYYYY";
write(fd, string, 10);

char *pshmd = mmap(NULL, 8, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

if(MAP_FAILED == pshmd) {
perror("mmap");
}

int pagesize = sysconf(_SC_PAGESIZE);
printf("pagesize=%d\n", pagesize);
*pshmd = 'b';
*(pshmd + 7) = 'e';
*(pshmd + 8) = 'n';
*(pshmd + 9) = 'e';

//ftruncate(fd, 12288);
*(pshmd + 4095) = 'e';
*(pshmd + 4096) = 'e';
*(pshmd + 9192) = 'e';
*(pshmd + 12288) = 'e';

}


//测试在进程的清理函数中释放锁
struct data {
pthread_mutex_t mutex;
int value;
};

struct data *something;

void cleanprocess()
{

printf("excute the unlock clean\n");
pthread_mutex_unlock(&(something->mutex));
}

void unlockInAtexit()
{

pid_t pid;

int shmd = shm_open("/kyleshm", O_RDWR | O_CREAT, 0777);
if(-1 == shmd) {
perror("creat shm");
}

if(-1 == ftruncate(shmd, sizeof(struct data))) {
perror("change the shm size");
}

struct data *pdata = mmap(NULL, sizeof(struct data), PROT_READ | PROT_WRITE, MAP_SHARED, shmd, 0);
if(MAP_FAILED == pdata) {
perror("create mmap");
}

pthread_mutex_init(&(pdata->mutex), NULL);

something = pdata;
pid = fork();
if(pid < 0) {
perror("fork");
}
else if(0 == pid) {
atexit(cleanprocess);

printf("child process\n");
pthread_mutex_lock(&(pdata->mutex));
printf("value = %d\n", pdata->value);
pdata->value = 123456;

exit(0);
}
else {
sleep(3);
printf("parent process\n");

pthread_mutex_lock(&(pdata->mutex));
printf("value = %d\n", pdata->value);
pdata->value = 98765;
pthread_mutex_unlock(&(pdata->mutex));

exit(0);
}
}


pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int meimei = 0;

void *fun1()
{

pthread_mutex_lock(&mutex);
meimei = 12345;
meimei = 12346;
meimei = 12347;
meimei = 12348;
printf("meimei = %d\n", meimei);
pthread_mutex_unlock(&mutex);
if(1234 != pthread_mutex_unlock(&mutex)) {
perror("unlock mutex");
}
if(1234 != pthread_mutex_unlock(&mutex)) {
perror("unlock mutex");
}
pthread_mutex_unlock(&mutex);
pthread_mutex_unlock(&mutex);

//pthread_exit(0);
return NULL;
}

void *fun2()
{

pthread_mutex_lock(&mutex);
meimei = 9876;
meimei = 9874;
meimei = 9873;
meimei = 9872;
printf("meimei = %d\n", meimei);
pthread_mutex_unlock(&mutex);

//pthread_exit(0);
return NULL;
}

void pthreadtest()
{

int producenum = 5;
pthread_t produceID[producenum];

if(0 != pthread_create(&produceID[0], NULL, fun1, NULL)) {
perror("create pthread");
}

if(0 != pthread_create(&produceID[1], NULL, fun2, NULL)) {
perror("create pthread");
}

pthread_join(produceID[0], NULL);
pthread_join(produceID[1], NULL);

exit(0);
}


//一个文件给定字节,同一个调用进程后来设置的值会覆盖掉之前设置的值。
//没有记录锁的类型是读写锁,只有读锁、写锁、没有锁三种
//而且同一个进程利用 f_GETLK 查看,始终会得到 F_UNLCK 。无论该字节是否已经设置上锁。(前提是没有其他进程对其进行上锁)
//只有被其他进程锁定才会获取到锁的类型
//Posix 记录锁是劝告性上锁(advisory locking);
//书上写,一个进程可以无视一个劝告性锁而写一个读锁定文件,或者读一个写锁定文件;(进程有对该文件相应操作的权限)
//自己测试发现及时一个进程使用劝告性写锁锁住一个文件,另一个进程任然可以写该文件。
//记录锁不应该同标准 I/O 库一起使用,因为该函数库会执行内部缓冲。当某个文件需要使用记录锁时,为避免问题,应对其使用 read 、write 函数。
//如果系统支持强制性锁,一个进程对某些字节上锁,其他进程在调用 read/write 时会阻塞,直到锁被打开;
//但是任然无法保证程序不出现混乱,例如一个进程首先读取了受保护变量的值,然后内核切换进程,
//另一个进程锁住文件,修改变量,释放锁;
//此时前一个进程被内核调度回来,依据刚才读取的值做操作(由于该值已被另一个进程修改,所以是错误的值),导致错误的行为。
//多个进程同时操作某个文件时,必须都得上锁,否则违规就可能发生。
void fcntltest()
{

pid_t pid;
struct flock flock;

int fd = open("foo", O_RDWR);
if(-1 == fd) {
perror("open foo");
exit(1);
}

printf("F_RDLCK=%d\n", F_RDLCK);
printf("F_WRLCK=%d\n", F_WRLCK);
printf("F_UNLCK=%d\n", F_UNLCK);

pid = fork();
if(pid < 0) {
perror("fork");
exit(1);
}
else if(0 == pid) {
flock.l_type = F_WRLCK;
flock.l_whence = SEEK_SET;
flock.l_start = 0;
flock.l_len = 0;
int rc = fcntl(fd, F_GETLK, &flock);
if(-1 == rc) {
perror("child get flock");
}
printf("child flock type : %d; lock pid = %d\n",
flock.l_type, flock.l_pid);

flock.l_type = F_WRLCK;
flock.l_whence = SEEK_SET;
flock.l_start = 0;
flock.l_len = 0;

rc = fcntl(fd, F_SETLK, &flock);
if(-1 == rc) {
perror("child set write flock");
}

flock.l_type = F_RDLCK;
flock.l_whence = SEEK_SET;
flock.l_start = 0;
flock.l_len = 0;

rc = fcntl(fd, F_SETLK, &flock);
if(-1 == rc) {
perror("child set read flock");
}

char str[1024];
lseek(fd, 0, SEEK_SET);
int rdlen = read(fd, str, 1024);
write(STDOUT_FILENO, str, rdlen);
write(STDOUT_FILENO, "\n", 1);

lseek(fd, 0, SEEK_SET);
rc = write(fd, "zxcvbn", 6);
if(6 != rc) {
perror("write file when flock by parent");
}

lseek(fd, 0, SEEK_SET);
rdlen = read(fd, str, 1024);
write(STDOUT_FILENO, str, rdlen);

exit(0);
}
else {
flock.l_type = F_WRLCK;
flock.l_whence = SEEK_SET;
flock.l_start = 0;
flock.l_len = 0;

int rc = fcntl(fd, F_SETLK, &flock);
if(-1 == rc) {
perror("set flock");
}

rc = fcntl(fd, F_GETLK, &flock);
if(-1 == rc) {
perror("get flock 1");
}
printf("flock type : %d; lock pid = %d\n",
flock.l_type, flock.l_pid);


flock.l_type = F_RDLCK;
flock.l_whence = SEEK_SET;
flock.l_start = 0;
flock.l_len = 0;

rc = fcntl(fd, F_SETLK, &flock);
if(-1 == rc) {
perror("set flock");
}

rc = fcntl(fd, F_GETLK, &flock);
if(-1 == rc) {
perror("get flock 2");
}
printf("flock type : %d; lock pid = %d\n",
flock.l_type, flock.l_pid);


flock.l_type = F_WRLCK;
flock.l_whence = SEEK_SET;
flock.l_start = 0;
flock.l_len = 0;

rc = fcntl(fd, F_SETLK, &flock);
if(-1 == rc) {
perror("parent set write flock");
}

sleep(5);
exit(0);
}
}

5.3 pthread

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <stdio.h>

#include <stdlib.h>
#include <pthread.h>
#include <signal.h>


void *thr_fn(void *arg);
void *thr_fn2(void *arg);

int globalnum = 0;
char c = 's';

int main()
{

pthread_t ntid;
pthread_t ntid2;
void *tret;
int rc = 0;
pthread_mutex_t mutexlock;

pthread_mutex_init(&mutexlock, NULL);

if(0 != (rc = pthread_create(&ntid, NULL, thr_fn, &mutexlock))) {
exit(0);
}

if(0 != (rc = pthread_create(&ntid2, NULL, thr_fn2, &mutexlock))) {
exit(0);
}

printf("main thread: pid = %lu, ppid = %lu, tid = %lu (0x%lx)\n",
getpid(), getppid(), pthread_self(), pthread_self());

pthread_mutex_lock(&mutexlock);
globalnum += 10;
c = 'k';
pthread_mutex_unlock(&mutexlock);
printf("num = %d, c = %c\n", globalnum, c);

pthread_join(ntid, &tret);
pthread_join(ntid2, &tret);

pthread_mutex_destroy(&mutexlock);
exit(0);
}



void *thr_fn(void *arg)
{

printf("new thread: pid = %lu, ppid = %lu, tid = %lu (0x%lx)\n",
getpid(), getppid(), pthread_self(), pthread_self());

pthread_mutex_lock(arg);
globalnum += 100;
c = 'y';
pthread_mutex_unlock(arg);

printf("num = %d, c = %c\n", globalnum, c);

pthread_exit(NULL);
}

void *thr_fn2(void *arg)
{

printf("new thread: pid = %lu, ppid = %lu, tid = %lu (0x%lx)\n",
getpid(), getppid(), pthread_self(), pthread_self());

pthread_mutex_lock(arg);
globalnum += 1000;
c = 'l';
pthread_mutex_unlock(arg);

printf("num = %d, c = %c\n", globalnum, c);

pthread_exit(NULL);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#include <stdio.h>

#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

#define PIPETOAYC

static volatile sig_atomic_t sigflag;
static sigset_t newmask, oldmask, zeromask;

static int child2parentfd[2];
static int parent2childfd[2];

static void charatatime(char *str);
static void sig_usr(int signo);
void TELL_WAIT();
void WAIT_CHILD();
void TELL_CHILD(pid_t pid);
void WAIT_PARENT();
void TELL_PARENT(pid_t pid);


int main()
{

pid_t pid;

TELL_WAIT();

if((pid = fork()) < 0) {
exit(1);
}
else if(pid > 0) {
WAIT_CHILD();
charatatime("Parent do whatever is necessary ...\n");
TELL_CHILD(pid);
//charatatime("GO on to do something in parent\n");
exit(0);
}
else {
charatatime("Child do whatever is necessary ...\n");
TELL_PARENT(getppid());
WAIT_PARENT();
//charatatime("GO on to do something in parent\n");
exit(0);
}
}

#ifdef SIGTOSYC
static void sig_usr(int signo)
{

sigflag = 1;
}

void TELL_WAIT()
{

if(SIG_ERR == signal(SIGUSR1, sig_usr)) {
exit(1);
}

if(SIG_ERR == signal(SIGUSR2, sig_usr)) {
exit(1);
}

sigemptyset(&newmask);
sigemptyset(&zeromask);
sigaddset(&newmask, SIGUSR1);
sigaddset(&newmask, SIGUSR2);

if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {
exit(1);
}
}

void TELL_CHILD(pid_t pid)
{

kill(pid, SIGUSR1);
}

void WAIT_CHILD()
{

while(0 == sigflag) { //可能有其他信号唤醒进程
sigsuspend(&zeromask);
}
sigflag = 0;

if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
exit(1);
}
}

void TELL_PARENT(pid_t pid)
{

kill(pid, SIGUSR2);
}

void WAIT_PARENT()
{

while(0 == sigflag) {
sigsuspend(&zeromask);
}
sigflag = 0;

if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
exit(1);
}
}
#endif

#ifdef PIPETOAYC
void TELL_WAIT()
{

if(pipe(child2parentfd) < 0 || pipe(parent2childfd) < 0) {
exit(0);
}
}

void TELL_PARENT(pid_t pid)
{

if(write(child2parentfd[1], "c", 1) != 1) {
exit(1);
}
}

void WAIT_PARENT()
{

char c;
if(read(parent2childfd[0], &c, 1) != 1) {
exit(1);
}

if(c != 'p') {
exit(1);
}
}

void TELL_CHILD(pid_t pid)
{

if(write(parent2childfd[1], "p", 1) != 1) {
exit(1);
}
}

void WAIT_CHILD()
{

char c;
if(read(child2parentfd[0], &c, 1) != 1) {
exit(1);
}

if(c != 'c') {
exit(1);
}
}
#endif







static void charatatime(char *str)
{

char *ptr = NULL;
int c = 0;

setbuf(stdout, NULL);
for(ptr = str; (c = *ptr++) != 0; ) {
putc(c, stdout);
}
}
0%