你了解system V的ipc底层如何设计的吗?消息队列互相通信的原理是什么呢?是否经常将信号量和信号混淆呢?——问题详解

news2024/9/22 16:08:41

        前言:本节主要讲解消息队列, 信号量的相关知识。 ——博主主要是以能够理解为目的进行讲解, 所以对于接口的使用或者底层原理很少涉及。 主要的讲解思路就是先讨论消息队列的原理, 提一下接口。 然后讲解ipc的设计——这个设计一些底层原理。 最后就是会让友友们理解一下信号量的相关概念。 

        ps:本届内容设计共享内存, 友友们务必学完共享内存后再来观看哦

目录

消息队列的原理

消息队列的接口

msgget

msgctl

ipc在内核里面的设计

信号量

储备知识

理解信号量

信号量接口

 信号量与进程间通信


消息队列的原理

        本节讲述的消息队列是system V标准的。 我们知道的是, 我们的进程, 因为是用户写的代码, 有操作系统的task_struct以及地址空间, 所以它是在用户和操作系统之间的。 而消息队列,是属于内核的。那么这里就是下图的样子:

        想要让上面两个进程a 和 b进行通信, 前提是,必须先让两个进程看到同一份资源!!!(这一句话是通信领域的结论, 地位就相当于管理层面的先描述再组织)

        现在我们要知道的是这个同一份资源, 有可能是文件缓冲区, 有可能是内存块。 所以, 公共资源种类的不同, 决定了通信方式的不同。 其中,以文件的形式给我们一个文件缓冲区, 这就叫做管道。是否允许匿名,这个就叫做匿名管道给我们一个内存块, 映射到地址空间, 这个就叫做共享内存;也可以给我一个队列, 这个队列就是我们在数据结构里面学到的那个队列, 也就是消息队列。 现在来谈这个队列。

        这个队列, 如果我们想要让两个进程a,b能够进行通信。 就必须让两个进程看到同一个队列。  当两个不同进程看到同一个队列之后,我们知道, 两个进程想要通信, 就要能够将数据块入队列当中。 但是问题来了, 两个进程怎么保证, 能够拿到对方的数据块呢?这个是因为允许不同的进程能够向内核中发送不同类型的数据块。 这些数据块假如a进程的是a类型的数据块, b进程的假如是b类型的数据块。 那么a进程要获取b进程的通信, 就可以拿到消息队列里面的b类型的数据块。 所以, 综上, 消息队列是什么?——消息队列的原理就是:a进程, b进程以数据块的形式发送数据进行通信!!!

消息队列的接口

        注:消息队列的接口不作为博主讲解的重点, 如果想要学习相关接口的友友自行去查阅相关资料哦。

msgget

        里面的返回值是int类型。 成功返回,会返回一个消息队列标识符。 失败返回-1。——无需指明大小, 只需直接创建即可。  第二个参数是就是那个key(使用路径和项目id确定的唯一key), 第三个参数就是创建消息队列的使用方式。

msgctl

这个函数是用来控制消息队列,这里不讲解这个接口的用法, 我们用这个接口和信号量的控制接口, 以及共享内存的控制接口来进行对比, 下面是信号量和共享内存的控制接口:

        由上面三个函数我们就可以观察到, 我们的进程间通信, 是被精心设计过的, 无论是我们的共享内存, 还是我们的消息队列, 亦或者我们的信号量。 他在内核层面上都有我们的semid_ds这样的结构, XXXid_ds结构体。 并且, 他们三个的XXXid_ds结构体里面第一个成员一定是struct_ipc_perm XXX_perm。并且, 这种结构体里面包含的字段全部都是一样的, 都是key还有各种权限。 

