一、信号 Signal

  • 信号是 Linux 系统响应某些条件而产生的一个事件,由操作系统事先定义,接收到该信号的进程可以采取自定义的行为,这是一种“订阅-发布”的模式。
  • 信号来源分为硬件来源和软件来源:
    • 硬件来源:如按下 CTRL+C、除 0、非法内存访问等;
    • 软件来源:如 Kill 命令、Alarm Clock 超时,当 Reader 中止之后又向管道写数据等。
  • 如下所示,Linux 系统上支持的 30 种不同类型的信号:

  • 一般的信号是都是由一个错误产生的。以除 0 为例,在 x86 机器上 DIV 或 IDIV 指令除数为 0 时,会引发 0 号中断,编号 #DE(Divide Error),即所谓除零异常。这是一个硬件级中断,会导致陷入内核,执行操作系统预定义在 IDT 中的中断处理程序,而操作系统处理这个异常的方法,就是向进程发送一个信号 SIGFPE。如果进程设置相应的 signal handler,就执行进程的处理方法。否则,执行操作系统的默认操作,一般这种信号的默认操作是杀死进程。
  • 同理,溢出、非法内存访问(越界)、非法指令等也都属于硬件中断,由操作系统处理。操作系统会将这些硬件异常包装成“信号”发送给进程。如果进程不处理这几个异常信号,那么默认的行为就是挂掉。
  • 但是,信号也可以作为进程间通信的一种方式,明确地由一个进程发送给另一个进程。
  • 进程如何发送信号?
    • 操作系统提供发送信号的系统调用;
    • 该系统调用会将信号放到目标进程的信号队列中;
    • 如果目标进程未处于执行状态,则该信号就由内核保存起来,直到该进程恢复执行并传递给它为止;
    • 如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。
  • 进程如何接收信号?
    • 每个进程有一个信号队列,放其它进程发给它、等待它处理的信号;
    • 进程在执行过程中的特定时刻,检查并处理自己的信号队列,如从系统空间返回到用户空间之前;
    • 发送信号时,必须指明发送目标进程的号码。一般用在具有亲缘关系的进程之间。
  • 用户进程对信号的处理过程有三种:
    • 处理信号:定义信号处理函数,当信号发生时,执行相应的处理函数;
    • 忽略信号:当不希望接收到的信号对进程的执行产生影响,而让进程继续执行时,可以忽略该信号,即不对信号进程作任何处理;
    • 不处理也不忽略:执行默认操作,linux 对每种信号都规定了默认操作;
  • 有的信号,用户进程是无法处理也无法忽略的,比如 SIGSTOP、SIGKILL 等。信号处理程序是一个用户层函数,进程可以为某个信号指定一个信号处理程序,接收到信号后,进程会跳转执行信号处理程序,执行完成后再返回到中断位置的下一条指令继续执行:

二、管道 Pipe

  • 管道命令,在 Linux Shell 中经常使用,一般使用管道操作符 | 来表示两个命令之间的数据通信。比如:
ps -ef | grep java | xargs echo
  • 管道操作符的内部实现其实就是 Linux 的管道接口,由管道操作符 | 分割的每个命令是独立的进程,各个进程的标准输出 STDOUT,会作为下一个进程的标准输入 STDIN。

① 定义

  • 管道是一种半双工的通信方式,数据只能单向流动,上游进程往管道中写入数据,下游进程从管道中接收数据。如果想实现双方通信,那么需要建立两个管道。
  • 管道适合于传输大量信息。管道发送的内容是以字节为单位的,没有格式的字节流。

② 创建管道

  • 通过 pipe() 系统调用来创建并打开一个管道,当最后一个使用它的进程关闭对他的引用时,pipe 将自动撤销。
  • 通过 pipe() 创建的是匿名管道,只能用于具有亲缘关系的进程之间(父子进程或兄弟进程)。

③ 管道的实现

  • 管道就是一个文件,是一种只存在于内存中的特殊的文件系统。
  • 在 Linux 中,管道借助文件系统的 File 结构实现,父进程使用 File 结构保存向管道写入数据的例程地址,子进程保存从管道读出数据的例程地址,这解释了上文所说的:
    • 单向流动;
    • 只能用于具有亲缘关系的进程之间。
  • 管道是由内核管理的一个缓冲区,缓冲区被设计成为环形的数据结构,以便管道可以被循环利用(循环队列)。

