9.进程间通信

1.简介

为啥要有进程间通信?

如果未来进程之间要协同呢?一个进程要把自己的数据交给另一个进程!进程是具有独立性的,所以把一个进程的数据交给另一个进程----基本不可能!必须通信起来,就必须要有另一个人的参与:操作系统!

进程间通信的前提(也是本质):先要让不同的进程,看到同一份资源!!

cpp 复制代码
1. 进程间通信⽬的
• 数据传输:⼀个进程需要将它的数据发送给另⼀个进程
• 资源共享:多个进程之间共享同样的资源。
• 通知事件:⼀个进程需要向另⼀个或⼀组进程发送消息,通知它(它们)发⽣了某种事件(如进
程终⽌时要通知⽗进程)。
• 进程控制:有些进程希望完全控制另⼀个进程的执⾏(如Debug进程),此时控制进程希望能够
拦截另⼀个进程的所有陷⼊和异常,并能够及时知道它的状态改变。

2. 进程间通信发展

• 管道(基于文件的通信方法)
• System V进程间通信(单独设计通信模块)
• POSIX进程间通信(网络间进程通信)

3. 进程间通信分类
管道
• 匿名管道pipe
• 命名管道

System V IPC
• System V 消息队列
• System V 共享内存
• System V 信号量

POSIX IPC
• 消息队列
• 共享内存
• 信号量
• 互斥量
• 条件变量
• 读写锁

2.管道

vscode中ctrl + · (esc下面的)即可调出linux命令行界面

我们把从一个进程连接到另一个进程的一个数据流称为一个 "管道";

管道是一个基于文件系统的内存级的单向通信的文件,主要用来进行进程间通信(IPC)的

3.匿名管道(没有文件路径,没有文件名)

cpp 复制代码
#include <unistd.h>
功能:创建⼀⽆名管道
原型
int pipe(int fd[2]);
参数
fd:⽂件描述符数组,其中fd[0]表⽰读端, fd[1]表⽰写端
返回值:成功返回0,失败返回错误代码
cpp 复制代码
管道的4种情况和5大特性

匿名管道特性:
1.常用与具有血缘关系的进程,进行IPC,常用于父子
2.单向通信
3.管道的生命周期随进程
4.面向字节流-》网络重点
5.管道自带同步机制-》多线程

4种情况:
1.管道里没有数据,读端就会被阻塞(写端不关,写端不写)
2.读端不读,读端也不关(写满了就不再写入了)
3.写端不写,写端关闭:read会读到返回值为0,表示读到文件结尾!
4.读关闭,写正常:OS会自动杀掉写进程!!!(因为OS不会做无效动作)
cpp 复制代码
单次向管道里面写入,写入的字节数小于PIPE_BUF,写入操作就是原子的(不可被分割的,要么不做
,要做就做完,没有第三状态)
Linux 系统:PIPE_BUF的值为 4096 字节 。
如果希望写操作具有原子性,要确保单次写入数据量不超过PIPE_BUF字节

3.1 实例代码

3.2 用 fork 来共享管道原理

3.3 站在文件描述符角度-理解管道

3.4 站在内核角度-管道本质

所以,看待管道,就如同看待文件一样!管道的使用和文件一致--"linux一切皆文件的思想"

3.5 创建进程池处理任务

cpp 复制代码
补充:C++的后缀可以是.cpp,.cc,.cxx
.hpp是用来实现库的,一般是开源库(头源不分离)
cpp 复制代码
轮询或者随机数或者load计数器 --- 负载均衡!!
避免让一个或者个别进程一直都很忙,其他进程一直都很闲

封装一下:

这里有一个引用计数的BUG

创建第二个往后的子进程时虽然每次都消除了无关的rw,但是每次新的子进程都把上一次的w继承下来了,使得每次新建一个子进程,往上的父进程写w的引用计数都会+1,这样导致不能按照正常顺序进行close,引用计数不为0关不掉。所以可以倒着关!

4.命名管道

复制代码
• 管道应⽤的⼀个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
• 如果我们想在不相关的进程之间交换数据,可以使⽤FIFO⽂件来做这项⼯作,它经常被称为命名
管道。
• 命名管道是⼀种特殊类型的⽂件

匿名管道与命名管道的区别
• 匿名管道由pipe函数创建并打开。
• 命名管道由mkfifo函数创建,打开⽤open
• FIFO(命名管道)与pipe(匿名管道)之间唯⼀的区别在它们创建与打开的⽅式不同,⼀但这些
⼯作完成之后,它们具有相同的语义。

命名管道的打开规则

• 如果当前打开操作是为读⽽打开FIFO时
◦ O_NONBLOCK disable:阻塞直到有相应进程为写⽽打开该FIFO
◦ O_NONBLOCK enable:⽴刻返回成功
• 如果当前打开操作是为写⽽打开FIFO时
◦ O_NONBLOCK disable:阻塞直到有相应进程为读⽽打开该FIFO
◦ O_NONBLOCK enable:⽴刻返回失败,错误码为ENXIO

