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

Linux学习之路 -- systemV进程通信 -- 消息队列和信号量(简单介绍)

一、简介:

        System V进程通信(System V IPC)是一组在Unix和类Unix操作系统中用于进程间通信的机制。这些机制在System V Release 2中首次引入,并在POSIX标准中得到部分采纳。System V IPC主要包括以下几种通信方式:

消息队列(Message Queues):

消息队列允许一个或多个进程写入或读取消息。消息队列可以看作是一个消息链表,每个消息都有一个类型和一个优先级。进程可以向队列中添加消息,也可以从队列中读取消息。
信号量(Semaphores):

信号量是一种用于同步进程的机制。它可以用来控制多个进程对共享资源的访问。System V信号量分为两种:二进制信号量和计数信号量。信号量通过操作原语如P(等待)和V(信号)来工作。
共享内存(Shared Memory):

共享内存允许两个或多个进程共享一段内存区域。这是最快的IPC方式,因为数据不需要在客户和服务器进程间复制,而是直接在内存中共享。共享内存通常与信号量结合使用,以同步对共享内存的访问。

2、消息队列

1、相关原理的简单介绍

进程之间的通信都要让两个进程看到同一块资源,在消息队列这种通信方式下,就是让两个进程在内核当中看到同一个队列。两个进程只需要向队列中写入特定类型的数据,由另外一个进程读取这个类型的数据即可。

2、相关接口介绍

<1>msgget

申请一个消息队列,参数key和shmget的key作用一样,都是标识队列的唯一性,参数msgflg和shmget的参数shmflg是一样的,一般常用的有两种参数 IPC_CREAT和IPC_EXCL(具体作用不赘述),成功返回msgid,失败返回-1,其返回值msgid可以作为用户层面上区分队列的标识。

<2>msgctl

该系统调用用来对消息队列进行控制,msqid为msgget的返回值。cmd参数表示控制命令,常用的为以下三个:

        IPC_STAT:获取消息队列的各种属性。
        IPC_SET:对消息队列的中的属性进行设置。
        IPC_RMID:删除消息队列。(如果在终端上,我们可以使用ipcrm -q + msgid来对消息队列进行删除)。
struct msqid_ds* buf是一个输出型参数,这个结构体和struct shmid_ds几乎完全一样,该结构体中的成员都是消息队列的一些属性进行描述。

其中在结构体struct ipc_perm中可以获取key值的大小。

<3>msgsnd&&msgrcv

msgsnd表示向消息队列中发送消息,msgrcv表示向消息队列中接受信息。

第一个参数就是msgget的返回值,我们直接传入即可。在第二个参数上,两个系统调用的传参稍稍有点差异,首先我们都需要定义一个struct msgbuf;

在这个结构体中,第一个成员是消息的类型,第二个成员是具体的消息数据。在msgsnd系统调用时我们需要将该结构体内的数据手动补充完成后,再传入数据,在msgrcv中不用,只要在定义好后,传入指针即可。第三个参数就是结构体中mtext数据的大小。而msgflg这个参数我们直接设置为0,表示默认就可以了。msgtyp表示需要接受的消息队列的类型。

3、信号量

1、简单的原理介绍

在正式介绍信号量之前,我们需要了解一些预备知识:

<1>对一个共享资源的保护,是在多执行流场景下的一个常见问题,这是因为在多个执行流访问同一块共享资源时,可能会造成一些问题,比如数据不一致等。目前,管道通信是系统帮我们将管道这个共享资源进行保护,而共享内存是不会被OS主动保护的,需要用户去手动保护。、

<2>为了保护共享资源,我们一般有两种机制,我们称为互斥和同步机制。互斥,我们可以理解成当一名同学去食堂打饭时,打饭的阿姨只能给这一名同学打饭,而不能给其他同学打的这种状态,我们就可以理解成互斥状态。而同步,我们可理解成一名同学打饭,打饭完成后立马吃完,一直站在窗口上,不断让阿姨打饭,这样就会造成其他同学压根打不到饭。所以规定一个同学打完饭后必须重新站到队尾,让每个同学都能打到饭,这种机制,我们就称为同步。

<3>被保护起来的,任意时刻只允许一个执行流访问的公共资源,我们称为临界资源,访问临界资源的代码,我们就叫做临界区,其他区域叫非临界区。

<4>原子性:操作对象的时候,只有两种情况,要么还没开始,要么已经结束。

注:由于信号量涉及的内容大多与多线程有关,所以下面只做简单的介绍。

