一、为什么要进行进程间通信?
主要因为进程间是独立的(每个进程都有自己的虚拟地址空间,操作的都是自己的虚拟地址空间,而不是直接访问物理地址),因此进程间是不能访问同一块区域,不能互相通信的,需要系统提供进程访问方式。
二、如何实现进程间的通信?
这个原理类似于声音传播一样,通过相同的介质,可以互相之间传递声音。因此,只要进程间有共同的访问介质,根据不同的场景选择不同的通信方式,则可以实现进程间的互相通信。
三、具体方式有哪些?
1. 从unix借鉴而来,管道——资源运输
2. systemV标准的进程间通信方式
——共享内存,消息队列,信号量
四、管道
- 管道的本质:管道本质为内核中的一块缓冲区,多个进程通过访问同一个缓冲区,可以访问同一块区域。
- 分类:匿名管道和命名管道
- 匿名管道的定义:内核中的缓冲区没有具体的标识符,故只能用于具有血缘关系的进程间通信。(子进程是通过复制父进程的方式获得操作句柄,父进程再创建管道的时候会返回管道的操作句柄,操作句柄就是文件描述符)
- 匿名管道的创建:
int pipe(int pipefd[2]);
pipefd[2]用于接收创建管道返回的操作句柄;
pipefd[0]—从管道中读取数据;pipefd[1]—给管道中写入数据;
(管道为半双工通信,即只能用于读或者只能用于写)
注意:管道需要在子进程之前创建出来,这样子进程才可以拷贝父进程的操作句柄,实现匿名管道的通信。 - 管道的读写特性:
(1)若管道中没有数据,则调用read读取会阻塞;
(2)若管道中数据满了,调用write会阻塞;
(3)若管道的读端 pipefd[0]被关闭,则继续调用write会产生异常导致进程退出;
(4)若管道的写端 pipefd[1]被关闭,则继续调用read直到读完所有数据进程退出。
小思考题:进程如何将输出结果不写如标准输出而是写入管道呢?
将标准输出重定向到管道的写端pipefd[1]。
6.命名管道:内核中的缓冲区,这块缓冲区具有标识符,可以用于同一主机的任意进程间的通信。多进程间的通信通过命名管道通信是通过打开命名管道文件访问同一块内核中的缓冲区实现的。
命令:int mkfifo(char *filename ,mode_t mode) 创建管道文件
filename:管道文件名称 mode:管道文件权限 - open打开命名管道的特性:
(1)若文件以只读的方式打开,则会阻塞;直到以写的方式打开;
(2)若文件以只写的方式打开,则会阻塞;直到以读的方式打开; - 几点注意事项:
(1)管道的声明周期随进程;
(2)管道自带同步与互斥:
同步:通过条件判断实现临界资源操作的合理性;
互斥:通过唯一访问实现临界资源操作的安全性。
五、共享内存 - 特性:共享内存是进程间最快的访问方式
- 本质:在物理内存上开辟的一块空间,多个进程将这段物理内存映射到自己的虚拟地址上,通过自己的虚拟地址可以访问同一块空间,实现进程间通信。
- 共享内存的操作流程
(1)创建共享内存空间;
int shmget(key_t key,size_t size, int shmflg);
key:标识符;size为大小;shmflg:存在则打开,不存在则创建
(2)建立映射关系;
void shmat(int shmid,const void shmaddr,int shmflg)
分别为:操作句柄,首地址以及操作
(3)对内存空间进行访问;
(4)解除映射关系;
int shmdt(const void* shmaddr);
(5)释放共享内存空间。
shmctl()
注意:声明周期随内核,但其没有同步与互斥。
六、消息队列
1.定义:内核中的一个优先级队列,多个进程通过访问同一个队列,进行添加节点或者获取节点实现通信。
2.操作流程:
(1)创建消息队列;
(2)进程向队列中添加或者获取节点;
(3)删除消息队列。
3.特性:
自带同步与互斥,生命周期随内核。
七、信号量
1.定义:用于实现进程间的同步与互斥。本质为一个内核中的计数器+pcb等待队列+使进程等待或唤醒的接口。
2.类似于停车场技术停车一样
同步实现:大于0访问,小于或者等于0则加入等待队列,保证合理性。
互斥实现:保证计数器不大于1,则保证了唯一性。