IPC(进程间通信)
一、为什么需要IPC?
-
进程空间都是独立的(虚拟地址空间隔离)
-
但进程之间有数据共享 或交换需求
-
需要一种机制让进程间能够通信
二、IPC通信方式分类
1. 古老的通信方式
-
无名管道(匿名管道)
-
有名管道(FIFO)
-
信号(Signal)
注意 :管道是自动申请资源并继续运行程序
2. IPC对象通信(System V IPC)
-
消息队列(用的相对少,不讨论)
-
共享内存
-
信号量集
主要用于:System V、BSD、SUSE、Fedora、kernel.org等系统
3. Socket通信
-
主要用于网络通信
-
也可用于本地进程间通信
三、管道底层实现
-
底层数据结构:队列(先进先出)
-
无名管道 (Unnamed Pipe)=>
pipe- 只能用于有亲缘关系的进程通信(如父子进程)
四、管道特性
1. 工作模式
-
半双工工作模式
-
实际编程中,通常当作单工使用
2. 文件特性
-
所有管道都是特殊文件
-
不支持定位操作
-
lseek→ 用于文件描述符 -
fseek→ 用于FILE*
-
-
建议使用文件IO :
open,read,write,close -
可选使用标准IO (带缓冲区):
fgets,fread,fgetc
3. 编程顺序
创建管道 → fork → 读写管道 → 关闭管道
五、管道行为分析
1. 写端行为
读端存在,一直向管道写:
- 超过64K(默认缓冲区大小)→ 写会阻塞
- 原因:写段写的太快
2. 读端行为
写端存在,从管道读:
- 如果管道为空 → 读会阻塞
- 原因:读段读的太快
3. 管道破裂(Broken Pipe)
读端关闭,继续写管道:
- 导致写段进程收到SIGPIPE信号
- 默认行为:进程退出
4. 管道结束
写端关闭,读管道:
- 如果管道没有内容 → read返回0
- 表示:进程间通信结束
六、有名管道(FIFO)
1. 特点
-
有名管道 =>
fifo -
可以用于任意单机进程通信
-
在文件系统中可见(有文件名称)
2. 编程步骤
创建有名管道 → 打开有名管道 → 读写管道 → 关闭管道 → 卸载有名管道
3. 特性
-
具有无名管道所有特性
-
额外特性:
-
如果有一端没有打开
-
open函数会阻塞直到另一端也打开
-
七、相关函数
1. pipe函数
int pipe(int pipefd[2]);
-
功能:创建并打开一个无名管道
-
参数:
-
pipefd[0]→ 无名管道的固定读端 -
pipefd[1]→ 无名管道的固定写端
-
-
返回值:
-
成功:0
-
失败:-1
-
2. mkfifo函数
int mkfifo(const char *pathname, mode_t mode);
-
功能:在指定路径下创建有名管道文件
-
参数:
-
pathname→ 要创建的有名管道路径+名称 -
mode→ 八进制文件权限(如0666)
-
-
返回值:
-
成功:0
-
失败:-1
-
八、使用场景对比
| 特性 | 无名管道 | 有名管道 |
|---|---|---|
| 关系要求 | 必须有亲缘关系 | 任意进程 |
| 文件可见性 | 不可见 | 文件系统可见 |
| 创建方式 | pipe() |
mkfifo() |
| 打开阻塞 | 不适用 | open会阻塞 |
| 生命周期 | 随进程结束 | 持久存在直到删除 |
九、注意事项
-
管道是半双工 的,但通常按单工使用
-
管道不能定位,只能顺序读写
-
注意处理管道破裂(SIGPIPE信号)
-
读写阻塞是正常现象,需要合理设计程序逻辑
-
及时关闭不需要的文件描述符,避免资源泄漏