我们以电影票买票来引入信号量的概念,当我们去看电影时,一般都需要购买电影票,只有买了电影票,才能在电影的时候拥有座位。这个座位实际上是就属于电影院的一种资源,我们买票的过程就是对资源进行预定,预订以后,虽然资源现在没有被我持有,但是在未来的时间段内,只要我想,就可以使用这一部分资源。

而电影院,就需要合理的卖票 -- 有多少资源,我们就卖多少票,而电影院需要确保每一份资源都不会出现并发访问的情况,也就是多人持有一张票的情况。虽然我们将电影院看成一个整体使用(也就是只有一张票)情况下,不容易出现问题,但是资源的利用效率太低。为了提高资源的利用率,我们就在电影院中安排了很多座位,也就是将电影院资源分成很多小资源。而当进程进来申请资源时,我们需要防止申请进程数大于资源数量的情况。为了避免该情况的发生,我们就需要限制进来的进程数,合理地分配资源。

如何限制进来地进程数呢?这就需要提到信号量了,信号量本质上是一个计数器,用于描述临界资源地一个计数器(我们用int count 这个变量来描述这个计数器)。

而我们的进程申请信号量,就相当于买票预定,只要信号量申请成功(count -- ),就一定有你的资源。如果我们申请失败,就必须继续等,只有等待申请成功,才能进行下一步。申请完成后,我们就可以进行访问资源,访问完成后,就可以释放信号量(count ++)。申请信号量和释放信号量的过程就是在保护临界资源。 

这里有一个问题,那就是在多进程场景下中,我们可不可以用int来实现信号量呢? 答案是不行,因为在进程之间,整型变量无法共享。不同的进程需要看到同一块计数器资源。另一个原因是因为整型的加减不是原子的,在我们将整型的加减代码转换成汇编代码,我们就会发现加减代码回转成多条汇编语句,只有一条汇编语句才能绝对保证原子性(了解即可)。

上面我们申请信号量和释放信号量,都是多进程/线程必须遵守的规定。所有进程访问临界资源时,都必须先申请信号量,这就说明所有的进程都要看到同一个信号量资源,那么信号量就是一块共享资源,为了确保这个信号量的安全,OS就必须确保信号的申请和释放操作是原子的。而申请信号量我们称为P操作,释放信号量我们称为V操作,和起来我们称为PV操作。

当我们的信号为1时,这就是互斥,而这个二元信号量也是就是一把锁。

2、信号量的相关接口:

当我们申请信号量时,是可以申请申请多个信号量,注意区分申请一个大小为n的信号量。

<1>semget

该系统调用的接口用于创建一个信号量集,第一个参数key和消息队列、共享内存的key无异,第二个参数表示想申请几个信号量,第三个参数和消息队列的shmget与msgget中的第三个参数无异,我们一般使用IPC_CREAT和IPC_EXCL。如果成功返回一个非负整数(semid),用于标识信号量集,失败返回-1。

<2>semctl

该接口用于控制创建的信号量集,第一个参数,我们返回semid即可。第二个参数,表示要对信号集中哪一个参数进行调整。第三个参数表示要对哪些信号量进行哪些操作。常见的有IPC_STAT、IPC_RMID、IPC_SET等等,和shmctl和msgctl的选项功能一样。" .... "这是一个可选的参数,它是一个union semun类型的变量,包含了与特定命令相关的附加信息。某些semctl命令需要额外的数据来执行操作,比如设置信号量的值、获取信号量集的权限等。这个参数提供了这些数据。

<3>semop

该接口用于表示对特定信号量执行操作。

第一个参数表示要对哪一个信号集进行操作,第二个参数需要我们自己定义一个结构体struct sembuf,里面包含的成员有如下几个:

第一个成员表示要对信号量集(我们可以看成一个数组)的哪一个进行操作,该参数就是信号量集的下标,第二个成员表示要进行P操作还是V操作,-1表示要进行P操作,1表示进行V操作。第三个成员表示操作标识,默认设为0。

第三个参数指定信号操作的数量,也就是操作几次。

4、system V进程通信的原理

通过上面的介绍,我们其实可以发现,共享内存,消息队列这三个通信方式有很多的相似之处,这是OS故意为之,目的就是为了更好地管理这些IPC资源( Inter-Process Communication)。为了管理好这些资源,OS肯定需要通过结构体对这些资源进行描述和组织。

