操作系统(七)—— 进程间通信

概述

进程之间需要协作完成一个事情,往往就需要通信,而且这个问题是面试的时候,面试官非常喜欢问的问题,很少有面试官会去问操作系统页面置换算法的。本文就介绍一下进程之间常用的通信方式。

信号

信号是在软件层面对中断的模拟。

特性

  • 任何时候都可以发出,无论接收信号目标进程是处于什么状态,如果接收信号的目标进程处于未运行状态,则信号会被操作系统内核保存起来,等待进程处于运行状态在传递给他。
  • 操作系统中有几十种信号,分别代表不同的意义
  • 用户态进程和内核态进程可以直接通过信号进行通信

信号的来源

  • 硬件:比如用户按下中断键(ctrl+c),系统就会向对应的终端进程发送SIGINT信号
  • 软件:比如用户在终端执行kill pid操作,系统就会向对应的进程发送SIGKILL信号

信号处理

信号一般有三种处理方式,如下:

  • 忽略
  • 默认处理:操作系统会对每一个信号设置一个默认处理
  • 捕获:接收信号的进程提前定义好信号处理函数,然后调用信号处理函数处理

下面详细解释一下第三种,捕获的处理流程。

    操作系统(七)—— 进程间通信

          图片来源:清华大学操作系统公开课

上图的处理流程如下:

第一步:进程X把信号处理函数注册到操作系统内核
第二步:操作系统接受到一个信号,然后需要中断进程的执行切换到信号处理函数
第三步:修改应用程序的堆栈,让栈中要执行的下一条指令切换到信号处理函数,当信号处理函数结束的时候,重新从刚刚修改的地方开始执行

一般来说作为一个用户程序不会自己编写信号处理函数,往往是木马病毒会这样做。

缺点

只能发送系统定义好的信号,无法发送具体的自定义信息。

管道

管道对于管道两端的进程而言就是文件,但是这个文件是一个特殊的文件,不属于文件系统。工作机制就是一个进程向管道写数据,另一个进程从管道读数据。管道是半双工的,数据只能向一个方向流动,如果想互相传输数据,需要搞两个管道。

管道的分类

  • 匿名管道:由于没有名字,所以只能用在父子进程或者兄弟进程之间通信,linux中的“|”符号就是管道的意思,比如cat file.txt|grep "hello",前面的cat file.txt就是输入,后面的grep "hello"就是接收
  • 命名管道:其实就是一个FIFO队列,在磁盘上有一个该管道(文件)的索引(文件名),所以任何进程只要有该文件的访问权限都可以访问。

缺点

传输数据只能是字节流,无法传输结构化数据。

消息队列

 消息队列按照FIFO的方式管理消息。

特性

  • 消息队列中存放的是结构化的数据,不像命名管道里面存放的是字节流。
  • 接收的时候不需要按照队列次序,而是可以根据自定义条件接收特定类型的消息。

共享内存

我们常见的更多是线程之间共享内存,因为同一个进程下面的线程可以共享进程堆,栈,文件系统,虚拟地址空间等,而不同进程之间共享内存就要求不同进程在虚拟地址空间映射的时候,映射到同一个物理空间,共享内存的效率很高属于直接通信,而上面介绍的那几个都是间接通信,但是控制机制很复杂,因为牵涉到进程并发时的安全问题,所以在多个进程同时写的时候要加锁处理,系统中没有实现这种同步机制,需要程序员自己控制。

看别的老铁的文章说共享内存之所以很快是只复制了两次,如下:

  输入文件 ——> 共享内存 ——> 输出文件

管道和消息队列需要复制4次,所以性能比较低。理由是管道和消息队列都需要系统调用,拷贝过程如下:

  输入文件 ——> 进程A ——> 内核缓冲区 ——> 进程B ——> 输出文件

我是没有搞明白上面从输入文件到进程A这个过程,如果这个需要,那共享内存也应该有这个过程,有明白的大佬,麻烦评论告知一下。

 

参考文章:

进程间通信的方式——信号、管道、消息队列、共享内存

清华大学操作系统公开课(陈瑜老师)

操作系统(七)—— 进程间通信

上一篇:Shell - egrep


下一篇:(二十七)缓存:进程内缓存要怎么玩?