5. 管道与FIFO
管道与 FIFO 是 UNIX 系统上最古老的 IPC 方法。FIFO 是管道的一个变体,可用于任意进程间的通信。
管道
概述
管道可看作一个单向字节流,管道两端分别为一个读进程和一个写进程。管道有以下特点:
管道是单向的字节流,意味着管道不存在消息边界的概念,不能随机访问数据,只能每次顺序读取任意大小的数据块,读取后管道内便不存在此数据。
管道可以确保写入不超过 PIPE_BUF 字节的操作是原子的。这可以保证多个进程写入时,只要数据大小不超过 PIPE_BUF 则数据不会混合。Linux 上,PIPE_BUF 的值为 4096。
管道的容量是有限的,读数据的进程应该被设计成可以尽可能快地读取数据。
创建和使用管道
#include <unistd.h>
// 创建管道,返回两个打开的文件描述符 filedes[0] filedes[1],分别为读取端和写入端
// 成功返回 0,失败返回 -1
// 因为是文件描述符,可以使用 read write 调用在管道上执行 IO
int pipe(int filedes[2]);管道一般用于父子进程通信,子进程会继承父进程的文件描述符。因为其是单向的,所以父子进程需要明确读写的角色,并把自己不需要的文件描述符关闭。 关闭未使用的管道描述符可以防止耗尽文件描述符的限制。 从管道中读取数据的进程会关闭其持有的管道的写入描述符,这样当其他进程完成输出并关闭其写入描述符之后,读者就能够看到文件结束(在读完管道中的数据之后),否则 read 会一直阻塞进程。 当一个进程试图向一个管道中写入数据但没有任何进程拥有该管道的打开着的读取描述符时,内核会向写入进程发送 一个 SIGPIPE 信号,默认情况下该信号会杀死进程。所以在实际中一般会忽略 SIGPIPE 信号,而通过 write 调用返回的 EPIPE 错误来获取管道状态。
FIFO
FIFO 与管道是类似的,FIFO 在文件系统中拥有一个名称,所以也称为命名管道,可用于非相关进程之间的通信。上面管道介绍的一些特性同样适用与 FIFO。 与管道一样,当所有引用 FIFO 的描述符都被关闭之后,所有未被读取的数据会被丢弃。
其中的 mode 与文件权限一致,可取:
上面的值可以取 OR 运算指定,这些权限会按进程的 umask 值来取掩码。具有合适权限的任何进程都可以打开 FIFO。 一旦创建好 FIFO 后,就可以像一个普通文件一样打开它,然后与管道一样操作它。打开一个 FIFO 时需要为其设置一个读取进程与写入进程。在默认情况下,打开一个 FIFO 读取数据(open 函数 O_RDONLY 标记)将会阻塞直到另一个进程打开 FIFO 以写入数据(open 函数 O_WRONLY 标记)。如果想取消此阻塞,则在 open 调用中添加 O_NOBLOCK 标记。

从一个包含 p 字节的管道或 FIFO 中读取 n 字节的语义如下:

向一个管道或 FIFO 写入 n 字节的语义如下:

下面是一个利用 FIFO 通信的例子,客户端将命令行的数据发送给服务端,服务端将发送来的字符串转为大写后发送回去:
辅助头文件:
服务端:
客户端:
运行结果:
最后更新于
这有帮助吗?