④ 管道的同步

  • 管道是一个具有特定大小的缓冲区:
    • 操作系统会保证读写进程的同步;
    • 下游进程或者上游进程需要等另一方释放锁后才能操作管道,管道就相当于一个文件,同一时刻只能有一个进程访问;
    • 当管道为空时,下游进程读阻塞;当管道满时,上游进程写阻塞;
    • 管道不再被任何进程使用时,自动消失。

三、命名管道 FIFO

  • Linux 管道包含匿名管道和命名管道,上文的匿名管道,只能用在亲缘进程中,管道文件信息保存在内存里。
  • 命名管道(FIFO)可用于没有亲缘的进程间,Pipe 和 FIFO 除了建立、打开、删除的方式不同外,二者几乎一模一样。
  • 通过 mknode() 系统调用或者 mkfifo() 函数建立命名管道,一旦建立,任何有访问权的进程都可以通过文件名将其打开和进行读写,而不局限于父子进程。
  • 建立命名管道时,会在磁盘中创建一个索引节点,命名管道的名字就相当于索引节点的文件名。索引节点设置了进程的访问权限,但是没有数据块。
  • 命名管道实质上也是通过内核缓冲区来实现数据传输,有访问权限的进程,可以通过磁盘的索引节点来读写这块缓冲区。
  • 当不再被任何进程使用时,命名管道在内存中释放,但磁盘节点仍然存在。

四、信号量 Semaphore

  • 信号量是一种特殊的变量,对它的操作都是原子的,有两种操作:V(signal())和 P(wait())。
  • V 操作会增加信号量 S 的数值,P 操作会减少它:
    • V(S):如果有其他进程因等待 S 而被挂起,就让它恢复运行,否则 S 加 1;
    • P(S):如果 S 为 0,则挂起进程,否则 S 减 1;
    • P、V 来自于荷兰语:Probeer (try)、Verhoog (increment)。
  • 如果信号量是一个任意的整数,通常被称为计数信号量(Counting semaphore),或一般信号量(general semaphore);如果信号量只有二进制的 0 或 1,称为二进制信号量(binary semaphore)。在 Linux 系统中,二进制信号量又称互斥锁(Mutex),信号量可以用于实现进程或线程的互斥和同步。
  • 信号量在底层的实现是通过硬件提供的原子指令,如 Test And Set、Compare And Swap 等。比如 golang 实现互斥量就是使用了 Compare And Swap 指令(go)。

五、共享内存 Shared Memory

  • 共享内存顾名思义,允许两个或多个进程共享同一段物理内存,不同进程可以将同一段共享内存映射到自己的地址空间,然后像访问正常内存一样访问它,不同进程可以通过向共享内存端读写数据来交换信息。
  • 一个进程可以通过操作系统的系统调用,创建一块共享内存区;其他进程通过系统调用把这段内存映射到自己的用户地址空间中;之后各个进程向读写正常内存一样,读写共享内存。共享内存区只会驻留在创建它的进程地址空间内。
  • 共享内存的优点是简单且高效,访问共享内存区域和访问进程独有的内存区域一样快,原因是不需要系统调用,不涉及用户态到内核态的转换,也不需要对数据不必要的复制。
  • 比如管道和消息队列,需要在内核和用户空间进行四次的数据拷贝(读输入文件、写到管道;读管道、写到输出文件),而共享内存则只拷贝两次:一次从输入文件到共享内存区,另一次从共享内存到输出文件。


  • 此外,消息传递的实现经常采用系统调用,也就经常需要用户态和内核态互相转换;而共享内存只在建立共享内存区域时需要系统调用;一旦建立共享内存,所有访问都可作为常规内存访问,无需借助内核。
  • 共享内存的缺点是存在并发问题,有可能出现多个进程修改同一块内存,因此共享内存一般与信号量结合使用。
  • Linux 的 2.2.x 内核支持多种共享内存方式,如 mmap() 系统调用,Posix 共享内存,以及系统 V 共享内存;
    • mmap() 系统调用的主要作用是将普通文件映射到进程的地址空间,然后可以像访问普通内存一样对文件进行访问,不必再调用 read(),write() 等操作;mmap() 不是专门用来共享内存的,但是多个进程可以通过 mmap() 映射同一个普通文件,来实现共享内存。



    • 系统 V 则是通过映射特殊文件系统 shm 中的文件实现进程间的共享内存,通过 shmget 可以创建或获得共享内存的标识符,取得共享内存标识符后,通过 shmat 将这个内存区映射到本进程的虚拟地址空间。
  • 有关 mmap() 系统调用、系统 V 共享内存的详细介绍,以及两者的对比,可以参考:
    • Linux环境进程间通信 - 共享内存(上);
    • Linux环境进程间通信 - 共享内存(下)。

