进程间通信的方式

管道

管道是内核里的一串缓存,从管道一端写入的数据,实际上被缓存在了内核中。另一端读取也是从内核中读取这段数据。此外,管道的数据是无格式数据,且大小受限。

Linux命令ps auxf | grep mysql中的竖线“ | ”就是一个管道,其功能是将前一个命令的输出,作为后一个命令的输入。由此看出,管道传输的数据是单向的。若要互相通信,则需要创建两个管道。

竖线“ | ”创建的管道属于匿名管道,匿名管道用完了就会被销毁。

另一种管道模式是命名管道,也称为FIFO,因为这种管道的数据是先进先出的传输方式。
创建命名管道时,需要指定管道的名字:mkfifo myPipe

“hello">myPipe //将数据写进管道
			  //停住了
cat<myPipe //读取管道里的数据
hello

可以看出,将数据写进管道之后会停住,因为管道里的内容没有被读取,当管道里的数据被读完后,命令才可以正常退出
而且,不管是匿名管道还是命名管道,进程写⼊的数据都是缓存在内核中,另⼀个进程读取数据时候⾃然也是从内核中获取,同时通信数据都遵循先进先出原则。
所以管道这种通信方式效率低,不适合进程间频繁地交换数据。
在这里插入图片描述

对于匿名管道,其通信范围是存在父子关系的进程。因为管道没有实体,也就没有管道文件,只能通过fork命令复制父进程的文件描述符fd,来达到通信的目的。
对于命名管道,它可以在不相关的进程间实现通信。因为命名管道提前创建了一个类型为管道的设备文件,在进程里只要使用这个设备文件,就可以相互通信。

消息队列

消息队列是保存在内核中的消息链表。在发送数据时,会分成一个个独立的数据单元,也就是 消息体。消息的发送方和接收方事件约定好消息体的数据类型,每个消息体都是固定大小的存储块,不像管道是无格式的字节流数据。从消息队列中读取了消息体后,内核就会把该消息体删除。

消息队列优于管道的地方在于,A进程给B进程发送消息,把数据放在对应的消息队列之后就可以返回了。B进程需要的时候再去读取数据就可以,是一种异步通信方式

消息队列也有其局限性,一是通信不够及时,二是消息队列的大小也有限制,即每个消息体有一个最大长度的限制。
此外,消息队列通信过程中,还存在用户态与内核态之间的数据拷贝开销。进程写入数据到内核中消息队列时,会从用户态拷贝数据到内核态;同理另一进程读取内核中消息时,会从内核态拷贝数据到用户态

共享内存

共享内存就是拿出一块虚拟地址空间来,映射到相同的物理内存中。这样一个进程写入的东西另一进程可以立马看到,不需要拷贝数据,改善了消息队列内核态与用户态之间消息拷贝过程的开销。

但共享内存同样有其缺点,那就是多个进程同时修改一处物理内存,很有可能造成冲突,先写完的内容很快会被后写完的覆盖。

信号量

为了改善共享内存中多个进程同时修改一处共享内存的问题,可以使用信号量。信号量是一个整形的计数器,用于实现进程间的互斥与同步,而并非直接用于进程间的通信。

P操作会把信号量减去 1,相减后如果信号量 < 0,则表明资源已被占⽤,进程需阻塞等待;相减后如果信号量 >= 0,则表明还有资源可使⽤,进程可正常继续执⾏。
V 操作会把信号量加上 1,相加后如果信号量 <= 0,则表明当前有阻塞中的进程,于是会将该进程唤醒运⾏;相加后如果信号量 > 0,则表明当前没有阻塞中的进程。
P 操作是⽤在进⼊共享资源之前,V 操作是⽤在离开共享资源之后,这两个操作必须成对出现。

信号

上面的进程间通信都是常规状态下的工作模式,对于异常情况下的工作模式,需要用信号的方式来通知进程。
任何时候都可以发送信号来通知进程,一旦有信号产生,有以下几种用户对信号的处理方式:
1.执⾏默认操作。Linux 对每种信号都规定了默认操作,如SIGTERM 信号为终⽌进程。
2.捕捉信号。我们可以为信号定义⼀个信号处理函数。当信号发⽣时,我们就执⾏相应的信号处理函数。
3.忽略信号。当我们不希望处理某些信号的时候,就可以忽略该信号,不做任何处理。但有两个信号是应⽤进程⽆法捕捉和忽略的,即 SIGKILL 和 SEGSTOP ,它们⽤于在任何时候中断或结束某⼀进程

Socket

Socket既可以实现同主机上进程间通信,也可以实现跨网络与不同主机上的进程间通信。
创建socket系统调用:
int socket(int domain, int type, int protocal)

domain 参数⽤来指定协议族,⽐如 AF_INET ⽤于 IPV4、AF_INET6 ⽤于 IPV6、AF_LOCAL/AF_UNIX ⽤于本机;
type 参数⽤来指定通信特性,⽐如 SOCK_STREAM 表示的是字节流,对应 TCP、SOCK_DGRAM 表示的是数据报,对应 UDP、SOCK_RAW 表示的是原始套接字;
protocal 参数原本是⽤来指定通信协议的,但现在基本废弃。因为协议已经通过前⾯两个参数指定完成,protocol ⽬前⼀般写成 0 即可。

根据创建 socket 类型的不同,通信的⽅式也就不同:
实现 TCP 字节流通信: socket 类型是 AF_INET 和 SOCK_STREAM;
实现 UDP 数据报通信:socket 类型是 AF_INET 和 SOCK_DGRAM;
实现本地进程间通信: 本地字节流 socket 类型是 AF_LOCAL 和 SOCK_STREAM,本地数据报 socket 类型是 AF_LOCAL 和 SOCK_DGRAM。另外,AF_UNIX 和 AF_LOCAL 是等价的,所以
AF_UNIX 也属于本地 socket。

  • 29
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

maplesea7

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

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

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

打赏作者

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

抵扣说明:

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

余额充值