进程间 5 种通信方式

进程间通信方式

  • 管道
  • 消息队列
  • 共享内存
  • 信号量
  • 套接字

进程为什么需要通信

有时候两个进程需要实现数据传输、资源共享、通知事件等,但是由于进程组织的时候是一个 pcb 结构体,而且是相互独立的,有独立的虚拟地址空间,所以 IPC 给我们提供了接口

进程间通信需要“介质”,也就是两个进程都可以访问到的公共资源

1、管道

比如说一个管道符,就可以理解为是一个匿名管道,也是两个进程通信的介质,前面的输出作为后面的输入

netstat -apu | grep 8080

管道可以理解为一个进程连接到另一个进程的数据流

文件描述符角度:管道相当于内核中的一块缓冲区(内存)构成一个队列;使用一对文件描述符来进行访问这个内存,读文件描述符就是从队列中取出数据,写文件描述符就是往队列中插入数据;

fork 共享管道原理:调用pipe的父进程有一个读端,有一个写端,当调用 fork 函数后子进程也有一个读端,一个写端,然后分别关闭父进程的写端,和子进程的读端,两个进程各有一个读端,一个写端,就可以通信了.

内核角度:内核中的一块缓冲区,多个进程通过访问同一块缓冲区实现数据通信

管道特性和规则

  • 管道中没有数据,则 read 一直阻塞不能读数据,直到有数据来到为止

  • 管道中放满数据,则 write 一直阻塞不能写数据,直到有进程读走数据为止

  • 若管道所有写端对应的文件描述符被关闭,则 read 读取完数据返回 0

  • 若管道所有读端对应的文件描述符被关闭,则 write 写数据会触发异常

管道自带同步与互斥, 保证了操作的原子性

  • 当要写入的数据量不大于管道缓冲区,linux 会保证写入的原子性,否则不保证原子性

匿名管道有一定的局限性,比如只能用于同一亲缘关系的进程间通信,如果是两个毫无关系的进程那就要得使用命名管道

命名管道用 mkfifo 函数创建,用 open 打开,匿名管道是用 pipe 函数创建并打开,当打开完之后,命名和匿名管道就没什么大区别了.

命名管道读写特性和匿名管道是一样的
命名管道的打开特性:

  • 如果当前操作是为了读而打开命名管道,则会阻塞,直到有相应的进程为写而打开该管道
  • 如果当前操作是为了写而打开命名管道,则会阻塞,直到有相应的进程为读打开该管道

命名管道提供面向字节流服务,会造成数据粘连,因为数据之间没有边界,而且适用于任意进程,半双工通信.

半双工通信就是:允许在两个方向传输,但是在同一时间只能允许一个方向的传输,所以它其实是转换方向的单工通信,比如说我们已经快淘汰的对讲机,像我们的手机通信,电话都是全双工通信.

管道通信的效率比较低,如果 A 进程向 B 进程传输数据,只有当B进程读取后数据,A 进程才能返回.

2、消息队列

是内核中维护的一个队列 ,消息队列提供了从一个进程向另外一个进程发送数据的方法,发送消息的进程,只管往队列中添加数据就行了,让接受数据的进程在队列中取出信息

进程间通过对同一个消息队列添加/获取数据块结点实现通信,传输的是有类型的数据块(数据节点)

另外如果一个进程发送的数据占的内存比较大,并且两个进程之间的通信特别频繁的话,消息队列模型就不大适合了,因为发送的数据很大的话,意味 发送消息(拷贝) 这个过程需要花很多时间来读内存

为了减少开销时间,还有一种通信叫共享内存

3、共享内存

这是最快的进程间通信方式,因为两个进程共用相同的内存,不需要进程拷贝数据.

当系统加载一个进程的时候,分配给进程的内存并不是实际物理内存,而是虚拟内存空间,那么我们可以让两个进程各自拿出一块虚拟地址空间来,然后映射到相同的物理内存中,这样,两个进程虽然有着独立的虚拟内存空间,但有一部分却是映射到相同的物理内存,这就完成了内存共享机制了。这种通信相较于其他进程间通信方式,少了两步用户态与内核态之间的数据拷贝过程.

在 Linux中,每个进程都有属于自己的进程控制块和地址空间,并且都有一个与之对应的页表,负责将进程的虚拟地址与物理地址进行映射,通过内存管理单元(MMU)进行管理,两个不同的虚拟地址通过页表映射到物理空间的同一区域,它们所指向的这块区域就是共享的内存

共享内存一共分为五个步骤

  1. 创建共享内存 shmget

  2. 将共享内存映射到虚拟地址空间 shmat

  3. 直接通过虚拟地址进行内存操作 memcpy

  4. 解除映射连接关系 shmdt

  5. 删除共享内存 shmctl

参考文章:共享内存

但是需要注意的是:共享内存并没有实现同步的机制,也就是说当进行写操作的时候,是不能读取的,那要怎么保证这一点呢?就得引出信号量

4、信号量

信号量可以保证多进程竞争内存的安全问题,实现进程间同步与互斥.

信号量的本质就是一个计数器,例如信号量的初始值是 1,然后 a 进程来访问内存1的时候,我们就把信号量的值设为 0,然后进程b 也要来访问内存1的时候,看到信号量的值为 0 就知道已经有进程在访问内存1了,这个时候进程 b 就无法访问内存1

参考文章

5、套接字

套接字可以对不同的主机之间进程通信,通过自定义协议的格式进行通信,也可以利用现有的知名协议进行进程间通信

例如,客户端和服务器之间的通信,我们平时通过浏览器发起一个 http 请求,然后服务器给你返回对应的数据,这种就是采用 Socket 的通信方式了,再比如我们用最多的微信聊天,当我们给女朋友发送一个信息的时候,这个信息首先会发送给微信后台服务器,然后由服务器转发给女朋友的微信客户端.

进程间通信目的
  • 数据传输:一个进程 需要将它的数据发送给另一个进程

  • 资源共享:多个进程之间共享同样的资源

  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它们发生了某种事件(进程终止时要通知父进程)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿的温柔香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值