#include <string.h>
char string[] = "Hello, world";
main()
{
int count, i;
int to_par[2], to_chil[2]; // 到父、子进程的管道
char buf[256];
pipe(to_par);
pipe(to_chil);
if (fork() == 0) {
// 子进程在此执行
close(0); // 关闭老的标准输入
dup(to_child[0]); // 将管道的读复制到标准输入
close(1); // 关闭老的标准输出
dup(to_par[1]); // 将管道的写复制到标准输出
close(to_par[1]); // 关闭不必要的管道描述符
close(to_chil[0]);
close(to_par[0]);
close(to_chil[1]);
for (;;) {
if ((count = read(0, buf, sizeof(buf)) == 0)
exit();
write(1, buf, count);
}
}
// 父进程在此执行
close(1); // 重新设置标准输入、输出
dup(to_chil[1]);
close(0);
dup(to_par[0]);
close(to_chil[1]);
close(to_par[0]);
close(to_chil[0]);
close(to_par[1]);
for (i = 0; i 《 15; i++) {
write(1, string, strlen(string));
read(0, buf, sizeof(buf));
}
}
子进程从父进程继承了文件描述符0和1(标准输入和标准输出)。两次执行系统调用 pipe 分别在数组 to_par 和 to_chil 中分配了两个文件描述符。然后该进程执行系统调用 fork,并复制进程上下文:象前一个例子一样,每个进程存取自己的私有数据。父进程关闭他的标准输出文件(文件描述符1),并复制(dup)从管道线 to_chil 返回的写文件描述符。因为在父进程文件描述符表中的第一个空槽是刚刚由关闭腾出来的,所以核心将管道线写文件描述符复制到了文件描述符表中的第一项中,这样,标准输出文件描述符变成了管道线 to_chil 的写文件描述符。父进程以类似的操作将标准输入文件描述符替换为管道线 to_par 的读文件描述符。与此类似,子进程关闭他的标准输入文件(文件描述符0),然后复制 (dup) 管道线 to_chil 的读文件描述符。由于文件描述符表的第一个空项是原先的标准输入项,所以子进程的标准输入变成了管道线 to_chil 的读文件描述符。子进程做一组类似的操作使他的标准输出变成管道线 to_par 的写文件描述符。然后两个进程关闭从 pipe 返回的文件描述符。上述操作的结果是:当父进程向标准输出写东西的时候,他实际上是写向 to_chil--向子进程发送数据,而子进程则从他的标准输入读管道线。当子进程向他的标准输出写的时候,他实际上是写入 to_par--向父进程发送数据,而父进程则从他的标准输入接收来自管道线的数据。两个进程通过两条管道线交换消息。
无论两个进程执行的顺序如何,这个程序执行的结果是不变的。他们可能去执行睡眠和唤醒来等待对方。父进程在15次循环后退出。然后子进程因管道线没有写进程而读到“文件尾”标志,并退出。