ipc在内核里面的设计

        在操作系统中, 所有的IPC资源, 都是整合进操作系统的IPC模块中的在以后的过程中, 我们管理共享内存, 管理消息队列, 管理信号量, 其实本质上就是管理下面这三种结构体:

        那么我们如何把这些数据结构管理起来呢?在操作系统当中, 其实也是用数组管理的:

        如果我们今天创建一个共享内存, 那么操作系统当中, 就要为我们创建shmid_fd这样的结构。 那么我们就要把shmid_ds的第一个字段填到数组的零号下标里面。 

        那么, 如果后来我们又创建了一个消息队列. 没关系, 虽然我们的struct_ds的类型不一样, 但是我们的结构体的第一个字段和上面的字段是一样的。 所以, 我们就把这个第一个字段的地址, 填到数组的二号下标里面。 同样的信号量. 所以, 从此往后, 我们要管理操作系统内部的不同的IPC资源要如何如何进行管理呢?——答案是先描述:对于不同的资源, 我们可以使用不同的描述的方式。 再组织, 我们可以对不同的资源进行增删查改, 最后转化为对该数组进行增删查改。

        未来呢, 我们一个进程如果想要申请一个共享内存, 那么申请共享内存, 就会给我们一个key, 然后操作系统就会遍历这个数组, 找到每一个ipc资源, 比较key, 确认是否已经创建过共享内存。 ——其中的, 这里的每一个ipc_perm结构, 他对应的数组的下标, 就是我们所说的shmid或者XXXid!!!

        那么, 我们如何利用这个数组元素访问XXXid_ds里面的其他字段呢?——只需要强转一下我们的类型——((shmid_ds*)array[])->某个字段——但是问题来了, 它是怎么知道要强转成为哪一个类型呢?ipc_perm是操作系统在用户层做的让我们用户看到的属性, 但是内核层面上叫做kern_ipc_perm, 这个东西里面有mod, delete等等。 其中mod里面就有一个选项, 这个选项是一种类型标志位, 让代码去区分自己是哪一种ipc资源。——也就是说, 操作系统能够区分指针指向的对象的类型(事实上, 我们也可以, 就是在对象里面添加一个标志位即可)。

        其中, 这个数组其实就是实现了多态!!!而其中的ipc_perm就是基类, 其他的字段就是子类。 指针数组指向了这些ipc_perm, 就是实现了多态。 

        这个数组是操作系统层面创建的数组, 和进程没有关系, 所以这个下标和文件描述符并不类似。 shmid这个下标是线性递增的。 就比如我们今天申请了这个资源, 释放掉, 下次再申请, 这个资源的数字会变大。 ——不会因为释放而减小。 但是不需要担心越界, 因为他是会发生回绕的, 但是我们的这个数组的下标永远是从零开始的, 只是在使用的时候会有一个起始计数器, 我们最后的shmid就会使用这个起始计数器加上我们的小标进行计算。 

信号量

储备知识

理解信号量, 我们需要先有一些储备知识, 先回忆一下这一张图

        这张图, 当a进程想要读取, b进程想要写入。 他们会不会发生错乱呢? ——比如我们今天a进程想要给b进程发送100字节, 并且这100个字节是应该整体被读取的。 但是如果这个时候a刚写了50个字节, b进程就过来读了, 那么b进程就可能把这50个字节先读走。 ——这就造成了一种情况, b进程只读取了a进程想要发给他的信息的一部分, 这就是数据不一致问题

  •         那么a和b看到的同一份资源, 我们叫做共享资源, 因为是共享的, 如果不加保护, 可能会导致数据不一致问题。
  •         那么我们要如何保护呢?就要用到一种加锁的方式。通过加锁保证一种工作状态。——这就是互斥访问。即:任何时刻只允许一个执行流, 访问共享资源。 ——这种概念称之为互斥, 就比如我们去ATM机取钱, 我们一个人去取钱了, 其他人就不能够进去了。
  •         共享资源是任何时刻只允许一个执行流访问(访问就是指的执行访问代码)的资源, 我们就叫做临界资源 管道就是临界资源, 但是临界资源一般是内存空间 
  •         假设我们写了100行代码, 其中的5 ~ 10行在访问临界资源。 所以, 这些访问临界资源的代码, 就叫做临界区!!!

        通过上面的讲解, 我们就能解释一个现象: 假如有五个进程在死循环的打印hello world。 为什么显示器上面的消息是:错乱的, 混乱的并且和命令行混在一起的。——这是因为我们在向显示器打印数据的时候, 是先向显示器文件的缓冲区中打印数据, 然后再将显示器文件的缓冲区的内容刷到显示器上面打印出来。 这其中, 我们的进程, 所有的进程都在向我们的显示器文件的缓冲区中打印数据, 很显然他们都能够看到我们的显示器文件, 那么我们的显示器文件就是一种共享资源又因为我们的显示器文件没有保护机制, 那么多进程一起打印的时候, 就会发生数据不一致问题 想要进行保护, 就要通过加锁互斥访问的形式变成临界资源!!!

