什么是进程间通信?进程间通信有哪几种方式?下面就针对这两个问题进行简单的梳理。
1. 进程间通信 / IPC
- 操作系统为用户提供的几种进程间通信方式。
1.1 为什么操作系统要提供给进程间通信方式给用户?
- 进程间具有独立性(每个进程间只能访问自己的虚拟地址),无法直接沟通,因此需要操作系统提供公共的媒介。
1.2 通信场景
- 数据传输、数据共享、进程控制
1.3 进程间通信方式
- 管道、共享内存、消息队列、信号量
2. 管道:用于进程间的数据传输
- 把从一个进程连接到另一个进程的一个数据流称为一个"管道";
- 本质:内核中的一块缓冲区,多个进程通过访问同一块缓冲区实现通信;
- 分类:匿名管道 / 命名管道。
2.1 匿名管道
- 一个进程通过系统调用在内核中创建了一个管道(缓冲区),并且调用返回管道的操作句柄,但是,内核中的这块缓冲区没有标识符,只能通过子进程复制父进程的方式获取到管道的操作句柄(文件描述符),进而访问同一块缓冲区。
- 特性:只能用于具有亲缘关系的进程间通信。
2.2 命名管道
- 内核中的这块缓冲区有一个标识符,这个标识符是一个可见于文件系统的管道文件。
- 特性:可用于同一主机上的任意进程间通信。
【 匿名与命名区别】
- 匿名:只能用于具有亲缘关系的进程间通信(没有标识符);
- 命名:可用于任意同一主机进程间通信(标识符是一个管道文件)。
【管道特性】
- 半双工通信;
- 管道的生命周期随进程;
- 管道自带同步与互斥;
- 管道提供字节流传输服务;
3. 共享内存:用于进程间的数据共享
- 特性:最快的进程间通信方式。
【实现原理】
- 在物理内存中开辟一块空间----这块空间在内核中是具有标识的;
- 将这块空间通过页表映射到自己的虚拟地址空间中;
- 通过虚拟地址进行内存操作;
- 解除映射关系;
- 删除共享内存。
//1.在物理内存中开辟一块空间
int shmget(key_t key, size_t size, int shmflg);
key:共享内存在内核中的标识,其它进程通过相同的标识打开同一个内存
size:共享内存大小
shmflg:标志位 IPC_CREAT | IPC_EXCL
返回值:成功返回共享内存的操作句柄,失败返回 -1
//2.映射
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmid:操作句柄
shmaddr:映射首地址,通常置空
shmflg:当前映射方式
返回值:错误返回(void*)-1
//3.解除映射关系
int shmdt(const void *shmaddr); //传入映射首地址
//4.删除共享内存
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid:操作句柄
cmd:对齐所要的操作 常用:IPC_RMTID 删除共享内存
struct shmid_ds *buf:获取或buf设置,取决于cmd
4. 消息队列:用于进程间的数据块传输
- 本质:内核中的一个优先级队列,多个进程通过向同一个队列中放置队列节点或获取节点实现通信。
【实现原理】
- 在内核中创建消息队列;
- 向队列中添加节点;
- 从队列中获取节点;
- 删除消息队列。
【特性】
- 消息队列自带同步与互斥;
- 传输有类型的数据块;
- 数据不会粘连;(即两条数据不会粘到一起发送)
- 生命周期随内核。
5. 信号量:用于实现进程间的同步与互斥
- 本质:内核中一个原子操作的计数器,等待队列。
- 互斥:通过同一时间对临界资源只有一个进程能够访问,实现数据的安全操作----数据访问的安全性;
- 同步:通过一些条件判断实现对临界资源的有序访问----数据访问的合理性。
【信号的PV原语】
- p操作:对计数进行判断,然后进行-1,若没有资源则等待;
- v操作:对计数进行+1,唤醒等待队列中的挂起的进程。