六、消息队列 Message Queue

  • 消息队列是一个消息的链表,保存在内核中。消息队列中的每个消息都是一个数据块,具有特定的格式。操作系统中可以存在多个消息队列,每个消息队列有唯一的 key,称为消息队列标识符。
  • 消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。和信号相比,消息队列能够传递更多的信息。与管道相比,消息队列提供了有格式的数据,但消息队列仍然有大小限制。
  • 消息队列允许一个或多个进程向它写入与读取消息。消息的发送者和接收者不需要同时与消息队列交互。消息会保存在队列中,直到接收者取回它。也就是说,消息队列是异步的,但这也造成了一个缺点,就是接收者必须轮询消息队列,才能收到最近的消息。
  • 操作系统提供创建消息队列、取消息、发消息等系统调用。操作系统负责读写同步:若消息队列已满,则写消息进程排队等待;若取消息进程没有找到需要的消息,则在等待队列中寻找。
  • 消息队列和管道相比,相同点在于二者都是通过发送-接收的方式进行通信,并且数据都有最大长度限制。不同点在于消息队列的数据是有格式的,并且取消息进程可以选择接收特定类型的消息,而不是像管道中那样默认全部接收。

七、套接字 Socket

  • 不同的计算机的进程之间通过 socket 通信,也可用于同一台计算机的不同进程。
  • 需要通信的进程之间首先要各自创建一个 socket,内容包括主机地址与端口号,声明自己接收来自某端口地址的数据。
  • 进程通过 socket 把消息发送到网络层中,网络层通过主机地址将其发到目的主机,目的主机通过端口号发给对应进程。
  • 操作系统提供创建 socket、发送、接收的系统调用,为每个 socket 设置发送缓冲区、接收缓冲区。

八、总结

方式 传输的信息量 使用场景 关键词
信号 少量 任何 硬件来源、软件来源 / 信号队列
管道 大量 亲缘进程间 单向流动 / 内核缓冲区 / 循环队列 / 没有格式的字节流 / 操作系统负责同步
命名管道 大量 任何 磁盘文件 / 访问权限 / 无数据块 / 内核缓冲区 / 操作系统负责同步
信号量 N 任何 互斥同步 / 原子性 / P 减 V 增
共享内存 大量 多个进程 内存映射 / 简单快速 / 操作系统不保证同步
消息队列 比信号多,但有限制 任何 有格式 / 按消息类型过滤 / 操作系统负责同步
套接字 大量 不同主机的进程 读缓存区 / 写缓冲区 / 操作系统负责同步

