进程间通信

进程通信是什么?

两个或者多个进程实现数据层面的交互,通信是有成本的。

为什么要有进程间通信:

在现实中进程之间一定会存在:

基本数据交互

发送命令

某种协同

通知

所以需要进程间通信。

如何实现进程间通信?

  1. 进程间通信的本质:必须让不同的进程看到用一份“资源”

  2. “资源”:特定形式的内存空间

  3. “资源”谁提供?操作系统,如果由进程之间提供会破坏进程之间的独立性

  4. 进程访问这个空间,本质就是访问操作系统

  5. 基于文件级别的通讯方式 ———— 管道

一般的操作系统,会有一个独立的通信模块 —— 隶属于文件系统 —— IPC通信模块定制标准 —— 进程通信是有标准的 ——system V && posix

管道:

管道原理:

用管道原理进行通信的一定是具有血缘关系的进程:父子,兄弟,爷孙

对应父子进程都具有独立的task_struct对应有独立的struct_file,但对应在数组中相同序号指向同一批文件:对应的stdin,stdout,stderrno都是公用的:

进程间通信_第1张图片

如果父子进程想要创建自己的管道文件,对应一定是存在内存中而非磁盘中。

如何建立通信信道?

对应父子进程对同一个文件进行读写,对应就创建了通信。

对于父子进程来说,读写时文件的inode相同,同一个文件缓冲区

不同的fd执行不同的操作,在不同的进程中存储。

如果父子进程同时进行写入读取,那么对应的管道文件不是就乱了吗?

所以我们最好规定,一个进程读,一个进程写。

这也就注定了管道只能进行单向通信:

进程间通信_第2张图片

所以在struct_file中的引用计数就会起作用,对应有不同的fd指向它,引用计数+1。

可以看到上面的内存文件(管道文件)没有名字,也对应叫匿名管道。

2.对应创建匿名管道文件的接口:pipe

进程间通信_第3张图片

对应的头文件和接口。

对应参数中数组的两个元素一定是读方法和写方法。

注意接口的使用:

进程间通信_第4张图片

下面写代码来验证:

进程间通信_第5张图片

对应的运行结果:

可以看到在012后面创建了两个文件:记住下标为0的是读,为1的是写。

接下来创建子进程让子进程写,父进程读:

进程间通信_第6张图片

先看看字符串是否构建成功:

进程间通信_第7张图片

可以看到构建成功了。

再向父进程写入:

进程间通信_第8张图片

进程间通信_第9张图片

对应代码的执行结果:

进程间通信_第10张图片

可以看到执行起来了。

下面来验证管道中的四种情况:

1.读写端正常,管道如果为空,读端就阻塞

2.读写端正常,管道如果被写满,写段就要阻塞

写满多大呢?修改代码来验证:

子进程一次写入一个字节:

进程间通信_第11张图片

父进程不读:

进程间通信_第12张图片

对应代码结果:

进程间通信_第13张图片

最后一个值是6535,对应管道缓冲区的大小位64KB。

3.读端正常,写段关闭,读端会读到0,表明文件结尾,不会阻塞。

0:

修改代码:

进程间通信_第14张图片

对应文件读完了。

进程间通信_第15张图片

可以看到后面n变为0,对应文件为空,所以我们要在文件读取是在加一个判断:

进程间通信_第16张图片

对应的结果:

进程间通信_第17张图片

对应代码更完善。

4.写段写入正常,读端关闭,操作系统通过信号干掉正在写入的进程。

代码:

对应获取子进程的退出码和退出信号:

进程间通信_第18张图片

让父进程读5秒就停,子进程继续写对应结果:

进程间通信_第19张图片

进程间通信_第20张图片

对应的是pipe错误退出。

对应让子进程写入,父进程读取,为了方便看到退出信号,对应就是操作系统终止了进程

管道的特征:

1.只有具有血缘关系的进程才能进行管道通信

2.管道只能进行单向通行

3.父子进程是会进行协同的:解决临界资源竞争问题(多线程)

4.管道是面向字节流的(网络)

5.管道是基于文件的,而文件的生命周期是随进程的。

细节:

1.对应管道通信其实就是对应的缓冲区拷贝:

写入是从用户到内存,读取是从内存到用户

2.管道的固定大小是可以改变的

3.snprintf的使用:

管道的使用场景:

1.自制shell中支持管道(文件重定向),这里不实现了,主要实现下一个

2.用管道实现一个简易版本的进程池:

进程池的概念:

对应让父进程写,子进程读,都读相同大小的内容,这样格式一样

对应可以实现一个简单的游戏日志:

进程间通信_第21张图片

将这些子进程管理起来,也是先描述,再组织。

代码对应对子进程的描述:

进程间通信_第22张图片

子进程初始化:

进程间通信_第23张图片

对应先对初始化好的子进程打印一下:

main函数:

进程间通信_第24张图片

对应代码的执行结果:

进程间通信_第25张图片

接下来让子进程执行一点有意义的东西:

注意对应输入输出函数对应的规范:

