文章目录
1.进程间通信的目的
进程之间具有独立性,这是普遍的性质,如果进程要发生通信,则代表需要进行数据的交互,即进程的数据由毫不相关,变成了有可能相关,一些独立的数据也有可能变成了共享的。
数据传输:一个进程需要将它的数据发送给另外一个进程
资源共享:多个进程之间共享同样的资源
通知事件:一个进程需要向另一个或一组进程发送消息,通知它们发生了某种事件(比如进程终止要通知父进程)
假设要对一组数据进行处理,一个进程负责先排序,另外一个进程负责进行数据的分类,当排序的进程完成之后
通知分类的进程进行分类,这就是通知事件
进程控制:有些进程希望完全能够控制另外一个进程的执行(如debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并且能够及时知道它的状态改变
比如有父子进程,父进程输入一个指令控制子进程做出相对的反应,这就是一个进程控制另外一个进程
2.进程间通信发展
管道
System V进程间通信
POSIX 进程间通信
这两种是通信标准,进程的通信必须得严格的按照标准,只有按照标准才能实现不同进程之间的交互顺利的进行
3.进程间通信分类
管道
匿名管道
命名管道
System V IPC
System V消息队列
System V共享内存
System V信号量
POSIX IPC
消息队列
共享内存
信号量
互斥量
条件变量
读写锁
4.管道
管道是实现进程间通信的一种方式,进程之间通信的本质就是让不同的进程看到同一份资源
4.1管道原理
管道是Unix中最古老的进程间通信的方式
我们把从一个进程连接到另一个进程的一个数据流称为一个管道
我们的子进程都是通过父进程产生的,子进程的创建是以父进程为模板,即拷贝父进程的PCB、file_struct、页表等等内容,由于拷贝了file_struct,因此父子进程可以通过文件描述符看到同一份资源(同一个文件),看到同一份资源即可以完成进程之间的数据交互,实现进程之间的通信。
这种通过能看到同一份文件来实现进程通信的方式就叫做管道通信,这个文件就是我们的管道
4.1.1系统层面理解管道
4.1.2为什么每个进程都会默认打开三个文件
上图很好的解释了,为什么我们创建的进程没有打开0,1,2文件描述符对应的文件,系统却默认帮我们打开了,这是因为我们的进程有一个共同的祖先,而这个祖先是默认打开了0,1,2号文件描述符对应的标准输入,标准输出,标准错误文件的。
而在创建文件的时候,子进程会拷贝父进程的files_struct,因此就将对应的映射关系给拷贝过来了(一个文件可以被多个文件打开),因此我们的进程默认是打开0,1,2这三个文件的
4.1.3struct file为什么不需要拷贝
一个新的进程的创建,需要给进程创建PCB、页表、虚拟地址空间等等,因为进程是具有独立性的
而struct file是描述文件的数据结构,即这个数据结构和进程的关系是访问与被访问的关系,并不属于进程,我们的进程只是可以通过文件描述符找到对应的struct file,然后读取struct file中的数据信息,找到进程想要操作的文件。因此struct file并不会被拷贝创建一份
4.1.4文件也有自己的内存区
一个文件也是有自己的内存(缓冲)区域的,并不是直接往硬盘上写入,而是先写到内存之中,再由操作系统刷新至硬盘之中;
因此,一个进程往文件之中写入内容,这个内容会被保存至内存之中,而另外一个进程同时打开了这个文件,因此是可以看到同一份资源的
4.2匿名管道
管道只能够进行单向通信,并且需要进程之间具有亲缘关系,常常用于父子进程之间
4.2.1匿名管道的创建命令
pipe接口,如果调用从成功返回0,调用失败返回-1
接口的参数是输出型参数,对应的内容pipefd[0]对应的是文件描述符3,对文件是读的方式
pipefd[1]对应的文件描述符是4,对文件是写的方式
4.2.2匿名管道的实现过程
4.2.2.1注意陷阱
很多人就会开始想,为什么不直接定义一个全局变量缓冲区,父进程往里面写,子进程读,这不就实现通信了吗?
我们的进程之间是具有独立性的,独立性体面在了数据的独立性。
开始阶段父子进程看到的数据的确是一样的,如果对齐进行了修改,那么就会发生写时拷贝,此时各自进程地址空间对应的数据映射关系就就会发生改变,即一个进程数据的改变不会影响另外一个进程
4.2.2.2图解分析管道创建过程
4.2.2.3代码实现
4.2.3匿名管道代码层面的特性
4.2.3.1如果写端,不关闭文件描述符,且长时间不写入,那么读端可能(管道内有历史数据就会先读取完)长时间阻塞
4.2.3.2当我们在实际写入时,如果写入条件不满足,那么写入端就要被阻塞
4.2.3.3如果写入端关闭文件描述符,读取端就会读取到文件末尾,read返回0值
4.2.3.4如果读取端关闭,写入端进程被OS直接杀掉(OS系统统筹管理资源,发现有浪费进程的事件或者进程就会终止它)
4.2.4匿名管道的特点
1.只能用于具有亲缘关系的进程之间的通信,通常一个管道由一个进程创建,然后该进程调用fork,此后父子进程可以使用该管道了
2.提供流式服务
3.一般而言,进程退出,管道释放,所以管道的生命周期随进程
一个进程打开一个文件,进程退出了,文件也会被关闭
我们的管道也是文件,进程退出,管道生命周期结束,所以说管道的生命周期随进程
4.一般而言,内核会对管道操作进行同步与互斥
同步:比如,写完才读,一慢都慢
互斥:读与写不同步
写端往管道写的慢,即使我们没有对读取端限速,我们读取的时候也会读的慢,说明了OS对管道的任何读写操作进行了优化,即管道中的读写操作是互斥的。
互斥是保护数据的安全,如果没有互斥,这边还没写完,那边已经读取完毕,就造成了数据的不完整
5.管道是半双工的,数据只能从一个方向,往另外一个方向流动,如果需要双方通信,则需要建立起两个管道
4.3命名管道
4.3.1命名管道原理
匿名管道使用是有限制的,即必须拥有血缘关系,当我们的进程之间没有关系的时候,怎么进行数据的交换呢?这时候我们可以使用命名管道来不同进程之间的数据的交互;
实现进程间的通信,首先得让它们看到同一份文件,匿名管道是亲缘关系继承拷贝属性信息,得到相同的文件描述符
命名管道是让两个进程打开同一个文件,文件是用文件路径加文件名做标识的,因此它们通过表示打开同一个文件
命名管道的本质是两个进程打开同一个文件进行操作,完成资源的共享。而我们的文件在内存上是有缓冲区的,这就为我们了的命名管道提供了依据,我们可以使用FIOF文件来做这项工作,它就被称为命名管道
命名管道是一种特殊类型的文件
4.3.2命名管道创建命令
4.3.3代码实现
服务器端(读取端):server
客户端(写入端):
4.3.4命令行指令创建命名管道
mkfifo即是一个调用接口,就是一个命令行指令,和我们的write、read都是一样的
4.3.5命名管道和匿名管道的区别
1.命名管道是mkfifo创建,open打开的,匿名管道是pipe创建并且打开的
2.匿名管道只可在具有亲缘关系的进程之间使用
3.创建和打开的方式不同,在创建、打开完成后他们的语义是相同的
5.system V共享内存
5.1共享内存原理
5.2共享内存数据结构
管道一般支持两个进程进行通信,而共享内存是一块内存区域,只需要通过用户级页表,将不同的进程的虚拟地址空间中的共享区域,和内存共享区域映射连接起来即可完成通信
共享内存在创建的时候一定是OS提供的,进程间通信的本质是让进程看到同一份资源,共享内存中的资源是由OS提供的,管道中的共享资源是文件系统提供的,本质也是OS提供的
即进程间通信的所有资源都是OS提供的,无非就是通过OS的文件部分还是OS的进程管理提供的
共享内存是一种通信的方式,不同进程之间进行通信需要OS来分配内存资源,那么内存怎么分配,内存映射链接的进程,这些都需要进行管理,因此也需要数据结构来描述共享内存
5.3共享内存操作
5.3.1共享内存的创建
5.3.1.1 ftok
ftok用来生成一个key值,用来作为共享内存的唯一标识
5.3.1.2 shmget
用来创建共享内存
5.3.1.3代码实现
5.3.1.4 共享内存资源生命周期随内核
5.3.2共享内存资源查看和删除
5.3.3共享内存大小最好为4096整数倍
5.3.4共享内存删除
5.3.5共享内存挂接
5.3.6实现进程通信
共享内存,底层不提供任何同步与互斥机制,因此使用共享内存使进程完成通信需要其他技术做支持的,在IPC之中就叫做信号量
共享内存是通过地址,让我们看到同样的资源的
5.3.7总结
1.内存共享,只需要发生一次拷贝,速度是进程通信之中最快的
2.内存共享,不提供同步与互斥,需要信号量的辅助
3. 内存共享之中的key是OS之中共享内存的唯一标识,shmid是用户层面的定位标识
4. 共享内存挂接时,返回的是虚拟地址空间的地址,和malloc返回的地址是一样的
6.system V 消息队列
队列先进先出,OS提供了一个消息队列,一个进程将数据封装起来变成数据块放入队列,另外一个进程去队列之中拿去数据,这个队列是OS提供的
7.system V信号量
7.1信号量底层操作识别
7.2信号量作用
信号量主要用于同步与互斥,本质就是一把锁
互斥:
我们要让进程之间实现通信,就得让进程看到同一份资源,然而看到同一份资源就会引入操作资源的麻烦,导致数据不一致的问题
比如,谁先写,谁先读,我写的时候你只读了一半就会发生数据的丢失
这里就需要信号量来提供互斥(锁)的概念,规定任何一方在操作的时候,另外一方不能进行操作
打个比方,小孩子吃饭,如果一人一个碗是不会发生争抢的,如果两个小孩子使用一个碗就难免发生争吵了,这时候就需要家长来提供秩序,规定不能的打架,要和睦相处,家长的管制就是一种信号量
临界资源:
我们将多个进程看到的同一份资源叫做临界资源
临界区:
访问临界资源的代码就叫做临界区
锁:
多个进程访问资源的时候,真正需要加锁的地方是临界区,因为只有临界区才会去访问临界资源
system V 里面的锁就叫做信号量
锁的表现形式是代码之中:加锁/解锁代码
二元信号量:0/1 两态,实现互斥功能
申请锁叫做P操作 释放锁叫做V操作 信号量最常见的就是PV操作
多元信号量:描述临界资源中,资源数目的个数,本质是计数器
8.system v标准
从上述进程通信方式之中我们可以看到,不同的方式的接口调用都是类似的。
描述通信方式的结构体也是类似的,传用的参数也是类似的
这种就有大幅度程度上的相似性,就是一种标准