【网络通信与信息安全】之深入解析进程之间的通信方式相关推荐

  1. 进程之间的通信方式-共享内存

    共享内存是被多个进程共享的一部分物理内存.共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容.原理图如下: 共享内存的实现分 ...

  2. html 如何实现一条竖线边上有 刻度_记一次腾讯面试:进程之间究竟有哪些通信方式?如何通信? ---- 告别死记硬背||CSDN博文精选...

    作者:帅地 有一次面试的时候,被问到进程之间有哪些通信方式,不过由于之前没深入思考且整理过,说的并不好.想必大家也都知道进程有哪些通信方式,可是我猜很多人都是靠着"背"来记忆的,所 ...

  3. 记一次腾讯面试:进程之间究竟有哪些通信方式?如何通信? ---- 告别死记硬背

    有一次面试的时候,被问到进程之间有哪些通信方式,不过由于之前没深入思考且整理过,说的并不好.想必大家也都知道进程有哪些通信方式,可是我猜很多人都是靠着"背"来记忆的,所以今天的这篇 ...

  4. 【网络通信与信息安全】之深入解析两台主机之间的通信过程和原理

    一.前言 本文通过在 Docker 容器中执行命令,来深入了解两台主机之间的通信过程.阅读完本文,您将熟悉以下内容: Docker 的基本操作: 创建 socket 并发送 HTTP 请求: 路由表. ...

  5. 【网络通信与信息安全】之深入解析从输入一个URL到页面加载完成的过程

    一.前言 从输入一个 URL,然后按下回车到显示页面,中间发生了什么?这是一道经典的面试题,不光前端面试会问到,后端面试也会被问到.这道题没有一个标准的答案,它涉及很多的知识点,面试官会通过这道题了解 ...

  6. 信息系统管理工程师复习笔记02 计算机网络通信与信息安全 原创整理

    第二章计算机网络通信与信息安全 资料根据信息系统管理工程师考试大纲进行整理,资料全部来源于信息系统管理工程师教程,亲自整理,用于自身复习,现在分享出来,欢迎指正!(从word中拿出来,排版会有点问题, ...

  7. Linux两个进程交换信息,如何在Linux中的进程之间交换二进制数据

    我需要创建一个可以进行无线网络扫描的linux应用程序,将结果放入一个结构中并以某种方式将其发送到另一个将使用该数据的主应用程序.我最初的想法是在主应用程序中创建一个管道,fork并通过execl启动 ...

  8. Linux 进程及进程之间的通信机制——管道

    参考: LInux C编程从初学到精通 电子工业出版社 Linux 进程 Linux 进程简介 Linux是一个多用户多任务的操作系统,多用户是指多个用户可以在同一时间使用同一台计算机系统:多用户是指 ...

  9. python进程的注意点(进程之间不共享全局变量、主进程会等待所有的子进程执行结束再结束)

    1. 进程的注意点介绍 进程之间不共享全局变量 主进程会等待所有的子进程执行结束再结束 2. 进程之间不共享全局变量 import multiprocessing import time# 定义全局变 ...

最新文章

  1. 【实用快捷键】设置WebStorm中Show in Explorer(在资源管理器中打开)快捷键Alt+Shift+R(类似VSCode)
  2. BNF范式(巴科斯范式)简介
  3. YII 控件使用笔记
  4. 安卓开发之探秘蓝牙隐藏API
  5. 前端基础-CSS的各种选择器的特点以及CSS的三大特性
  6. 漫步数理统计十一——连续随机变量(下)
  7. python----动态规划
  8. 【任务悬赏】就地过年,原地充电,华为云社区喊你拿新年红包啦!
  9. Spring的三大核心接口——BeanFactory、ApplicationContext、WebApplicationContext
  10. php sql慢查询,Select多行SQL语句慢查询优化
  11. 接口测试工具-Jmeter使用笔记(六:从文本读取参数)
  12. STM32—驱动GY85-IMU模块
  13. 中了磁碟机病毒,惨啊!!!
  14. JWplayer 用法及参数介绍(二)
  15. 药物组合疗法的机器学习方法(综述类)
  16. 科学研究:青少年熬夜更易产生反社会行为,还会延缓大脑发育
  17. 由验证控件引起的IE的超级爆笑BUG
  18. C语言小项目--贪吃蛇游戏
  19. COD测定仪小故障不及时处理,可能会演变成的大问题
  20. 直接收藏-超级好用的国内配色网站

热门文章

  1. oracle 触发器的种类和触发事件
  2. 程序员,请您不要老是熬夜
  3. Kubernetes容器集群 - harbor仓库高可用集群部署说明
  4. Benny:只处理那些NVARCHAR字段,并且NTEXT也是小于4000个字符时的情况.不管类型.只管长度....
  5. Django日志模块配置
  6. Vue.js 笔记之 img src
  7. mysql 用户管理和权限设置
  8. 从零开始Bootstrap3
  9. Enterprise Library 5.0 开发向导- 简介(1)
  10. php udp 非阻塞,使用非阻塞udp读取时丢失消息