Linux 进程间通信

进程间通信的目的

  1. 数据交换
  2. 状态同步
  3. 事件通知
  4. 资源共享
  5. 进程控制

一般来将进程之间的通信根据内容可以划分为两种,一种是传输控制信息,另一种是传输大量的数据。一般控制信息只有一个或几个字节,用来达到进程控制操作的高速执行(比如利用信号量实现进程同步);大量数据的传输一般用于进程之间信息的交换(CS架构中,客户端和服务器之间大量数据的交互)[4]。

发展过程

Linux中进程间通信(IPC)是从Unix中衍生过来的,最初的Unix具备管道、FIFO、信号,后来经过贝尔实验室和BSD(加州大学伯克利分校的伯克利软件发布中心)的拓展形成了现在的进程通信基础。

贝尔实验室对早期的Unix进程通信方式进行了拓展,形成了“System V IPC”, 通信局限于单个计算机内;BSD打破了该限制,形成了基于套接字的进程间通信机制。而Linux将两者间的特色都继承了下来,同时它还兼容POSIX IPC标准(目前POSIX标准更加通用一些)。

通信方式

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

管道(Pipe)

管道是Unix对操作系统的贡献之一,它是一个环形缓冲区,允许两个进程之间以生产者/消费者的模式进行通讯,是一个先进先出的队列[2]。

管道是一种半双工的通信方式(消息只能单向传输)。数据传输方式似于流式传输,不提供随机访问,进程之间要约定一套数据协议来解析传输的数据[5]。数据由操作系统缓冲,直到接收方从中读取[1],它可以像文件描述符一样去通过select,poll和epoll函数去监听。

管道在建立过程中需要双方进行协同操作(有些API中打开管道过程中会阻塞,直到双方都执行了打开操作),不能独立于进程而存在。

管道分为匿名管道和命名管道(Named Pipe):匿名管道只有亲缘关系的进程(父进程与子进程之间,或子进程与子进程之间)可以共享;没有亲缘关系的进程之间只能通过命名管道通信。

FIFO(first-in first-out special file, named pipe)在Linux中属于一种命名管道。通过绑定文件系统中指定文件来代替标准的输入输出[1],允许多个进程访问。

Unix中对管道自动采取互斥保护措施,每次只有一个进程能访问管道[2]。

信号(signal)

信号是用于向一个进程通知某种事件的发生。除了进程间通信外还可以发送给进程本身。因为无法利用信号传输数据,所以信号主要用于进程管理上。

Unix中内核平等的对待所有的信号,对于同时发生的信号,一次只给进程一个信号,而没有特定的顺序。进程间可以互相发送信号,内核在内部也可能发送信号[2]。

我们经常使用的 Ctrl+C 就是发送一个退出的信号给某一进程。

消息(Message)队列

消息队列的功能类似于信箱,它也可以在没有亲缘关系的进程之间去使用,在双方没有直接连接的情况下进行通信[1]。消息队列是直接面对消息的,消息的发送者指定发送的消息的类型和大小,消息的接收者指定消息接收的方式(先进先出的规则或指定的消息类型)。

克服了信号承载信息量少和管道只能承载无格式字节流的缺点,可以实现进程之间多对一的通信(C/S模式)。即使是接收者没有打开消息队列,发送者仍然可以打开队列发送消息而不阻塞,不需要额外的同步操作,可以避免管道中的打开和读写阻塞问题。

消息队列中存储的消息数量是有上限的,同时每一条消息的大小也是有上限的。

共享内存

多个进程将同一个共享的内存段映射到自己的进程资源中,向同一段内存中读写数据来进行数据交互,是进程之间通信速度最快的一种方式,经常与其他通信机制(信号量)混合使用来达到进程间通信的目的[5]。

Pipe和FIFO以及消息队列在交互信息的时候都需要通过内核拷贝的方式--将数据从发送方拷贝到内核缓存,再从内核缓存中拷贝到接收方[6],一次通信要COPY两次。而共享内存则不需要通过内核拷贝,所以速度上要比Pipe、FIFO和消息队列快一些。

共享内存通常会在实时性要求比较高的业务情况下使用,比如金融系统中,但涉及到分布式的服务,经常需要跨主机的情况共享内存不太适合。

套接字(Socket)

常用于跨主机的进程通信,由BSD最早提供,后来被linux所吸收。它有通过网络端口跨主机通信的TCP/IP socket(面向流数据)和UDP socket(面向消息),也有UNIX domain socket这种只能在本地使用的类型。

Unix domain socket 类似于TCP/IP互联网套接字,但所有通信都在内核中进行,使用文件系统作为其地址空间。进程做为inode来使用一个Unix domain socket,并且多个进程可以与同一个套接字进行通信(docker服务监听本地请求就是通过Unix domain socket)[1]。

进程之间可以使用socket进行双向通信。

信号量

用于不同进程之间,或者同一进程中不同线程之间同步的通信机制。采用PV原语来实现。

参考链接

猜你喜欢

转载自www.cnblogs.com/cnblogs-wangzhipeng/p/9584956.html