本文主要介绍进程间通信IPCInter Process Communication)的一些方式,包括:

  • 管道(管道/匿名管道,命名管道/FIFO)

  • 消息队列/报文队列

  • 共享内存

  • 信号量/信号灯

  • 信号/软中断

  • 套接字/socket

管道

包括管道(Pipe)与命名管道(named pipe)。

管道/匿名管道

管道(Pipe),也称匿名管道,是Linux下最常见的进程间通信的方式之一,它是在两个进程之间实现一个数据流通的通道。优点在于简单易用,缺点在于功能简单,有许多限制。

匿名管道在系统中是没有实名的,并不可以在文件系统中以任何方式看到该管道,它只是进程的一种资源,会随着进程的结束而被系统清除。

  • 管道没有名字,所以也称为匿名管道
  • 管道是半双工的,数据只能向一个方向流动。若要进行双向通信,需要建立两个管道
  • 只能用于父子进程或兄弟进程等具有亲缘关系之间的进程通信
  • 单独构成一种独立的文件系统。管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,并且只存在于内存中
  • 数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲的末尾,并且每次都是从缓冲区的头部读出数据
  • 管道的缓冲区是有限的(管道只存在于内存中,在创建管道时,为缓冲区分配一个页面大小)
  • 管道传送的是无格式的字节流,因而读出方和写入方须事先约定好数据的格式,比如多少字节算做一个消息等

命名管道/FIFO

命名管道(named pipe)也称为FIFO,它是一种文件类型,在文件系统中可以看到它,创建一个FIFO文件类似于创建一个普通文件。

管道应用的一个最大限制在于它没有名字,因而只能用于具有亲缘概关系进程间的通信。而FIFO提供了一个路径名与之关联,以FIFO文件的形式存在于文件系统中,通过路径访问的方式,可以在不相关的进程间通信。

  • 命名管道可用于任何两个进程间的通信,因而比管道使用灵活方便
  • 命名管道作为一种特殊的文件存放在系统文件中,而不像管道那样存在于内存(使用完消失),除非对其进行删除操作,否则该命名管道不会消失
  • FIFO的出现解决了系统在应用过程中产生的大量中间临时文件的问题,它可以被Shell调用使数据从一个进程到另一个进程,系统不必为该中间通道清理不必要的垃圾,或者释放该通道的资源,它可以被留作后来的进程使用。

消息队列/报文队列

消息队列,也称报文队列,是一种以链表式结构组织的一组数据,存放在内核中,是由各进程通过消息队列标识符来引用的一种数据传送方式。

  • 消息队列 就是一个消息的链表,可以把消息看作一个记录,具有特定的格式及特定的优先级
  • 消息队列是随内核持续的,消息队列会一直存在,需要调用接口显式删除或使用命令删除
  • 每个消息队列在系统范围内对应唯一的键值
  • 克服了管道数据无格式字节流的缺点
  • 消息队列可以实现消息的随机查询,不一定要以先进先出的次序读取,也可以按消息类型读取

附:

进程持续:IPC一直存在,直到打开IPC对象的最后一个进程关闭该对象为止,如管道有名管道

内核持续:IPC一直持续到内核重新自举或者显示删除该对象为止,如消息队列信号量共享内存

文件系统持续:IPC一直持续到显示删除该对象为止

共享内存

共享内存可以说是Linux下最快速、最有效的进程间通信方式。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间,进程A可以及时看到进程B对内存中数据的更新,反之,B也可以看到A的修改。

  • 显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的复制

    (对于管道和消息队列等通信方式,需要在内核空间和用户空间进行四次的数据复制,而共享内存则只复制两次:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程间共享内存,并不是使用后就接触映射,而是保持共享区,直至通信完毕)

  • 最大不足在于,由于多个进程对同一内存区域就具有访问的权限,各个进程之间的同步问题显得尤为重要,通常与信号量结合使用解决同步问题

信号量/信号灯

信号量(Semaphore),也称信号灯,其原理是一种数据操作锁的概念,它本身不具备数据交换的功能,而是通过控制其它的通信资源(文件、外部设备等)来实现进程间通信。

  • 信号量本身不具备数据传输的功能,它只是一种外部资源的标识,负责协调各个进程,保证它们正确合理地使用公共资源
  • 信号量分为单值和多值两种,前者只能被一个进程获得,后者可以被若干个进程获得
  • 信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作

附:PV操作

1962年,荷兰学者Dijksrta在参与X8计算机的开发中设计并实现了具有多道程序运行能力的操作系统——THE Multiprogramming System。为了解决这个操作系统中进程(线程)的同步与互斥问题,他巧妙地利用火车运行控制系统中的“信号灯”(semaphore,或叫“信号量”)概念加以解决。信号量的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。注意,这个信号量的值仅能由PV操作来改变。

PV操作由P操作原语V操作原语组成(原语也叫原子操作Atomic Operation,是不可中断的过程),PV两个字母是荷兰文 Passeren(通过)和Vrijgeven(释放)的简称,对信号量(注意不要和Windows中的信号量机制相混淆)进行操作,具体定义如下:

P(S):

​ ①将信号量S的值减1,即S=S-1;

​ ②如果S>=0,则该进程继续执行;否则该进程置为等待状态。

V(S):

​ ①将信号量S的值加1,即S=S+1;

​ ②该进程继续执行;如果该信号的等待队列中有等待进程就唤醒一等待进程。
用PV操作实现多线程的同步与互斥是非常简单的,只要考虑逻辑处理上合理严密而不用考虑具体技术细节。

参考:https://blog.csdn.net/morewindows/article/details/7650470

信号/软中断

信号机制是进程之间相互传递消息的一种方法,信号全程为软中断信号,也有人称作软中断

从它的命名可以看出,它的实质和使用很像中断。注意它和信号量完全不是一个概念。

  • 信号是系统中用于处理异步事件的主要手段
  • 信号只是用来通知某个进程发生了什么事,并不给该进程传递任何数据
  • 在Linux的信号控制中,有时不希望进程在接收到信号时立刻中断进程的执行,也不希望该信号完全被忽略,而是延迟一段时间再去调用相关的信号处理函数,可以通过阻塞信号的方法来实现

套接字/socket

套接字是操作系统内核的一个数据结构,它是网络中的节点进行相互通信的门户。

套接字也就是网络进程的ID,网络通信,归根到底还是进程间的通信(不同计算机上的进程间通信)。在网络中,每个节点(计算机或路由器)都有一个网络地址,也就是IP地址,但仅凭网络地址还不能确定计算机中的哪个进程,需要端口号(port)来一一对应进程。

  • 套接字相比较其它的IPC,它可以实现不同计算机之间的进程间通信

参考:

精通Linux C编程

https://blog.csdn.net/wh_sjc/article/details/70283843