由于管道(Pipe)只能应用于存在血缘关系的进程间通信,为了突破这个限制,使用命名管道(FIFO)来实现 不相干进程间 的通信。
FIFO 是 Linux 基础文件类型中的一种,它并不占用磁盘上实际的数据块,而仅仅是标识内核中的一条通道。各进程可以打开这个文件进行 read/write,实际上是在读写内核通道。
#include
#include
int mkfifo(const char *pathname, mode_t mode);
pathname
: 指定创建的文件路径名mode
: 指定文件的访问权限,实际为一个形如 “rwxrwxrwx” 的 int 值,分别表示 “所有者”、“所在组”、“其他组” 的用户访问权限。r
为可读,值为 4;w
为可写,值为 2;x
为可执行,值为 1。通常使用八进制数来表示权限。例如 0644
。成功返回 0,失败返回 -1,并设置相应的 errno
。
注意 :如果文件已存在,也会返回 -1,创建失败的错误原因是:文件已存在。
注意,mkfifo
方法只是创建了一个这样的文件,可供其它进程操作。而其它进程想要使用该文件进行通信,还是需要像文件操作一样,打开、读或写、关闭 文件的。
打开 FIFO 文件通常有四种方式,即:
open(const char *path, O_RDONLY); // 只读的方式打开(会堵塞)
open(const char *path, O_RDONLY | O_NONBLOCK); // 非堵塞的只读方式打开
open(const char *path, O_WRONLY); // 只写的方式打开(会堵塞)
open(const char *path, O_WRONLY | O_NONBLOCK); // 非堵塞的只写方式打开
O_RDONLY
和 O_WRONLY
我们已经很熟悉了,但 O_NONBLOCK
是什么意思呢?
O_NONBLOCK
表示非堵塞的方式,默认情况下,打开 FIFO 是会堵塞进程的。例如我们的 A 进程使用只读的方式打开文件时,此时它会被堵塞住,直到有一个 B 进程以只写的方式打开了同一个 FIFO 文件。同理,先以只写的方式打开,也会堵塞住,直到有一个进程以只读的方法打开同一个 FIFO 文件。
可能会觉得有点晕,下面通过一个实际例子,并给出运行的 gif 图,来理解堵塞的含义。
将一个 Hello World
写到 FIFO 文件中去。
#include
#include
#include
#include
#include
#include
int main() {
char pathname[] = "my_fifo";
char buf[] = "Hello World\n";
mkfifo(pathname, 0644);
int fd = open(pathname, O_WRONLY);
if (fd == -1) {
perror("open failed\n");
exit(EXIT_FAILURE);
}
write(fd, &buf, strlen(buf));
printf("write done\n");
close(fd);
return 0;
}
读取 FIFO 文件中的信息,并输出到标准输出流中。
#include
#include
#include
#include
#include
#include
#include
int main() {
char pathname[] = "my_fifo";
char buf[127] = { 0 };
mkfifo(pathname, 0644);
int fd = open(pathname, O_RDONLY);
if (fd == -1) {
perror("open failed\n");
exit(EXIT_FAILURE);
}
int n;
while ((n = read(fd, buf, 10)) > 0) {
write(STDOUT_FILENO, &buf, n);
}
close(fd);
return 0;
}
先执行读的程序,open 会堵塞住等待读入,再执行写的程序,两个进程都成功执行完毕。
同理反过来,先执行写的程序,也会一直堵塞等待。
这里展示的是先执行 读 的进程,再执行 写 的进程,可以观察到确实会有堵塞的情况。
反过来也是一样的会有先堵塞 写 进程,直到 读 进程执行时结束。