当前位置: 首页 > article >正文

Linux进程间通信

“休息过长就会发霉。”--沃尔特·司各特

在讲述完毕Linux中的基础IO之后,我们要来到Linux中很重要的一个模块,即进程间通信。

目录

1.引入

2.管道

2.1内容

2.2原理

2.3操作

2.3.1匿名管道的创建

2.3.2命名管道的创建

1.引入

进程间通信(ipc)是指:操作系统为用户提供的几种进程间通信方式,让进程之间可以实现通信功能。现在的问题是,进程间为什么不能直接进行通信,而需要操作系统提供通信方式。

这个问题的答案和之前的提到的内容有关,我们知道:进程在访问数据的时候都是通过地址来访问的,然而一个进程中的地址都是虚拟地址,在经过页表映射之后才可以访问实际的物理地址。

因此当一个进程想要给另外一个进程传递数据(进程间通信)的时候,就需要将空间地址传递给对方。但是就接收数据的进程而言,它其中的页表映射内容和发送数据进程的页表内容大相径庭,所以接收数据的进程无法访问到具体的数据地址。

该设计的初衷是让进程具有独立性(稳定)与之伴随着的问题便是:进程间的通信无法直接完成。所以需要操作系统来提供一些相关的进程间通信方式:管道、共享内存、消息队列和信号量

2.管道

2.1内容

管道的作用:用于实现数据(资源)的传输。

管道的特性:半双工通信--可以选择方向的单向通信(同一时间不能既发送数据又接收数据),正如其名,管道里的流水既可以流出也可以流入,但无法同一时间既流出又流入。

管道的本质:内核中的一块缓冲区(内核空间中的内存--由系统进行管理)

管道的分类:

  • 匿名管道:管道没有标识符,无法被其他进程找到,只能做拥有具有亲缘关系的进程间通信;
  • 命名管道:管道具有标识符,能够被其他进程找到,可以做用于同意主句的任意进程间通信。

2.2原理

匿名管道的原理:父进程创建了一个匿名管道之后,会收到匿管道对应的文件描述符,进而可以对匿名管道中的内容做增删查改等基本操作。而当子进程创建出来之后,子进程复制父进程,则会复制到同一个匿名管道的操作句柄,进而也可以对其进行基本的操作。

如此操作之后,则会产生数据交互区,即实现了父子进程间的通信。其中发送数据就是向管道写入数据,接受数据就是从管道读取数据。

不过值得注意的是,刚才有提到管道的“文件描述符”这一名词,这是因为管道本质上是一种特殊的文件,因为Linux中一切皆文件。但是管道并不是传统意义上的磁盘文件,它的本质是内存。

命名管道的原理:本质上也是内核中的一块缓冲区。与匿名管道不同的便是,它在创建的时候有名字,可以用于同一主机上任意进程间通信。其余的原理基本同匿名管道。

2.3操作

2.3.1匿名管道的创建

int pipe(int pipefd[2]);

pipe接口的功能是创建一个匿名管道,并通过参数返回管道的两个操作句柄。其中pipefd是一个具有两个整形元素的数据,内部创建管道会将相关的描述符存储在数据之中。

  • pipefd[0] -- 用于从管道中读取数据;
  • pipefd[1] -- 用于从管道中写入数据。

返回值:创建成功返回0,失败返回-1。注意创建匿名管道之前,一定要在创建子进程之前。

在了解匿名管道的概念和操作之后,我们照旧通过代码来对上述内容进行一个简单的实践。

最后执行上述代码,我们在终端上得到这样的结果:

可以明显的观察到,子进程1对管道写入了数据,子进程2对管道中的数据进行了读取。这样的结果很好的阐明了:管道对于进程间通信的实现方式。

值得注意的是:当管道中的读端被关闭之后,持续向管道写入数据会导致进程崩溃,因为管道能容纳的数据有限,只存不取(只写不读)会产生数据过多,管道无法存储。因此当进程不在读取管道中的数据时,此时向管道写入数据时没有意义的,系统将进程结束--进程崩溃。

而当管道中的写端被关闭之后,则当读取完管道中的数据之后,不会产生阻塞,会返回0。这意味着当我们通过read返回得到0时,说明管道中的数据已经被读取完,无法读取到新的数据了已经。因此,我们也可以通过read的返回值,来决定什么时候停止从管道读取数据。(有些人永远等不来~)

2.3.2命名管道的创建

int mkfifo(const char* pathname, mode_t mode);

mkfifo接口的功能时创建一个命名管道,会依赖pathname参数建立特殊的FIFO文件,mode则是管道文件的访问权限。

创建成功则返回0,失败则返回-1。

照例我来通过代码对命名管道的接口进行实践:

我们向创建一个read_pipe.c文件,其中代码如上图所示,目的是从创建的管道文件中读取数据。

在此之后,我们创建一个write_pipe.c文件,来向管道写入数据,程序内容如上图所示。

生成可执行程序之后,我们复制终端,在两个终端上分别进行读写操作,发现程序运行良好,很好的实现的进程通信的目的。(结果不做展示,望大家自我实践)

值得注意的是,对于命名管道:如果我单独打开两个可执行程序,即只向管道读取数据或是只向管道写入数据会造成阻塞,直到管道被其他任意进程以另一种方式打开,进程才能继续运行。

造成这样结果的原因也十分简单,我们可以从管道的作用来理解,管道本身的作用便是实现进程间通信,若是进程间不存在通信,则会阻塞直到通信开始。这样的设计有助于节省空间,因为当一个管道不同时具备读写(即管道不通信),就没必要为其开辟对应的缓冲区。

除上述讲到的命名管道特性外,匿名管道具有的特性,命名管道都具有。


http://www.kler.cn/a/9405.html

相关文章:

  • 06.VSCODE:备战大项目,CMake专项配置
  • Django基础用法+Demo演示
  • 《新智慧》期刊的征稿范围主要包括哪些方面?
  • 【前端】Vue中如何避免出现内存泄漏
  • 【 ElementUI 组件Steps 步骤条使用新手详细教程】
  • Autosar CP DDS规范导读
  • 计讯物联小型水库雨水情测报与大坝安全监测一体化解决方案,确保水库安全运行
  • SpringAMQP
  • 数字化转型的避坑指南:细说数字化转型十二大坑
  • 实验6 TensorFlow基础
  • 亚马逊测评只能下单上好评?卖家倾向养号测评还有这些骚操作
  • Drone+Gitea CICD环境搭建流程笔记
  • Git(四):远程仓库的搭建、获取与更新
  • redis基础总结-常用命令
  • 初识C语言 ——“C Primer Plus”
  • TOGAF—架构治理
  • leaflet使用L.geoJSON加载文件,参数onEachFeature的使用方法(129)
  • git常用命令
  • 【前端】VUE3去掉控制台的warn信息
  • redis set list
  • 一天吃透计算机网络八股文
  • 初识设计模式 - 命令模式
  • 【ROS2指南-12】编写一个简单的发布者和订阅者
  • IDEA2020.1 Failed to execute goal org.codehaus.mojo:exec-maven-plugin
  • Sentry安装使用(最全最细)
  • 你是真的“C”——宏与函数的英雄本色