理解信号量

        那么如何理解信号量?信号量的本质, 其实就是一把计数器!类似于一个整数的的计数器:比如 int cnt——信号量是用来秒数临界资源中,资源数量的多少!!!

       临界资源中的资源数量是什么意思?——就比如我们去看电影, 电影院有100个座位, 那么对应着就一定有100张电影票。当我们去看电影的时候, 如果我们买了票了, 但是没有去。那么此时电影院其实就是这么一个大的临界资源, 我们每一人对应每一个进程, 每一张票就代表着我们能够访问电影院里面的一个座位。 那么请问我们去看电影, 是我们做到这个座位上票是我的? 还是我们将票买到了, 这个座位是我的?是不是我们将票买到了, 这个座位就是我的了呢?——所以, 买票的本质, 是对资源的预定机制!!!对于电影院, 需要维护一个票数的计数器, 每卖出一张票, 我们的计数器就要减一, 放映厅里面的资源就会少一个!!!票数计数器到零之后, 资源已经被申请完毕了!!! ——就如同下图:

        这一块临界资源按照某个单位划分为一块一块的。 假如今天有一个执行流访问临界资源, 那么这个执行流可能只访问临界资源的一部分。 

        对于我们的临界资源, 如果今天有一个执行流想要访问临界资源, 那么他可能只访问其中的一部分。 我们对应的系统在临界资源的使用上, 我们可以让整个临界资源只被这一个执行流访问。但是这个执行流只访问临界资源其中的一块, 所以系统就可以只把这一块分配给这个执行流。 当又来了一个执行流的时候, 这个执行流也只是访问一块资源, 那么这个时候就没必要将整块资源全部锁住, 只需要将刚刚第一个执行流的资源锁住, 将那一块资源给第二个执行流就行了。 所以我们把临界资源拆成一小份, 前提是能够被拆成一小份, 并且每个执行流只使用一小份资源, 这种情况下, 我们就可以使得这两个执行流同时都进来访问。 这种情况可以提高多执行流访问临界资源的并发度, 一定程度上提高效率。 

        在这种情况下, 我们最怕的是, 多个执行流访问同一个资源, n个资源但是有n+个执行流。 排除我们分配资源的bug问题, 为了防止出现n个资源有n+个执行流的问题, 我们就会引入一个计数器:

int cnt = 15;(假如有15个资源)

int number = cnt--; (每申请一个资源就渐渐)

cnt <= 0; (小于等于0的时候资源没了, 不再分配资源, 直到有资源空出来了!)

