OS43.【Linux】进程间通信 管道通信理论部分

目录

1.进程间通信是什么

2.进程间通信的本质

"资源"的提供方?

操作系统需要管理进程间通信的资源

IPC

简述进程间通信标准

理解基于文件级别形式的通信方式: 管道(以匿名管道为例)

实现原理

举例父子进程通过管道通信

如果父进程关掉管道文件,那么子进程读取管道文件会出错吗?

父子进程打开文件的方式

补充: tty的含义

结论: 两个进程之间必须要有血缘关系才能使用管道进行通信


1.进程间通信是什么

顾名思义,是两个或多个进程直接进行数据交互,例如发送命令、某种协同、通知......

由于进程之间天然具有独立性,它们的地址空间是相互隔离的,那么就导致进程间通信的成本比较高,显然进程间通信就需要"绕过"进程之间独立性

2.进程间通信的本质

由进程间通信定义可以得出进程间通信的本质: 让不同进程之间看到同一份"资源"

这里的"资源"可以理解为特定形式的内存空间,例如下图展示进程间的简单通信方式:

"资源"的提供方?

进程间通信的本质是让不同进程之间看到同一份"资源",那么这个"资源"由谁提供?

假设"资源"由进程提供,比如进程A和进程B要通信,"资源"由进程A提供,那么这个"资源"是属于进程A独有的,进程B想要访问这个"资源"会破坏进程A和B之间的独立性,假设不成立

因此需要操作系统(即第三方)提供"资源",可以得出: 进程访问这个"资源"进行通信,本质上就是访问操作系统

由于进程处于用户态,权限低,操作系统不允许进程直接访问操作系统的资源

进程间通信时就需要向操作系统申请使用这个"资源",那么进程需要使用系统调用来告诉操作系统进行"资源"的创建、使用、释放

操作系统需要管理进程间通信的资源

操作系统上有很多进程,它们或多或少都要通信,那么操作系统就要为这些进程提供提供很多资源,因此操作系统系统管理资源,即先描述再组织

IPC

一般的操作系统会有一个隶属于文件系统的独立的IPC通信模块,IPC全称是Inter-Process Communication,翻译过来是进程间通信

*注: 上述将的文件系统不仅仅管理磁盘上的文件,也管理内存级别的文件

简述进程间通信标准

早期的Linux内核是没有进程间通信模块的,这就导致开发者们需要各自指定通信标准

为了统一标准,需要权威人员来定制标准,方便不同设备之间 设备内的进程与进程之间通信等

最终落成两个标准:

System V 本机内部通信

POSIX 网络通信

理解基于文件级别形式的通信方式: 管道(以匿名管道为例)

实现原理

设想一下,如果进程间通信使用磁盘上的文件,那么由于访问外设,速度会很慢,而管道就能解决这个问题

之前在OS5.【Linux】基本指令入门(4)文章提到过管道,本文继续深入讲解管道

可以这样理解: 下图特定形式的内存空间可以是管道,管道是Unix中最古老的进程间通信的形式,而且是基于内存文件级别形式的通信方式

进程使用管道前,需要使用系统调用来创建管道文件并打开

进程管理文件是靠fd_array来进行的,之前在OS30.【Linux】文件IO (2) 文件描述符文章讲过,如下图

虽然管道在内存中,但根据Linux"的一切皆文件"思想,那么同样可以使用open、read和write来操作管道文件,比如:

对于普通文件,如果需要修改里面的内容,需要先将磁盘的文件加载到内存,在内存中修改数据,之后将其写回磁盘

→内存中修改过的数据和磁盘文件的数据不一样,就称”内存中修改数据”为”脏” 数据

得出一个事实: 访问磁盘中文件必须先将文件中的数据加载到内存中

虽然管道文件和普通文件一样有自己的inode,file_operations和缓冲区,但是管道文件的内容并不刷新到磁盘中,这是由管道的特性决定的: 内存级别的文件

举例父子进程通过管道通信

父子进程想要使用管道通信,那么它们的fd_array中就必须要有指针指向同一个管道文件的file_struct

可以这样做: 父进程打开管道文件,之后父进程创建子进程,那么子进程就能和父进程一样访问同一个管道文件

原因: 根据OS18.【Linux】进程基础知识(2)文章的结论: 用fork()创建子进程时,父子进程共用同一份代码,数据以写时拷贝的方式各自私有,那么子进程继承了父进程的file_struct,如果之后父子进程都没有打开或关闭文件(可以理解为父进程刚创建子进程那一瞬间),根据写时拷贝的原理,父子进程的file_struct指针都指向内存中的同一个file_struct

