Linux进程间通信(IPC)
一、信号通信
1. 信号概述
-
应用场景:
-
异步通信
-
通知机制
-
处理随机事件
-
-
特点:轻量级,用于进程间简单通知
2. 信号处理流程
-
信号产生:根据需要(随机事件)产生信号
-
内核处理:Linux内核接收到信号请求,在PCB链表中查找对应的PID
-
信号处理:找到对应进程后,暂停当前工作流程,执行PCB中信号处理函数
- 例:发送信号2,则调用handle2()
-
恢复执行:信号处理函数执行完毕后,进程继续原来的代码执行
3. 信号相关函数
发送信号
int kill(pid_t pid, int sig);
-
功能:给指定进程发送信号
-
参数:
-
pid:接收信号的进程PID -
sig:信号编号(可用kill -l查看)
-
-
返回值:成功返回0,失败返回-1
信号捕获与处理
void (*signal(int signum, void (*handler)(int)))(int);
// 或使用简化类型
sighandler_t signal(int signum, sighandler_t handler);
-
handler参数选项:
-
SIG_DFL:默认处理 -
SIG_IGN:忽略信号 -
自定义函数:用户自定义处理函数
-
查看信号信息
man 7 signal # 查看系统中信号的说明和默认处理行为
二、管道通信
1. 无名管道
int pipe(int pipefd[2]);
-
功能:创建并打开一个无名管道
-
参数:
-
pipefd[0]:固定读端 -
pipefd[1]:固定写端
-
-
特点:
-
只能用于有亲缘关系的进程间通信
-
单向通信
-
生命周期随进程结束
-
2. 有名管道
int mkfifo(const char *pathname, mode_t mode);
-
功能:创建有名管道文件
-
参数:
-
pathname:管道文件路径+名称 -
mode:八进制文件权限
-
-
特点:
-
可用于任意进程间通信
-
以文件形式存在于文件系统
-
需要手动删除
-
三、共享内存
1. 概述
-
提供者:System V(Unix操作系统)
-
特点:
-
最高效的IPC方式
-
进程直接读写内存,无需内核介入
-
需要配合其他同步机制(如信号、信号量集)
-
2. 与管道对比
| 特性 | 共享内存 | 管道 |
|---|---|---|
| 读写方向 | 双方都可读写 | 单向 |
| 读阻塞 | 无 | 有 |
| 写阻塞 | 无 | 有 |
| 数据存储 | 内存数组 | 内核缓冲区 |
| 数据保持 | 不删除数据 | 读取后删除 |
3. 使用步骤
生成key → 申请对象 → 映射对象 → 读写对象 → 撤销映射 → 删除对象
4. 相关函数
生成唯一键值
key_t ftok(const char *pathname, int proj_id);
-
功能:生成唯一临时键值
-
参数:
-
pathname:任意存在的文件路径 -
proj_id:整形数字(通常用ASCII字符)
-
-
注意:路径文件不能被删除重建
申请共享内存
int shmget(key_t key, size_t size, int shmflg);
-
功能:向内核申请共享内存
-
参数:
-
key:唯一键值 -
size:共享内存大小 -
shmflg:访问权限(八进制)+ 标志-
IPC_CREAT:第一个申请时使用 -
IPC_EXCL:检测是否存在
-
-
-
返回值:成功返回共享内存ID(shmid)
映射共享内存
void *shmat(int shmid, const void *shmaddr, int shmflg);
-
功能:将共享内存映射到本地内存空间
-
参数:
-
shmid:共享内存ID -
shmaddr:本地地址(通常为NULL,系统自动分配) -
shmflg:-
0:可读写 -
SHM_RDONLY:只读
-
-
-
返回值:成功返回映射地址
读写操作
memcpy(); // 二进制数据
strcpy(); // 字符串数据
撤销映射
int shmdt(const void *shmaddr);
-
功能:断开本地内存与共享内存的映射
-
参数 :
shmaddr- 映射地址
删除共享内存对象
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
-
功能:修改属性或删除共享内存
-
参数:
-
shmid:共享内存ID -
cmd:IPC_RMID(删除) -
buf:NULL(只删除对象)
-
5. 系统命令
ipcs -a # 查询所有IPC对象(共享内存、信号量集、消息队列)
ipcrm -m ID # 删除指定的共享内存
四、使用注意事项
-
信号:
-
信号处理函数应尽量简短
-
注意信号的可重入性问题
-
某些信号不可捕获(如SIGKILL)
-
-
管道:
-
无名管道需在fork前创建
-
有名管道需要处理读写阻塞
-
注意管道缓冲区大小限制
-
-
共享内存:
-
必须配合同步机制使用
-
注意内存映射的生命周期
-
及时清理,避免内存泄漏
-
注意多进程并发访问的数据一致性问题
-
-
通用建议:
-
错误处理要完善
-
资源使用后及时释放
-
考虑进程异常退出的清理工作
-