那么, 经过上面的解释, 我们可以得出结论——

  • 1、我只要申请计数器成功了, 就表示我具有访问资源的权限了!
  • 2、申请了计数器资源, 就代表我当前要访问我的资源了吗?——并没有, 但是我们只要申请成功了, 并且没有释放, 那么这里面的资源就一定要有一个供我使用。——所以申请计数器资源的本质就是对资源的一种, 预定机制。
  • 3、如果多个执行流全部都申请, 那么计数器减为零, 就不能申请了。——这个就说明计数器可以有效保证进入共享资源的执行流的数量。
  • 4、所以每一个执行流想访问共享资源的时候, 不是直接访问, 而是先申请计数器资源, 就如同我们看电影需要先买票。

        那么, 上面提这么多还是没有提到信号量, 但是其实我们一直都在说信号量——因为这个“计数器”就被成为信号量!!!

        现在再来看这个问题, 如果电影放映厅里面只有一个座位呢?——是不是也就意味着, 我们只需要一个值为一的计数器。——那么如果有人想买票, 就需要先抢这个座位, 并且所有人中, 只有一个人能够抢到, 也就只有一个人能看电影。 在放电影期间, 只有一个执行流在访问临界资源。 ——这个概念叫做互斥。 我们把只能为一为零两态的计数器叫做二元信号量, 本质就是一个锁!!!

        那么, 我们要访问临界资源, 先要申请计数器资源, 所以信号量计数器资源的本质, 不也就是共享资源吗?——所以, 刚刚我们举得例子当中的int本身就是一个临界资源, 而这个临界资源是用来保护我们的要访问的临界资源的, 但是我们的信号量在保证别人的前提下, 是不是也要保护自己是安全的?(因为数据不一致可能发生在计数器上面)——那么请问这个计数器临界资源减减的时候是不是安全的呢?——答案是不是的:cnt--在c语言上, 是一条语句, 但是变成汇编语言, 那么至少会变成多条汇编语句,我们知道cnt是保存在内存中的, 而cnt在--的时候要转移到cdu中进行计算, 那么减减操作就是:

        1、首先cnt变量的内存从内存转移到cpu中的寄存器中。 2、在cpu内进行计算。 3、将计算结果写回cnt变量的内存位置。

        但是这里有一个问题:就是进程在运行的时候, 可以随意的切换, 有可能在我们的三步执行过程之间的某一个邻接点的时候,这个进程就被切走了!!切走的时候, 多执行流都访问这个变量时, 就有可能让cnt减出问题, 比如多减了一下, 或者少减了一下。 

        所以, 我们的cnt不能随意地减减, 所以这里就有了一个概念——申请计数器, 本质是对计数器减减, 这个操作在信号量当中专门封装了一个方法, 就好比这个信号量类里面有一个计数器成员, 申请的时候就有一个成员方法——申请信号量, 对信号量做减减, 这个操作就可以成为p操作。 ——释放资源, 就好比释放信号量, 本质是对计数器进行++操作。 并且这个操作表示归还资源, 因为加加资源变多了, 就表示归还资源。 归还资源, 其他进程就可以申请了, 这个操作, 就叫做v操作。 ——信号量的申请和释放, 就被我们叫做pv操作。 ——其中, 这些操作, 在汇编层面上就会将我们的语句c语句变成多条汇编语句, 所以多执行流调度的时候, 一定会已出现多执行流混合交叉的问题, 就可能这个变量出现问题。 ——所以, 这里就需要我们我们申请和释放的这个pv操作必须是原子的。 ——什么是原子的? 在技术层面上来说, 如果一条语句转化为汇编语句, 只有一条汇编语句, 那么就是原子的。 

        所以, 通过以上的论述, 我们就能总结出一个结论。——就是对于信号量来说, 信号量本质就是一把计数器, 对于计数器匹配的操作, 就叫pv操作, 这个pv操作是原子的。 所有的执行流申请资源, 就必须先申请信号量资源, 得到信号量之后, 才能访问临界资源。 如果信号量的值为1/0两态的, 我们就称为二元信号量, 就是互斥。 其中, 我们申请信号量的本质, 就是对临界资源的预定机制!!!

信号量接口

        信号量接口博主没有具体学习过, 这里不讲解具体, 只是提一下

        其中第二个参数是申请多个信号量, 多个信号量和信号量是几, 是不一样的概念。 

 

        上面的参数中有一个结构体叫做sembuf, 这个结构体如下:

 信号量与进程间通信

        信号量为什么是进程间通信的一种?

  •         1、我们的通信, 不仅仅是传送数据, 通信也在于双方之间的互相协同。 
  •         2、协同虽然不是以传送数据为目的。 但是他以时间通知为目的。 它的本质就是在传递信息, 只不过不是传统的hello world、hello linux这样的数据了。 ——就比如我们上课, 以及我们吃饭, 假如小王和校长, 小张吃饭的时候喜欢和小王吃, 所以小王吃饭的时候就会给小张打电话。 小王喜欢逃课, 小张在上课的时候, 如果有签到, 就给小王打电话。 这两个人互相通知, 这个互相就叫做协同;这个通知, 就叫做信号。 这个通知是不是通信?是的!!!——所以,信号量的本质, 其实也是通信。

