两个进程之间的管道是指在父进程中创建的一对文件。 此管道会在父进程派生时连接生成的进程。 由于管道并不存在于任何文件名称空间中,因此说管道是匿名的。 虽然可通过单个管道将任意数量的子进程相互连接以及连接到其父进程,但是一个管道通常只连接两个进程。
管道是通过调用 pipe(2) 在成为父进程的进程中创建的。 此调用在传递给它的数组中返回两个文件描述符。 派生之后,两个进程将从 p[0] 中读取,向 p[1] 中写入。 实际上,进程将从为它们管理的循环缓冲区中读取以及向其中写入。
由于调用 fork(2) 会复制每个进程的打开文件表,因此每个进程都有两个读取器以及两个写入器。 关闭额外的读取器和写入器即可使管道正常运行。 例如,如果读取器的另一端保持打开状态以便同一进程进行写入,则永远不会返回文件结束指示。 以下代码说明了管道创建、派生以及清除重复的管道端。
#include <stdio.h> #include <unistd.h> ... int p[2]; ... if (pipe(p) == -1) exit(1); switch( fork() ) { case 0: /* in child */ close( p[0] ); dup2( p[1], 1); close P[1] ); exec( ... ); exit(1); default: /* in parent */ close( p[1] ); dup2( P[0], 0 ); close( p[0] ); break; } ...
下表显示了在特定情况下,从管道中读取以及向其中写入的结果。
表 6–1 管道中的读/写结果
尝试 |
情况 |
结果 |
---|---|---|
读取 |
管道为空,已连接写入器 |
阻塞读取 |
写入 |
管道已满,已连接读取器 |
阻塞写入 |
读取 |
管道为空,未连接写入器 |
返回 EOF |
写入 |
没有读取器 |
SIGPIPE |
可以通过为描述符调用 fcntl(2) 来设置 FNDELAY,从而避免发生阻塞。 这样会导致从 I/O 调用返回错误 (-1),并将 errno 设置为 EWOULDBLOCK。