Linux 无名管道 & 有名管道(FIFO)超详细讲解
·
一、前言
管道是 Linux 最经典的进程间通信 IPC 方式,分为两种:
- 无名管道(匿名管道 pipe)
- 有名管道(命名管道 FIFO)
很多初学者分不清:什么时候用无名管道?什么时候用有名管道?区别在哪、怎么用、适用场景是什么?这篇一次性讲透,带原理 + 特点 + 代码实战。
二、无名管道(匿名管道)
1. 什么是无名管道
无名管道是 Linux 内核开辟的一块内存缓冲区,没有实体文件、没有文件名,只存在于内存中。
2. 核心特性
- 只能用于具有亲缘关系的进程通信(父子进程、兄弟进程);
- 半双工通信:单向数据流动,一端读、一端写;
- 没有磁盘文件,随进程消亡自动释放;
- 只能血缘进程共享文件描述符实现通信;
- 占用两个文件描述符:
fd[0]读端、fd[1]写端。
3. 工作原理
- 父进程调用
pipe(fd)创建管道,内核分配缓冲区; fork()创建子进程,子进程复制父进程的文件描述符表;- 父子进程约定:一个关闭读端只写,一个关闭写端只读;
- 数据在内核缓冲区流转,不落地磁盘。
4. 无名管道使用限制
- 不能无亲缘关系进程通信;
- 只能单向通信,要双向得建两个管道;
- 没有文件实体,只能通过继承 fd 通信。
5. 无名管道 C 语言实战代码
c
运行
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int main()
{
int fd[2];
pid_t pid;
char buf[1024] = {0};
const char *msg = "Hello 无名管道";
// 1. 创建无名管道
if(pipe(fd) == -1)
{
perror("pipe error");
exit(EXIT_FAILURE);
}
// 2. 创建子进程
pid = fork();
if(pid < 0)
{
perror("fork error");
exit(EXIT_FAILURE);
}
if(pid > 0) // 父进程:写数据
{
close(fd[0]); // 关闭读端
write(fd[1], msg, strlen(msg));
close(fd[1]);
wait(NULL); // 等待子进程结束
}
else // 子进程:读数据
{
close(fd[1]); // 关闭写端
read(fd[0], buf, sizeof(buf));
printf("子进程收到:%s\n", buf);
close(fd[0]);
}
return 0;
}
编译运行:
bash
运行
gcc pipe.c -o pipe
./pipe
三、有名管道(FIFO 命名管道)
1. 什么是有名管道
有名管道也叫 FIFO,是存在于磁盘上的特殊文件,类型标识为 p。
有文件名、有路径,任何进程都可以通过路径打开通信。
2. 核心特性
- 支持无亲缘关系进程间通信;
- 依然是半双工单向通信;
- 以文件形式存在磁盘,持久保存,不删就一直在;
- 遵循 先进先出 规则;
- 可以跨程序、跨终端、跨无关进程通信。
3. 创建方式
- 命令行手动创建:
bash
运行
mkfifo my_fifo
查看管道:
bash
运行
ls -l
# 开头带 p 就是命名管道
prw-r--r-- 1 zhiwan zhiwan 0 5月 7 10:00 my_fifo
- 代码函数创建:
mkfifo(路径, 权限)
4. 通信原理
- 通过
mkfifo创建管道特殊文件; - 一个进程以只写方式打开,一个进程以只读方式打开;
- 数据在内核缓冲区传输,不实际写入磁盘;
- 进程退出后,管道文件不会自动消失,需手动
unlink或rm删除。
5. 有名管道线程 / 进程通信代码(你刚才运行成功的版本简化版)
c
运行
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <pthread.h>
#define FIFO_PATH "./my_named_pipe"
// 写线程
void *write_func(void *arg)
{
// 创建有名管道
mkfifo(FIFO_PATH, 0666);
int fd = open(FIFO_PATH, O_WRONLY);
char msg[] = "Hello 有名管道";
write(fd, msg, sizeof(msg));
close(fd);
pthread_exit(NULL);
}
// 读线程
void *read_func(void *arg)
{
sleep(1);
int fd = open(FIFO_PATH, O_RDONLY);
char buf[1024] = {0};
read(fd, buf, sizeof(buf));
printf("读取到:%s\n", buf);
close(fd);
unlink(FIFO_PATH); // 删除管道文件
pthread_exit(NULL);
}
int main()
{
pthread_t t1, t2;
pthread_create(&t1, NULL, write_func, NULL);
pthread_create(&t2, NULL, read_func, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
return 0;
}
四、无名管道 VS 有名管道 核心对比
表格
| 对比项 | 无名管道 pipe | 有名管道 FIFO |
|---|---|---|
| 存在形式 | 内存缓冲区,无磁盘文件 | 磁盘特殊文件,有路径文件名 |
| 通信进程 | 仅限亲缘进程(父子) | 任意无亲缘进程 |
| 创建方式 | pipe() |
mkfifo() / 命令 mkfifo |
| 生命周期 | 进程全部退出自动销毁 | 手动删除才消失 |
| 访问方式 | 继承文件描述符 | 文件路径打开 |
| 适用场景 | 父子进程简单通信 | 不同程序、跨终端进程通信 |
| 半双工 | 单向通信 | 单向通信 |
五、关键知识点总结
- 无名管道:无文件、只血缘进程、随进程销毁,简单轻量;
- 有名管道:有实体管道文件、任意进程可通信、生命周期持久;
- 管道都是半双工,单向流动,双向通信要建两个管道;
- 有名管道程序结束后如果不
unlink,文件会一直留在目录里; - 管道数据走内核缓冲区,不会真正落地磁盘,效率高。
六、适用场景选择
- 写父子进程简单通信 → 用 无名管道
- 两个独立程序、不同终端、无关进程要通信 → 用 有名管道 FIFO
更多推荐




所有评论(0)