得出进程间通信的本质前提: 先让不同的进程看到同一份资源,这里是让父子进程都能看到同一个内存级别的文件——管道

如果父进程关掉管道文件,那么子进程读取管道文件会出错吗?

答: 不会,因为管道也是文件,那么必然有引用计数这个属性(有关引用计数的知识参见OS30.【Linux】文件IO (2) 文件描述符文章),父进程关掉管道文件,引用计数--,但是管道文件的引用计数不会为0,因为子进程仍然在打开管道文件

父子进程打开文件的方式

管道文件目标: 让一个进程读,另外一个进程写

设想一下,如果父进程以只读方式打开管道文件, 之后父进程创建子进程,由于父子进程之间具有继承关系,那么子进程也只能以只读方式打开管道文件无法做到一个进程读,一个进程写

初步解决方法:使用两个指针执行同一个管道文件,一个指针用来读,另外一个指针用来写,

假设父进程写管道,子进程读管道,最终的情况如下图:

补充: tty的含义

*注: 上图的tty是teletypewriter,即电传打字机,如下图:

但为什么标准输入(fd=0)、标准输出(fd=1)、错误输出(fd=2)在图中用tty表示呢?

askubuntu.com What is a tty, and how do I access a tty?给出了回答:

回答中指出: tty是纯文本终端,而标准输入、标准输出、错误输出都是输出到终端显示器上的,那么就能理解tty的含义了

父进程写管道,子进程读管道,那么父进程以怎样的方式打开管道文件才能实现进程间通信呢?

方法1.父进程一开始只打开写端

子进程继承父进程的file_struct,那么:

但子进程没有打开读端,这就出问题了,那么需要改进方法1

方法2.父进程一开始打开读端和写端

注意到图中父进程分两次打开管道文件,一次打开读端(r),一次打开写端(w),非常不建议一次打开读写端(rw),因为读端和写端的指针指向文件中的位置不一定相同

(注: 图中的fd[0]=3,fd[1]=4之后再解释)

子进程继承父进程的file_struct,那么:

这样父进程就能写管道,子进程就能读管道了! 

由于父进程写管道,子进程读管道,那么最好将父进程读端关闭,子进程写端关闭

        原因: 目标是"让一个进程读,另外一个进程写",如果父子进程都参与读写,可能父进程读的太急,父进程会读到自己刚写的数据,这样子进程无法取得父进程写入的数据了; 同理, 如果子进程读的太急,子进程会读到自己刚写的数据,父进程无法取得子进程写入的数据,可以对管道中的数据要加标签来区分子进程和父进程写入的数据,但是操作系统维护成本比较高

强烈建议关闭父进程的写端和子进程的读端,否则父进程或子进程可能误操作,严格遵循单向通信

为了简单起见,Linux中的管道是单向通信的,如下图:

注意: 上图的管道是没有名字的,称为匿名管道,是管道文件的其中一种,因为是子进程继承父进程了管道,不需要名字

也可以这样表示,读写过程已在下图标注:

这样父进程的写端和子进程的读端都指向同一个管道文件的缓冲区

Linux的 /usr/include/unistd.h的注释也是这样说的:

/* Create a one-way communication channel (pipe).
   If successful, two file descriptors are stored in PIPEDES;
   bytes written on PIPEDES[1] can be read from PIPEDES[0].
   Returns 0 if successful, -1 if not.  */
extern int pipe (int __pipedes[2]) __THROW __wur;

#ifdef __USE_GNU
/* Same as pipe but apply flags passed in FLAGS to the new file
   descriptors.  */
extern int pipe2 (int __pipedes[2], int __flags) __THROW __wur;
#endif

管道是单向通讯通道(a one-way communication channel),如果父子进程需要双向通信可以使用两个管道

结论: 两个进程之间必须要有血缘关系才能使用管道进行通信

例如上述例子的进程A创建了进程B,A和B是父子关系,能进行管道通信,如果进程B创建了进程C,不仅B和C之间可以使用管道通信,A和C之间也可以,因为管道文件是一路继承下来的,所有有血缘关系的进程是共享同一个管道文件的

同理,如果进程A的父进程是进程D,此时血缘关系为血缘关系是D-->A-->B-->C(D-->A中D为父进程,A为子进程),那么A、B、C、D打开的是同一个管道文件,管道文件是共享的

        →如果D创建出来A之后,D关闭读端,保留写端,而A保留读端,关闭写端,那么A创建的B和C都可以从管道中读取东西

进程E如果打开了管道文件,而且进程E有两个子进程D和F,那么这三个进程共享同一个管道文件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhangcoder

赠人玫瑰手有余香,感谢支持~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值