进程间通信方法(IPC)
1.概念
-
IPC指的是InterProcess Communication,进程间的通信需要借助操作系统提供的特殊方法,如文件、管道、信号、共享内存、消息队列、套接字、命名管道等等。
-
进程间通信方式对比:
- 管道:使用最简单
- 信号:开销最小
- 共享映射区:无血缘关系
- 本地套接字:最稳定
2.管道
-
管道的特质:
- 本质是一个伪文件
- 由两个文件描述符引用,一个表示读端,一个表示写端
- 规定数据从管道的写端流入,从读端流出
-
管道的原理:管道实现为内核使用环形队列机制,借助内核缓冲区(4k)实现
-
管道的局限性:
- 数据不能进程自己写,自己读
- 管道中的数据不可以反复读取,一旦读走,管道不复存在
- 只能在有公共祖先的进程间使用管道
- 采用半双工通信方式,数据只能在单方向上流动
3.pipe函数
-
原型:
int pipe(int pipefd[2]);
-
作用:创建并打开管道
-
参数:
fd[0]:读端
fd[1]:写端
-
返回值:
成功:0
失败:-1,设置errno
-
管道的读写行为
-
读管道
- 管道中有数据,read返回实际读到的字节数
- 管道中无数据:
- 管道写端被全部关闭,read返回0(好像读到文件结尾)
- 管道写端没有全部被关闭,read阻塞等待(不久的将来可能有数据到来,让出CPU)
-
写管道
- 管道读端全部被关闭,进程会异常终止(也可以捕捉SIGPIPE信号,使进程不终止)
- 管道读端没有全部关闭:
- 管道已满,write阻塞(很少见,因为CPU会将缓冲区自动扩容)
- 管道未满,write将数据写入,并返回实际写入的字数
-
4.FIFO
-
功能:创建命名管道,与终端指令mkfifo效果类似
-
原型:
int mkfifo(const char* pathname, mode_t mode);
-
参数
pathname:管道的名称
mode_t:八进制的权限,比如0664
-
返回值
成功:0
失败:-1,设置errno;
5.存储映射
-
原理:
-
相关函数模型:
void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset);
-
作用:创建共享内存映射
-
参数:
add:指定映射区的首地址,通常为NULL,由系统自动分配
length:共享内存映射区的大小(小于等于文件大小)
prot:共享内存映射区的读写属性,PROT_READ、PROT_WRITE、PROT_EXEC
flags:标注共享内存的共享属性,MAP_SHARED、MAP_PRIVATE
fd:用于创建共享内存映射区的文件描述符
offset:偏移位置,必须为 4k 的整数倍,默认0表示映射文件全部
-
返回值:
成功:映射区的首地址
失败:MAP_FAILED,设置errno
-
释放共享内存映射
int munmap(void* addr, size_t length);
-
参数:
addr:内存映射的基地址
length:内存映射的长度
-
注意事项:
-
用于创建映射区的大小为0,实际指定非0大小的映射区,出现”总线错误“
-
用于创建映射区的大小为0,实际指定0大小的映射区,出现”参数无效错误“
-
用于创建映射区的文件读写属性为只读,映射区为读写,出现”参数无效错误“
-
创建映射区,必须要read权限。当访问权限指定为共享MAP_SHARED,mmap的读写权限应该小于等于文件的open权限
-
文件描述符,在mmap创建映射区完成即可关闭,后续访问文件用地址访问
-
offset必须是4096的整数倍
-
对申请的内存映射区,不能越界访问
-
munmap用于释放的地址,必须是mmap申请返回的地址
-
映射区访问权限为私有MAP_PRIVATE,对内存所做的所有修改,只在内存有效,不会反应到物理磁盘
-
映射区访问权限为私有MAP_PRIVATE,只需要open文件时有读权限,用于创建映射区即可
-
-
mmap函数的保险调用方式
- open时设为可读可写
- mmap设为读写权限,共享模式
fd = open("文件名", O_RDWR|O_CREAT|O_TRUNC, 0664); mmap(NULL, 文件的大小, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
6.进程间通信
-
父子进程用mmp进程通信:
- 父进程创建映射区。open和mmap函数
- 指定MAP_SHARED权限
- fork创建子进程
- 一个进程读,另外一个进程写
-
无血缘关系的进程间mmp通信:
- 两个进程打开同一个文件,创建映射区
- 指定flag为MAP_SHARED
- 一个进程写入,另个一进程读出
-
无血缘关系的进程可以用mmp和fifo进行通信
- mmp可以反复读取数据
- fifo只能读取一次数据