总的来说:命名管道主要解决,毫无关系的进程之间,进行文件级进程通信!!!

剩下的特点,匿名和命名管道的特点相同

mkfifo创建命名管道

unlink 通常是一个用于删除文件或解除链接的函数

效果:两端同步

代码实现:

5.system V共享内存

共享内存区是最快的IPC(进程间通信)形式。⼀旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执⾏进⼊内核的系统调⽤来传递彼此的数据

进程结束了,如果没有进行删除共享内存,共享内存(用ipcs查看)资源会一直存在 --- 共享内存的资源,生命周期随内核!! ----- 如果没有显式的删除,即使进程退出了,IPC资源依旧被占用

5.1 相关函数

cpp 复制代码
key vs shmid
1.key: 只有内核使用,用来标识shm的唯一性
2.shmid:给用户用,用来进行访问shm
cpp 复制代码
shmat函数

功能:将共享内存段连接到进程地址空间
原型    //void* 起始虚拟地址
        void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
        shmid: 共享内存的标识符
        shmaddr:指定连接的地址(虚拟地址,固定地址进行挂接)
        shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回⼀个指针,指向共享内存第⼀个节;失败返回-1

说明:
shmaddr为NULL,核⼼⾃动选择⼀个地址
shmaddr不为NULL且shmflg⽆SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会⾃动向下调整为SHMLBA的整数倍。
公式:shmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表⽰连接操作⽤来只读共享内存
cpp 复制代码
shmdt函数

功能:将共享内存段与当前进程脱离
原型
        int shmdt(const void *shmaddr);
参数
        shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段
cpp 复制代码
shmctl函数

功能:⽤于控制共享内存
原型
        int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
        shmid:由shmget返回的共享内存标识码
        cmd:将要采取的动作(有三个可取值)
        buf:指向⼀个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值

IPC_SET:在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值

IPC_RMID:删除共享内存段

5.2 代码实现共享内存通信:

6.system V消息队列(了解)

#消息队列的生命周期也随内核

6.2 相关调用接口

6.3 数据不一致问题

7.system V信号量(了解)

cpp 复制代码
• 多个执⾏流(进程), 能看到的同⼀份公共资源:共享资源
• 被保护起来的资源叫做临界资源
• 保护的⽅式常⻅:互斥与同步
• 任何时刻,只允许⼀个执⾏流访问资源,叫做互斥
• 多个执⾏流,访问临界资源的时候,具有⼀定的顺序性,叫做同步
• 系统中某些资源⼀次只允许⼀个进程使⽤,称这样的资源为临界资源或互斥资源。
• 在进程中涉及到互斥资源的程序段叫临界区。你写的代码=访问临界资源的代码(临界区)+不访问
临界资源的代码(⾮临界区)
• 所谓的对共享资源进⾏保护,本质是对访问共享资源的代码进⾏保护

7.1 理解

信号量:信号灯,本质是一个计数器,用来表明临界资源中,资源数量的多少;所有进程,访问临界资源中的一小块前,就必须先申请信号量;申请信号量的本质是:对资源的预定机制;资源就给你了,等你随时访问,没人和你抢。

借助电影院理解信号量:

买票:买到票了,该座位就是我的了,即使我不来,该座位也要为我预留。

买票本质是对资源的预定机制!-> 想要访问临界资源,都得先买票(即减少相应信号量)

细节1:信号量本身就是共享资源:申请 --,原子性(P操作);sem++,原子性(V操作)

细节2:信号量只有1或者0两态的信号量,叫做二元信号量(就是互斥--共享资源整体使用)

7.2 相关函数

信号量和通信有什么关系?

  1. 先访问信号量P,每个进程都得先看到同一个信号量!!(system V 解决的就是这个问题)

  2. 不是传递数据,才是通信IPC,通知,同步互斥也!

OS内部存在大量的信号量集合->也要对信号量进行管理->先描述,在组织

可以看出,共享内存,消息队列,信号量 -> key区分唯一性! -> OS中,共享内存,消息队列,信号量,被当作了用一种资源!!!****(统称位system V IPC)

8.内核是如何组织管理IPC资源的

相关推荐
谷新龙0015 分钟前
Elasticsearch服务器开发(第2版) - 读书笔记 第二章 索引
服务器·elasticsearch
dessler16 分钟前
RabbitMQ-镜像队列(Mirrored Queues)
linux·运维·rabbitmq
瑾曦20 分钟前
Docker相关命令
linux
发抖吧小喵喵23 分钟前
rpm包直接安装新系统缺少依赖问题处理
linux·运维·服务器
Asuicao1 小时前
最新docker国内镜像源地址大全
运维·docker·容器
xhdll1 小时前
embodied复现所需docker环境配置粗略流程
运维·docker·容器
码农101号1 小时前
Linux中Docker Swarm介绍和使用
linux·spring cloud·docker
Nazi61 小时前
dockerfile基础
linux·运维·docker·容器·云计算
跑不了的你1 小时前
Ubuntu 开启wifi 5G 热点
服务器·5g·ubuntu