——————以上就是本节全部内容哦, 如果对友友们有帮助的话可以关注博主, 方便学习更多知识哦!!!同时对于本节内容, 博主整理了笔记图片, 友友们可以保存方便查阅哦:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2155433.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Godot4.3】点数据简易表示法和Points2D

概述 在构造多点路径时我们会用到PackedVector2Array&#xff0c;并使用Vector2()来构造点。在手动创建多点数据时&#xff0c;这种写法其实很难看&#xff0c;有大量重复的Vector2()&#xff0c;比如下面这样&#xff1a; var points [Vector2(100,100),Vector2(200,200),V…

240922-MacOS终端访问硬盘

A. 最终效果 B. 操作步骤 在macOS中&#xff0c;可以通过命令行使用Terminal访问硬盘的不同位置。你可以按照以下步骤操作&#xff1a; 打开终端&#xff08;Terminal&#xff09;&#xff1a; 在应用程序中打开终端&#xff0c;或者使用 Spotlight 搜索“Terminal”来启动。 …

WebLogic 靶场攻略

后台弱⼝令GetShell 步骤一&#xff1a;环境部署 cd vulhub-master/weblogic/weak_password docker-compose up -d docker ps 步骤二&#xff1a;漏洞复现 默认账号密码&#xff1a;weblogic/Oracle123 步骤二&#xff1a;进行登录 http://192.168.10.190:7001/console/…

thinkphp8 从入门到放弃(后面会完善用到哪里写到哪)

thinkphp8 从入门到放弃 引言 thinkphp* 大道至简一、 thinkphp8 安装安装Composerthinkphp 安装命令(tp-项目名称)多应用安装&#xff08;一个项目不会只有一个应用&#xff09;安装完文件目录如下本地部署配置伪静态好了项目可以run 二、架构服务&#xff08;Service&#xf…

【C++ Primer Plus习题】17.2

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: #include <iostream> #include <fstream> using namesp…

【树莓派】通过HTTP服务器实时传输视频流

前言一、树莓派端的代码二、网页端网址展示总结 前言 树莓派3B的通过HTTP服务器实时传输MJPEG视频流 一、树莓派端的代码 注意在先安装完库 pip install Flask picameraFlask 是一个用 Python 编写的轻量级 web 框架。它主要用于构建 web 应用和 web 服务。Flask 提供了许多…

【无标题】HG6201M路由的超级管理密码获取

这里写自定义目录标题 1、开启telnet http://192.168.1.1/cgi-bin/telnetenable.cgi?telnetenable1&keyXXXXX 注意&#xff1a;此处的XXXXX为路由背面标签的MAC地址&#xff0c;去掉“-”&#xff0c;且大写。 成功后会显示&#xff1a;telnet开启 2、登录telnet 此处采…

C++异常(基本知识)、C++模板类(基本知识)

什么是异常和异常处理&#xff1f; 异常就是程序运行过程中出现的问题。“异常”问题并不经常出现&#xff08;按道理来说我们写好的正常的程序是不应该频繁出现异常的&#xff09;&#xff1b;异常处理技术使得我们可以写程序解决出现的异常问题。很多情况下&#xff0c;处理异…

有女朋友后,怎么养成贤内助?为自己找个好伴侣,为孩子找个好妈妈,为母亲找个好儿媳

有女朋友后&#xff0c;怎么养成贤内助&#xff1f;为自己找个好伴侣&#xff0c;为孩子找个好妈妈&#xff0c;为母亲找个好儿媳 时代背景女生有点作怎么办&#xff1f;大商家族的爱情观 时代背景 一块钱的东西&#xff0c;赋予俩块钱的意义&#xff0c;三块钱卖出去。 用商…

【垃圾识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目选题+TensorFlow+图像识别

一、介绍 垃圾识别分类系统。本系统采用Python作为主要编程语言&#xff0c;通过收集了5种常见的垃圾数据集&#xff08;‘塑料’, ‘玻璃’, ‘纸张’, ‘纸板’, ‘金属’&#xff09;&#xff0c;然后基于TensorFlow搭建卷积神经网络算法模型&#xff0c;通过对图像数据集进…

vscode -ssh免密登陆

