进程间通讯

  • Post author:
  • Post category:其他


进程间通讯是进程之间协作的必要部件,随着不断发展,也有许多种类的进程间通讯出现。

之前一直没有系统的进行梳理,正好最近在网上发现一篇讲的不错的文章,于是打算以此做为大纲,梳理一下进程间通讯的几个方式

先贴文


凉了!张三同学没答好「进程间通信」,被面试官挂了…_小林coding的博客-CSDN博客

简单的总结一下各个通讯方式的特点:



1.管道

为满足进程间通讯,早期UNIX系统最早使用的就是管道,管道就是内核空间内存里的一部分内存。

其实,

所谓的管道,就是内核里面的一串缓存

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

有些地方(如介绍linux和UNIX的一些书籍),介绍管道为“本质也是特殊文件”,这种说法是没有错的,因为使用管道的进程通过文件描述符进行读写,进程将其视作一个文件,这其实是Linux构建思想中的“一切皆文件”。


管道对用户进程来说就可以认为是一个虚拟文件,所谓虚拟,就在于本质并非磁盘中的文件,而是内核中的一部分内存。



管道通信:深入理解“linux 一切皆文件”的思想



关于管道的一些细节和问题



管道的创建



管道作为虚拟的文件,在文件系统中是没有相应的映像的(只有磁盘文件才有),因此需要使用pipe()调用创建新管道——这个调用返回一对文件描述符,代表入口和出口。



关于命名管道(FIFO)和匿名管道的区别



表现上,命名管道可以被多个无父子关系的进程使用,匿名必须有父子关系。

究其原因,在于命名管道创建和匿名管道的不同

匿名管道会被组织成一个独立于文件系统的,被称作pipefs特殊文件系统——但注意,其他用户程序看不到他,整合的目的只是为了适应以文件方式操作,从这个角度看,可以说pipefs文件系统是对真正文件系统的一个模仿。

image.png

而命名管道(FIFO)创建时,会在文件系统树中创建磁盘索引节点,这个文件名将被包含在目录树中,从而任何进程都能访问命名管道——因为全部进程都能访问文件系统树。

image.png



关于进程对管道相关的内存访问



管道涉及的内存在内核空间中,因此想对其读写的进程需要使用系统调用访问——因为在进程看来是文件,因此使用的系统调用不是其他,正是和文件相关的read()和write()。



关于管道涉及的内存的形状



管道在内核空间中是一个环形缓冲区,



2.消息队列

管道的通信方式是效率低的,因此管道不适合进程间频繁地交换数据。

为了解决这个问题,使用

保存在内核中的消息链表

代替

在内核中的环形缓冲区


消息队列也有其的缺点,这个缺点和管道是一脉相承的,主要导致原因就是使用了内核空间中的内存,这导致每次数据传输都要从用户态拷贝数据到内核态。



3.共享内存

为解决内核态转换问题,共享内存利用os实现的虚拟内存机制,将两个进程的一部分内存映射到同一个物理内存空间。

aHR0cHM6Ly9pbWdrci5jbi1iai51ZmlsZW9zLmNvbS85MWQwMjBiMy1lMmQ3LTRhZmEtOWY5YS00OWI2ZWRiMDU4ZWUucG5n.png



共享内存的想法其实是消息队列和管道的延伸



我们要注意到,进程的内核空间本质上是全部进程共享的,因为在切换进程的时候,内核空间占有的对应虚拟内存段将不会被切换,只有用户空间对应的才会被切换。

而对于共享内存,由于映射关系相同的原因,即使切换到不同的用户空间,那段特殊的共享内存在逻辑上相当于没有被切换。

从这一点看,共享内存对应的内存片段确实类似内核空间——只是它没有了严格限制的内核权限,更加方便。



4.套接字(Soket)

前面几种方式解决一个计算机内的进程通讯间文件,为解决不同计算机进程的通讯,人们发明了套接字。

套接字本质是一个数据结构,包含通讯目的地地址,端口号等信息,每个进程有着独特的套接字号,以此区分彼此。

一类套接字允许不同计算机的进程通过网络交换数据。

  • 基于网络型,此类套接字使用分对称方式通讯,类似网络通讯。

当然,也有一类套接字能向下兼容计算机内部进程的通讯。

  • 基于文件型,此类套接字基于本地文件系统支持,实现过程类似管道。



5.信号

信号在最早的Unix系统中即被引人,用于在用户态进程间通信。内核也用信号通知进程系统所发生的事件。信号已有30多年的历史,但只有很小的变化。



信号定义



信号(signal)是指很短的消息,可以被发送到一个进程或一组进程。发送给进程的唯一信息通常是一个



,以此来标识信号。



信号目的



使用信号的两个主要目的是:

  • 让进程知道已经发生了一个特定的事件。
    
  • 强迫进程执行它自己代码中的信号处理程序。
    

当然,让进程知道事件已经发生,进程就可能会做出一些反应,因此这两个目的通常会都被实现。



信号特点



信号是进程间通信机制中

唯一的异步通信机制

,因为可以在任何时候发送信号给某一进程,一旦有信号产生,我们就有下面这几种,用户进程对信号的处理方式。



进程对信号的响应



进程对信号有三种响应方式,比较类似于一些代码中对异常的处理方式:

  • 执行操作
  • 捕捉该信号
  • 忽略此信号



6.信号量

严格来说,信号量不算通讯方式,它是为了解决共享数据竞争问题,因此算保证进程间通讯正常运行的必要的一个机制。

信号量保证解决如下竞争问题。

  • 互斥的方式,可保证任意时刻只有一个进程访问共享资源;
  • 同步的方式,可保证进程 A 应在进程 B 之前执行;



进程使用信号量时异常终止发生的事



进程使用信号量实际上有一个问题,就是进程异常中断而无法由它手动释放持有的信号量。不解决这个问题会导致信号量最终陷入不足的境地,其他进程将会无限停留在阻塞状态。

内核对其的解决方式就是使用一种失效安全机制——进程本身会在一个sem_undo数据结构中存放自己获得的各种信号量的值,当内核发现进程错乱,则会访问这个结构,简单的还原被修改的信号量。



关于IPC(Inter-Process Communication)

IPC(Inter-Process Communication)是指多个进程之间相互通信,交换信息的方法,实际上在操作系统内部,信号量,消息队列或者信号都被视作属于这些机制。

  • 通过信号量与其他进程进行同步
  • 向其他进程发送信号或者从其他进程接受信号
  • 和其他进程共享一段内存区

可以看到,这些机制其实就是上面谈到的几类方式。



IPC机制的运行



内核有着提供这些机制元素的函数,调用这些函数,进程就相当于请求IPC资源,内核创建了对应的结构之后,分配给进程IPC标识符,进程就能随时访问资源了。