在父进程中的 else 子句将会关闭管道的读端:
close(pipeFDs[ReadEnd]); /* called in parent code */
然后父进程将向无名管道中写入某些字节数据(ASCII 代码),子进程读取这些数据,然后向标准输出中回放它们。
在这个程序中还需要澄清的一点是在父进程代码中的 wait 函数。一旦被创建后,子进程很大程度上独立于它的父进程,正如简短的 pipeUN 程序所展示的那样。子进程可以执行任意的代码,而它们可能与父进程完全没有关系。但是,假如当子进程终止时,系统将会通过一个信号来通知父进程。
要是父进程在子进程之前终止又该如何呢?在这种情形下,除非采取了预防措施,子进程将会变成在进程表中的一个僵尸进程。预防措施有两大类型:第一种是让父进程去通知系统,告诉系统它对子进程的终止没有任何兴趣:
signal(SIGCHLD, SIG_IGN); /* in parent: ignore notification */
第二种方法是在子进程终止时,让父进程执行一个 wait 。这样就确保了父进程可以独立于子进程而存在。在 pipeUN 程序中使用了第二种方法,其中父进程的代码使用的是下面的调用:
wait(NULL); /* called in parent */
这个对 wait 的调用意味着一直等待直到任意一个子进程的终止发生,因此在 pipeUN 程序中,只有一个子进程。(其中的 NULL 参数可以被替换为一个保存有子程序退出状态的整数变量的地址。)对于更细粒度的控制,还可以使用更灵活的 waitpid 函数,例如特别指定多个子进程中的某一个。
pipeUN 将会采取另一个预防措施。当父进程结束了等待,父进程将会调用常规的 exit 函数去退出。对应的,子进程将会调用 _exit 变种来退出,这类变种将快速跟踪终止相关的通知。在效果上,子进程会告诉系统立刻去通知父进程它的这个子进程已经终止了。
假如两个进程向相同的无名管道中写入内容,字节数据会交错吗?例如,假如进程 P1 向管道写入内容:
foo bar
同时进程 P2 并发地写入:
baz baz
到相同的管道,最后的结果似乎是管道中的内容将会是任意错乱的,例如像这样:
baz foo baz bar
只要没有写入超过 PIPE_BUF 字节,POSIX 标准就能确保写入不会交错。在 Linux 系统中, PIPE_BUF 的大小是 4096 字节。对于管道我更喜欢只有一个写入方和一个读取方,从而绕过这个问题。
命名管道
无名管道没有备份文件:系统将维持一个内存缓存来将字节数据从写方传给读方。一旦写方和读方终止,这个缓存将会被回收,进而无名管道消失。相反的,命名管道有备份文件和一个不同的 API。
下面让我们通过另一个命令行示例来了解命名管道的要点。下面是具体的步骤:
- 开启两个终端。这两个终端的工作目录应该相同。
-
在其中一个终端中,键入下面的两个命令(命令行提示符仍然是 % ,我的注释以 ## 打头。):
% mkfifo tester ## 创建一个备份文件,名为 tester % cat tester ## 将管道的内容输出到 stdout
在最开始,没有任何东西会出现在终端中,因为到现在为止没有在命名管道中写入任何东西。
-
在第二个终端中输入下面的命令:
% cat > tester ## redirect keyboard input to the pipe hello, world! ## then hit Return key bye, bye ## ditto <Control-C> ## terminate session with a Control-C
无论在这个终端中输入什么,它都会在另一个终端中显示出来。一旦键入 Ctrl+C ,就会回到正常的命令行提示符,因为管道已经被关闭了。
-
通过移除实现命名管道的文件来进行清理:
% unlink tester
正如 mkfifo 程序的名字所暗示的那样,命名管道也被叫做 FIFO,因为第一个进入的字节,就会第一个出,其他的类似。有一个名为 mkfifo 的库函数,用它可以在程序中创建一个命名管道,它将在下一个示例中被用到,该示例由两个进程组成:一个向命名管道写入,而另一个从该管道读取。
示例 2. fifoWriter 程序
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <time.h> #include <stdlib.h> #include <stdio.h> -
#define MaxLoops 12000 /* outer loop */ #define ChunkSize 16 /* how many written at a time */ #define IntsPerChunk 4 /* four 4-byte ints per chunk */ #define MaxZs 250 /* max microseconds to sleep */ -
int main() { const char* pipeName = "./fifoChannel"; mkfifo(pipeName, 0666); /* read/write for user/group/others */ int fd = open(pipeName, O_CREAT | O_WRONLY); /* open as write-only */ if (fd < 0) return -1; /** error **/ int i; for (i = 0; i < MaxLoops; i++) { /* write MaxWrites times */ int j; for (j = 0; j < ChunkSize; j++) { /* each time, write ChunkSize bytes */ int k; int chunk[IntsPerChunk]; for (k = 0; k < IntsPerChunk; k++) chunk[k] = [rand][9](); write(fd, chunk, sizeof(chunk)); } usleep(([rand][9]() % MaxZs) + 1); /* pause a bit for realism */ } -
close(fd); /* close pipe: generates an end-of-file */ unlink(pipeName); /* unlink from the implementing file */ [printf][10]("%i ints sent to the pipe.\n", MaxLoops * ChunkSize * IntsPerChunk); -
return 0; }
上面的 fifoWriter 程序可以被总结为如下:
|