有名管道
2026/8/25大约 3 分钟
Linux 进程间通信:命名管道 FIFO
1. FIFO 是什么
FIFO(Named Pipe,命名管道)本质上也是管道。
区别在于:
FIFO 在文件系统中拥有一个名字,因此没有亲缘关系的进程也可以通信。
进程A 进程B
│ │
write() read()
│ │
└──────────┐ ┌──────────┘
↓ ↓
┌──────────────┐
│ FIFO │
│ 命名管道 │
└──────────────┘核心 API:
mkfifo() // 创建 FIFO
open() // 打开
read() // 读取
write() // 写入
close() // 关闭2. 创建 FIFO
#define MYFIFO "/tmp/myfifo"
if (access(MYFIFO, F_OK) == -1) {
mkfifo(MYFIFO, 0666);
}access():
access(MYFIFO, F_OK)用于判断:
FIFO 是否已经存在。
如果不存在:
mkfifo(MYFIFO, 0666);创建 FIFO。
创建后:
/tmp/myfifo会出现在文件系统中。
3. 0666 是什么?
mkfifo(MYFIFO, 0666);这里的 0666 是文件权限:
0666
│
├── 6:拥有者 rw-
├── 6:所属组 rw-
└── 6:其他用户 rw-也就是:
读 + 写实际权限还会受到进程 umask 的影响。
4. 读端
int fd = open(MYFIFO, O_RDONLY);
char buf[100] = {0};
read(fd, buf, sizeof(buf));
printf("%s\n", buf);
close(fd);流程:
open(O_RDONLY)
↓
read()
↓
获取数据
↓
close()默认阻塞模式下,如果没有写端打开:
open(MYFIFO, O_RDONLY);可能会阻塞等待。
5. 写端
int fd = open(MYFIFO, O_WRONLY);
char *msg = "ledon";
write(fd, msg, strlen(msg) + 1);
close(fd);流程:
open(O_WRONLY)
↓
write()
↓
数据进入 FIFO
↓
close()如果没有读端打开,open(O_WRONLY) 默认也可能阻塞等待。
6. 最容易忘的:main 的 argc 和 argv
写进程:
int main(int argc, char *argv[])运行:
./fifowr ledon此时:
argc = 2
argv[0] → "./fifowr"
argv[1] → "ledon"所以:
argv[1]就是我们在命令行输入的:
ledon这就是:
write(fd, argv[1], strlen(argv[1]) + 1);能够把 ledon 写入 FIFO 的原因。
7. argc 和 argv 到底是什么关系?
例如:
./fifowr ledon命令行实际上被分成两个字符串:
argv[0] = "./fifowr"
argv[1] = "ledon"因此:
argc = 2argc:
参数个数。
argv:
参数字符串数组。
所以:
argc < 2就是检查:
用户有没有输入要发送的数据。
如果执行:
./fifowr那么:
argc = 1没有:
argv[1]所以程序提示:
Usage: ./fifowr string8. 完整通信过程
执行:
./fiford读进程:
access()
↓
不存在?
↓
mkfifo()
↓
open(O_RDONLY)
↓
等待写端另一个终端执行:
./fifowr ledon写进程:
argc = 2
argv[0] = "./fifowr"
argv[1] = "ledon"
↓
open(O_WRONLY)
↓
write(fd, argv[1], strlen(argv[1]) + 1)
↓
FIFO然后读进程:
FIFO
↓
read()
↓
str_rd = "ledon"
↓
strcmp()
↓
Turn ON LED!整个数据流:
./fifowr ledon
│
│ argv[1]
↓
write()
↓
┌───────────────┐
│ FIFO │
└───────────────┘
↓
read()
↓
str_rd
↓
strcmp("ledon")
↓
开灯9. 为什么写字符串时要 +1?
write(fd,
argv[1],
strlen(argv[1]) + 1);假设:
argv[1] = "ledon"内存实际上是:
l e d o n \0strlen("ledon"):
5所以:
strlen() + 1
↓
6把最后的:
'\0'也一起写进去。
这样读出来以后:
strcmp(str_rd, "ledon");
printf("%s", str_rd);才能把它当作标准 C 字符串使用。
10. read() 返回值
ssize_t nread;
nread = read(fd, str_rd, MAX_DATA_LEN);返回值:
nread > 0
↓
实际读取的字节数
nread == 0
↓
所有写端都关闭
nread < 0
↓
读取发生错误同理:
ssize_t nwrite;
nwrite = write(...);正常情况下返回实际写入的字节数,失败返回 -1。
11. FIFO 和 Pipe 的本质区别
| 无名管道 Pipe | 命名管道 FIFO | |
|---|---|---|
| 创建 | pipe() | mkfifo() |
| 名字 | 无 | 有路径名 |
| 亲缘关系 | 通常需要 | 不需要 |
| 连接方式 | fork() 继承 fd | 通过路径 open() |
| 数据操作 | read/write | read/write |
一句话:
Pipe 靠
fork()后的文件描述符继承建立联系;FIFO 靠文件系统中的路径名建立联系。
12. FIFO 的典型应用
Qt GUI
│
│ "ledon"
↓
FIFO
│
↓
后台服务进程
│
↓
GPIO / LED / 设备驱动FIFO 本身只负责:
传输数据。
例如:
ledon
ledoff
reboot
status这些是应用程序自己定义的命令协议。
13. 一句话记忆
FIFO = 有名字的管道。
核心流程:
mkfifo()
↓
文件系统产生 FIFO
↓
两个独立进程 open()
↓
write() ← FIFO → read()
↓
完成通信最需要记住的四件事:
mkfifo() → 创建
open() → 打开
write() → 写入
read() → 读取以及两个关键点:
① 不要求进程存在亲缘关系
② 默认阻塞模式下,open() 可能等待另一端这就是 FIFO 的核心。

