管道的优缺点
1. 什么是管道(Pipe)
管道是 Linux / Unix 进程间通信(IPC) 的一种方式,它提供了一个单向的数据通道,让一个进程的输出可以直接作为另一个进程的输入。
例如:
ls | grep ".cpp"
ls
进程会把目录内容写到管道里grep
进程会从管道里读数据管道在内核中实现,不会在磁盘上生成临时文件
2. 管道的类型
类型 | 系统调用 | 特点 |
---|---|---|
匿名管道 | pipe() | 只能在有亲缘关系的进程之间通信(父子、兄弟) |
命名管道(FIFO) | mkfifo() | 没有亲缘关系的进程也能通信,文件系统中会有一个特殊文件 |
半双工 | pipe() | 默认是单向的,一端写,一端读 |
全双工 | socketpair() | 双向通信,管道的升级版 |
3. 管道的优点
管道是最常用的 IPC(进程间通信)机制之一,主要优点有:
3.1 简单易用
匿名管道只需一个
pipe(fd)
系统调用就能创建。读写和文件操作一样:
read()
/write()
。在 Shell 里更简单,直接用
|
。
例如:
int fd[2];
pipe(fd);
write(fd[1], "hello", 5);
char buf[10];
read(fd[0], buf, sizeof(buf));
3.2 高效
数据不落地,只在内核缓冲区传递。
不需要像文件那样写入磁盘再读出来,速度很快。
在父子进程间传递数据时,开销比
socket
更低。
3.3 进程同步的副作用
当管道写满时,写端会阻塞,直到另一端读走数据。
当管道为空时,读端会阻塞,直到有数据可读。
因此管道天然可以起到简单的同步机制。
3.4 非常适合流水线数据处理
比如:
cat access.log | grep "ERROR" | sort | uniq -c
cat
把日志传给grep
grep
把匹配结果传给sort
sort
把结果传给uniq
每个命令只处理自己的数据段,利用管道高效协作。
4. 管道的缺点
虽然简单,但管道的限制非常多,主要有:
4.1 只能在同一台主机上使用
管道是基于内核缓冲区的,不能跨主机通信。
如果需要不同设备间通信,就得用 Socket。
4.2 匿名管道必须有亲缘关系
pid_t pid = fork();
pipe(fd);
父进程和子进程可以通过
fd[0]
、fd[1]
读写数据。但如果两个完全无关的进程想通信,匿名管道不行,必须用命名管道 FIFO 或 Socket。
4.3 单向通信
一个
pipe()
只能单向传输:fd[0]
专门读fd[1]
专门写
如果需要双向通信,要么建两根管道,要么用 socketpair。
4.4 数据量有限
管道的缓冲区大小是有限的(Linux 默认 64KB)。
如果写端不停写,而读端处理不及时,就会阻塞。
$ cat /proc/sys/fs/pipe-max-size
1048576
虽然可以调整,但性能会受影响。
4.5 数据无格式,需自己解析
管道只是一段字节流,没有消息边界。
例如:
write(fd[1], "abc", 3); write(fd[1], "xyz", 3);
读端可能一次性收到
"abcxyz"
,也可能分两次读到"abc"
和"xyz"
。如果需要消息边界,通常会选择 消息队列 或 Socket。
5. 管道与其他 IPC 的比较
特性 | 管道 | FIFO | 共享内存 | 消息队列 | Socket |
---|---|---|---|---|---|
速度 | 较快 | 较快 | 最快 | 快 | 最慢 |
是否跨主机 | × | × | × | × | √ |
是否阻塞 | √ | √ | × | 可选 | 可选 |
双向通信 | × | × | √ | √ | √ |
数据结构 | 字节流 | 字节流 | 自定义 | 消息 | 自定义 |
使用复杂度 | 简单 | 中等 | 较复杂 | 中等 | 较复杂 |
如果只在父子进程之间传数据,管道最简单;
如果需要跨主机通信,必须用 Socket。
6. 总结
优点
简单易用,API 成本低
内核缓冲区,传输高效
天然支持阻塞,能起到同步作用
适合命令行流水线式数据处理
缺点
只能在同一台主机使用
匿名管道必须是父子或兄弟进程
单向通信,不支持全双工
数据量有限,写多了会阻塞
字节流无格式,应用层需要自己解析