1.生成ssh使用的公钥/密钥对 请从客户端上的 PowerShell 或 cmd 提示符运行以下命令&#xff0c;具体使用方法详细见&#xff1a;微软官方 ssh-keygen -t rsa 一路回车&#xff0c;秘钥会生成到C:\Users\name\.ssh 2.将私钥配置到vscode的ssh中&#xff1a; 3.将本地公钥配置…

【计算机网络篇】数据链路层 功能|组帧|流量控制与可靠传输机制

&#x1f9f8;安清h&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;【计算机网络】 &#x1f6a6;作者简介&#xff1a;一个有趣爱睡觉的intp&#xff0c;期待和更多人分享自己所学知识的真诚大学生。 系列文章目录 【计算机网络篇】计算机网络概述 【计算机网络篇…

最新免费域名申请

在互联网时代&#xff0c;每个码农都想拥有一个免费的域名&#xff0c;方便开发调试&#xff0c;也可用作自己网站等。如何申请一个免费的域名&#xff0c;时间上先错过了freenom&#xff0c;后面又错过nic.eu.org申请(现在申请时间长且很难通过)&#xff0c;直到最近又有免费的…

[杂谈-黑神话:悟空] 中国3A游戏的崛起之路:挑战与机遇并存

[杂谈-黑神话:悟空] 中国3A游戏的崛起之路&#xff1a;挑战与机遇并存 《黑神话&#xff1a;悟空》的出现&#xff0c;让我们看到了中国3A游戏的希望和未来。对于中国游戏产业的从业者和爱好者来说&#xff0c;这是一个值得关注和期待的领域。 在游戏产业蓬勃发展的今天&#…

QT打包--windeployqt执行,运行程序提示缺少库

执行windeployqt.exe D:\Data\code\QtCode\Release\RegularExp\RegularExp.exe 生成相应的dll动态库 执行RegularExp.exe&#xff0c;出错&#xff1a;“由于找不到libgcc_s_seh-1.dll…” 找到安装的qt对应的libgcc_s_seh-1.dll拷贝到RegularExp.exe同级目录下&#xff0c; 执…

力扣题解2374

大家好&#xff0c;欢迎来到无限大的频道。 今日继续给大家带来力扣题解。 题目描述&#xff08;中等&#xff09;&#xff1a; 边积分最高的节点 给你一个有向图&#xff0c;图中有 n 个节点&#xff0c;节点编号从 0 到 n - 1 &#xff0c;其中每个节点都 恰有一条 出边。…

[大语言模型-论文精读] 以《黑神话:悟空》为研究案例探讨VLMs能否玩动作角色扮演游戏?

1. 论文简介 论文《Can VLMs Play Action Role-Playing Games? Take Black Myth Wukong as a Study Case》是阿里巴巴集团的Peng Chen、Pi Bu、Jun Song和Yuan Gao&#xff0c;在2024.09.19提交到arXiv上的研究论文。 论文: https://arxiv.org/abs/2409.12889代码和数据: h…

2024年9月19日

1.四个属性: 连接数据库 四个属性 操作数据库重要的对象 操作sql语句的对象 预编译 结果集 对象的属性和数据表的子弹 映射准备这条sql语句 文件:核心配置文件 2.创建 build success 做工具类 如果客户名不为空,否则客户名为空

Python中的IPython:交互式的Python shell

你是否曾经想要一个能让你与Python代码实时交互的环境&#xff1f;一个可以即时执行代码、查看结果&#xff0c;甚至可以在运行过程中修改变量的工具&#xff1f;如果是&#xff0c;那么IPython就是为你量身打造的利器&#xff01;在这篇文章中&#xff0c;我们将深入探讨IPyth…

CDVAE项目环境配置

CDVAE环境配置 1. 系统环境2. 设置环境变量3. 配置环境变量4. 安装CDVAE虚拟环境5. 资料下载 1. 系统环境 系统环境&#xff1a;Ubuntu22.04GeForce RTX 3090cuda12.6&#xff08;cuda版本11.1以上均适用&#xff09;。 2. 设置环境变量 先按照CDVAE中描述的设置环境变量。 …