进程间通信——有名管道
有名管道
匿名管道 由于没有名字,只能用于亲缘关系的进程间通信。因此提出了有名管道(FIFO),也叫命名管道或FIFO文件。
有名管道(FIFO) 提供了一个路径名字与之关联,以 FIFO 的文件形式存在于文件系统中,其打开方式与打开一个普通文件是一样的,即使与该 FIFO 的创建进程没有亲缘关系,只要能够访问该路径名就能够通过 FIFO 进行通信。
一旦打开了 FIFO 就可以使用和匿名管道或其他文件系统一样的 I/O 操作(read、write、close 等)了,FIFO 也有写入端和读出端且读出的顺序和写入的顺序是一样的,因此称为 先入先出。
有名管道和匿名管道的区别:
1. FIFO 在文件系统中作为一个特殊的文件存在,但是 FIFO 的内容却在内存中。
2. 当使用 FIFO 的进程退出后,FIFO 文件将继续保存在文件系统中以便后续使用。
3. FIFO 有名字,没有亲缘关系的进程可以通过打开有名管道来进行通信。
有名管道的使用
- 通过命令创建有名管道:mkfifo [名字]
- 通过函数创建有名管道:
参数:#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char* pPathName, mode_t Mode);
(1). pPathName:管道路径名字
(2). Mode:管道文件的权限,和 open 一个文件中的 mode 一样,是一个八进制数。
返回值: 成功返回0,失败返回-1并设置错误码。
一旦使用 mkfifo 创建了一个 FIFO,就可以使用常见的 I/O 操作函数(open、close、read、write 等)。
FIFO 遵循先进先出,对管道和 FIFO 的读总是从开始处返回数据,对他们的写则是添加数据到末尾,他们不支持 lseek 等文件定位操作。
两种方式创建有名管道:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
int iRet = 0;
// 判断文件是否存在
iRet = access("fifo_1", F_OK);
if (iRet == -1) {
puts("管道不存在,创建管道\n");
iRet = mkfifo("fifo_1", 0664);
if (iRet == -1) {
perror("mkfifo");
exit(0);
}
}
return 0;
}

一个有名管道的读写示例:
// write.c
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
//写数据
int main()
{
//判断文件是否存在
int ret = access("test", F_OK);
if (ret == -1) {
printf("管道不存在,创建管道\n");
ret = mkfifo("test", 0664);
if (ret == -1) {
perror("mkfifo");
exit(0);
}
}
//以只写的方式打开管道
int fd = open("test", O_WRONLY);
if (fd == -1) {
perror("open");
exit(0);
}
//写数据
for (int i = 0; i < 100; ++i) {
char buf[1024];
sprintf(buf, "hello, %d\n", i);
printf("write data: %s\n", buf);
write(fd, buf, strlen(buf));
sleep(1);
}
close(fd);
return 0;
}
// read.c
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
//读数据
int main()
{
//以只读的方式打开管道
int fd = open("test", O_RDONLY);
if (fd == -1) {
perror("open");
exit(0);
}
//读数据
while (1) {
char buf[1024] = {0};
int len = read(fd, buf, sizeof(buf));
if (len == 0) {
printf("写端断开连接了...\n");
break;
}
printf("recv data: %s\n", buf);
}
close(fd);
return 0;
}
有名管道的注意事项
- 一个为只读打开管道的进程会阻塞,直到有另外一个进程可写的打开管道,只运行 write.c 会阻塞。
- 一个为只写打开管道的进程会阻塞,知道有另外一个进程可读的打开管道,只运行 read.c 会阻塞。
读写管道的特点:
- 读管道:
- 管道中有数据:read 返回实际读到的数据字节数。
- 管道中无数据:
- 没有打开的管道写端:read 返回 0 (相当于读到文件尾)。
- 有打开的管道写端:read 阻塞等待。
- 写管道:
- 没有打开的管道读端:进程异常终止(收到一个 SIGPIPE 信号)。
- 有打开的管道读端:
- 管道已满:write 会阻塞
- 管道没满:write 会写入数据,返回实际写入的数据字节数。

浙公网安备 33010602011771号