输入:const  &

输出:*

输入输出: &

定义一个头文件来存放日志:

进程间通信_第26张图片

main函数中初始化这个日志函数指针数组:

进程间通信_第27张图片

控制子进程:

进程间通信_第28张图片

用菜单来对应要实现的功能.

子进程运行的时候也要修改:

执行对应的函数。

对应退出函数,关闭文件描述符,等待子进程:

进程间通信_第29张图片

对应的结果:

进程间通信_第30张图片

对应没有问题。

初始化bug:

命名管道:

上面的匿名管道都是具有血缘关系之间的进程的通行,那么如果没有血缘关系呢?

对应就是命名管道。

对命名管道的理解:

命名管道本质只是管道的一个标识,它不存在这块管道缓冲区还是存在的。

如果两个不同的进程,打开同一个文件的时候,在内核中,操作系统会打开几个文件?

同一个文件。

对应的文件都是路径+文件名

对应的编码:

进程间通信_第31张图片

进程间通信_第32张图片

对应的结果:

实现一个自己的日志:

进程间通信_第33张图片

共享内存:

原理:

主要分为三步:

1.申请物理内存。

2.挂接到进程地址空间中。

3.返回起始虚拟地址。

进程间通信_第34张图片

直接代码对应的系统接口:

shmget:

进程间通信_第35张图片

对应返回值和参数的含义:

进程间通信_第36张图片

创建K:

ftok:

进程间通信_第37张图片

对key的理解:

1.key对应是一个int,至于数字是多少不重要,重要的是它的唯一性,能够让不同的进程执行唯一标识。

2.通过key创建共享进程

3.key存在共享内存的描述对象中

4.上面创建key的函数的两个参数都是由用户自己约定的,

5.key 路径 唯一性

shmat:

进程间通信_第38张图片

将共享内存链接到对应的共享内存。

shmdt:

脱离连接。

shmctl:

用于控制共享内存的这里是把它删掉。

3.共享内存的特性:

1.共享内存没有同步互斥之类的保护机制

2.共享内存是所用进程中通信中,速度最快的。(拷贝少)

3.共享内存内部的数据由用户自己来维护:shmid

共享内存的本质:

1.开辟一块物理空间,让多个进程映射到同一块物理内存到自己的地址空间进行访问。

2.共享内存的删除不是直接删除,而是删除进程与物理地址的映射关系,到映射链数为0的时候,才为真正的删除。

3.共享内存的生命周期是随内核的(本质是一块内存)。

只要不被删除就一直存在系统中。

消息队列信号量:

1.将共享内存的接口与消息队列的接口进行比较:

对应的get:

进程间通信_第39张图片

接口基本相似。

都有key所以都要用到ftok接口。

对应的ctrl接口:

接口的参数都相同,对应的最后一个参数需要自己创建结构体,自己传指针。

对应描述的结构体:

进程间通信_第40张图片

从这里可以看到c语言玩继承的影子。

消息队列的原理:

就是让不同的进程看到同一个队列:

A,B进程的发送方式都是以数据块的形式发送:

进程间通信_第41张图片

发送的数据块必须是带类型的,不然就不知道是谁发送的。

进程间通信在内核中的数据结构设计:

所有的IPC资源都是存在系统的IPC模块中的:

其中不同的通信方法都是对系统内存中一块空间的数据结构的继承:

进程间通信_第42张图片

对应的key都是面向系统内核的,id都是面向用户的。对应不同的通信方式都是存在同一个指针数组的。

学习信号量的储备知识:

管道通信的不足:

1.AB看到同一份资源,如果不加保护,会导致数据不一致问题。

2.加锁:互斥访问,对应任何时候,只允许一个执行流访问资源。

3.共享的,任何时候只允许一个执行流访问的资源叫临界资源。

解释一个现象:

多进程,多线程并发打印会导致:

显示器上的消息:

  • 错乱

  • 与命令行混在一起

信号量:

本质:

是一个计数器,用来描述临界资源中资源数量的多少。

原理和概念:

1.申请计数器成功了,就表示具有访问资源的权限了。

2.申请计数器资源是对资源的预订机制

3.计数器可以有效保证进入共享资源的执行流的数量

4.所以每个执行流访问共享资源不需进过计数器,不能直接访问

上面对应的“计数器”就是信号量

那么如果信号量对应的共享资源只有一个呢?

意味着只能有一个执行流来访问这个临界资源。

这样的信号量叫二元信号量,其实就是一个锁。

信号量的pv操作:

p操作:申请信号量,本质是对计数器的--

v操作:释放信号量,本质上是对信号量的++

两个概念:

1.多个信号量:对应着多种不同的共享资源的管理

2.信号量是几:对应共享资源在信号量中的几号位

信号量凭什么也是通信的一种?

  • 通信不仅仅是通信数据,互相协同也是

  • 要协同,本质也是通信,信号量要被所用要通信的进程看到。

信号量的接口太复杂,这里不看了。

你可能感兴趣的:(服务器,网络,linux)