在内核中,存在一个ipc_ids的结构体管理ipc资源,其中包含了一个entries指针,这个指针指向ipc_id_ary,在ipc_id_ary中有一个柔性数组,存放了struct kern_ipc_perm* 的指针,struct kern_ipc_perm是在内核中管理这些ipc资源属性的结构体。每当我们创建ipc资源时,OS就把描述对应资源的结构体指针放入该柔性数组。msgid、shmid、semid其实就是对应的数组下标,如果下标过大,id会进行回绕。这里操作系统将所有的指针都看成kern_ipc_perm,当OS需要对特定ipc资源进行操作时,直接把对应的指针强转为对应的类型即可,这里访问ipc资源可以通过key来实现(key的地址就是每个结构体的地址,通过key就可以直接访问结构体)。为了获取对应的类型,OS会在ipc_perm的mode成员中加入数据类型。我们一般可以用位操作定义不同的宏,通过宏来表示不同的类型。

但是由于其设计理念和Liunx一切皆文件的设计初衷稍有偏差,导致这种通信方式已经被边缘化。感谢阅读,如有不对之处,还望各位大佬指正。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • ssrf实现
  • springboot+vue+mybatis计算机毕业设计电子产品交易系统+PPT+论文+讲解+售后
  • iview checkbox单独使用时 如何去掉显示的true和false以及不显示文字
  • VBA技术资料MF180:将某个文件夹中的某类图片导入Word
  • sqlite blob 数据检索(基于sqlite3_get_table的优化)
  • 如何使用Gitee管理自己的项目
  • 【自由能系列(初级)】自由能原理——神经科学的“能量守恒”方程
  • 惠海H6900B升压恒流调光IC芯片3.7V7.4V12V升压18V24V36V 48V 植物灯/电解水
  • 娱乐社交、游戏等行业共探合规前提下,实现产品可持续的增长与营收 | 网易数智x华为云泛娱乐行业沙龙-杭州站邀您前来!
  • 学苑教育杂志社学苑教育编辑部学苑教育杂志2024年第23期目录
  • 解决有向图中节点出度和入度计算问题
  • 万字详解Spring框架基础(Java开发社区最受欢迎的框架之一)
  • python 和C通过共享内存通信
  • Redis 实现哨兵模式
  • uniapp,uview:inputnumber或者input,当type为number的时候,在ios里输入不了小数的问题
  • @jsonView过滤属性
  • [分享]iOS开发-关于在xcode中引用文件夹右边出现问号的解决办法
  • 【399天】跃迁之路——程序员高效学习方法论探索系列(实验阶段156-2018.03.11)...
  • Angular2开发踩坑系列-生产环境编译
  • Codepen 每日精选(2018-3-25)
  • Cumulo 的 ClojureScript 模块已经成型
  • echarts的各种常用效果展示
  • iOS动画编程-View动画[ 1 ] 基础View动画
  • java 多线程基础, 我觉得还是有必要看看的
  • Java小白进阶笔记(3)-初级面向对象
  • linux安装openssl、swoole等扩展的具体步骤
  • Linux后台研发超实用命令总结
  • Map集合、散列表、红黑树介绍
  • Redux 中间件分析
  • Spring Boot MyBatis配置多种数据库
  • Swoft 源码剖析 - 代码自动更新机制
  • Unix命令
  • Vue 2.3、2.4 知识点小结
  • 爱情 北京女病人
  • 不上全站https的网站你们就等着被恶心死吧
  • 记一次用 NodeJs 实现模拟登录的思路
  • 经典排序算法及其 Java 实现
  • 码农张的Bug人生 - 初来乍到
  • 十年未变!安全,谁之责?(下)
  • 王永庆:技术创新改变教育未来
  • 一加3T解锁OEM、刷入TWRP、第三方ROM以及ROOT
  • scrapy中间件源码分析及常用中间件大全
  • 数据库巡检项
  • 小白应该如何快速入门阿里云服务器,新手使用ECS的方法 ...
  • 智能情侣枕Pillow Talk,倾听彼此的心跳
  • ​​​【收录 Hello 算法】9.4 小结
  • ​【已解决】npm install​卡主不动的情况
  • ()、[]、{}、(())、[[]]等各种括号的使用
  • (5)STL算法之复制
  • (黑马点评)二、短信登录功能实现
  • (六) ES6 新特性 —— 迭代器(iterator)
  • (六)DockerCompose安装与配置
  • (三)elasticsearch 源码之启动流程分析
  • (四)linux文件内容查看
  • (四)opengl函数加载和错误处理