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

简明linux系统编程--共享内存消息队列信号量

目录

1.父子进程共享内存

1.1基本说明

1.2主要步骤

 1.3shmget函数介绍​编辑

1.4函数返回值

1.5shmat函数介绍

1.6shmdt函数介绍

1.7结合代码理解

2.非亲缘关系的进程的共享内存通信

2.1和上面的区别

2.2如何通信

2.3具体代码

3.父子进程消息队列

4.非亲缘关系的进程的消息队列 

5.信号量的介绍

5.1基本说明

5.2进程控制

5.3函数介绍

5.4代码说明

6.命名信号量(无亲缘关系)

7.信号量线程同步


1.父子进程共享内存

1.1基本说明

共享进程就是多个进程都可以使用的,但是为了不影响进程的连续性,我们的系统会把这个共享的内存映射到每一个独立的进程空间里面去;

按照下面的这个图进行理解,我们的这个共享内存会映射到这个AB里面各一份,AB是两个各自独立的进程,这个时候,AB对于映射到自己进程的这个小的空间进行操作就是相当于对于这个大的共享内存进行操作;

1.2主要步骤

其实整个列成只需要4个步骤:

  • 建立共享内存,
  • 把共享内存映射到AB进程里面去,
  • 解除映射和绑定的关系,
  • 删除共享内存,

但是第四个需要根据实际情况决定是否要删除,如果多个进程都要使用,删除这个共享内存之后,可能某些进程就会受到影响;

 1.3shmget函数介绍

key(键值)指的就是共享内存的一个编号,是在内核层面进行标识的;

但是在应用层上面,我们使用的是标识符(IPC_CREATE)进行标记的;

size就是这个共享内存的大小;

其中这个函数参数的第三个IPC_CREAT就是指的当我们创建共享内存单元的时候,如果这个共享内存单元不存在,我们就创建,已经存在的话,我们就尝试去访问打开这个共享内存;

1.4函数返回值

返回的就是应用层的共享内存的描述符,就是上面说到的这个identifier;

1.5shmat函数介绍

shmat把共享内存的标识符映射到调用的进程的地址空间里面,实际上就是一个映射的过程;

第一个参数就是标识符,我们把这个共享内存映射到进程的哪一个地方,我们可以指定,需要给出来具体地址,但是也可以使用null让这个操作系统给我们分配位置

第三个参数,就是这个shmflg,如果我们写成0,就是进程对于这个映射过来的共享内存具有读写权限,当然,我们可以设置为只读或者是只写的权限,但是我们写成0就可以了;

返回值,表示的映射的地址空间的首地址,就是这个进程里面映射区域的首地址

1.6shmdt函数介绍

shmat函数就是用来进行这个解除共享内存在这个进程里面的映射,从而让这个共享内存在这个映射区域里面不会发挥作用;

1.7结合代码理解

  •  首先我们就是shget函数创建共享内存,IPC_CREATE就是应用层上面的标识符,用来区分共享内存的,类似于每一个人的身份证号码;
  • fork函数就是创建这个父子进程,其中这个子进程返回值是0,父进程返回值是这个子进程的pid,因此这个父进程的返回值大于0;
  • 首先进入的就是父进程的分支语句,我们的这个父进程把这个映射的区域进行清除之后,把这个msg这个信息写代映射的这个空间里面去,相当于间接对于共享内存进行操作,然后解除这个共享内存的区域
  • 我们的这个子进程需要sleep一段时间,读取这个共享内存里面的内容,最后打印输出;
  • 同时这个里面使用了wait_pid函数,让这个父进程进行等待,等待这个子进程完成任务,就是一个进程等待;

2.非亲缘关系的进程的共享内存通信

2.1和上面的区别

上面的这个是父子进程,两个是有亲缘关系的,但是这个时候我们的两个进程是完全独立运行的,两个之间没有任何的关系;

2.2如何通信

就是我们上面的这个进行共享内存创建的时候,使用的是这个shmget函数的IPC_PRIVATE参数,就这个参数而言,父进程和子进程是相同的,因为这个子进程完全拷贝我们的父进程的代码和数据

但是如果是两个不相关的进程,我们肯定是一个进程进行read操作,一个进程进行write操作,这个read进程把这个写的内容进行读取,这个时候我们的两个进程里面肯定是要各自去创建共享内存的,而且这个shmget函数的第一个参数需要是一样的,否则我们的一个进程写进去之后,另外一个进程根本无法进行读取;

上面的是因为父子进程的这个第一个参数是一样的,所以这个可以使用默认值,他们也可以找到写入的数据,但是这个没有亲缘关系的两个进程,如果不进行这个参数的指定,读的进程是根本找不到这个内从写到哪里呢,就也不知道去哪里读,这个就是和上面的父子进程最大的区别;

2.3具体代码

负责写内容的进程,可以看到这个里面是设置了一个宏常量进行这个参数的制定的,而且两个进程的这个参数值是一样的,这样这个数据才可以被正常的读取;

负责读取内容的进程:

3.父子进程消息队列

描述符标识符就是这个函数的返回值,返回值和我们的这个key相互关联,这个key是在我们的内核层,就是键值,应用层使用的就是identifier进行区分;

下面的这个msgget函数就是创建一个消息队列,我们的父进程使用一个函数megsnd发送消息的内容到这个队列里面去,我们的子进程通过一个函数msgrcv读取这个父进程放到这个消息队列里面的内容;

  • 我们的这个结构体里面的内容就是这个消息队列的内容的组成,其中这个父进程写入数据到这个消息队列里面,这个子进程获取数据;
  • 因为这个结构体里面包含了这个数据类型的大小,因此我们计算这个发送内容的大小的时候,使用的是这个结构体的大小减去这个里面的这个类型的大小;
  • 我们的这个消息队列和共享内存的最大差别就是:消息队列里面的一个结构体可以一次性传递不同类型的数据,这个是有这个结构体组成内容控制的;
  • 这个里面的buf这个结构体,子进程和父进程都是有的,因为这个子进程拷贝了这个父进程的数据和代码;

4.非亲缘关系的进程的消息队列 

非亲缘关系的进程之间的这个消息的传输也是可以进行的,只要我们的这个接收端前往和发送端的消息号相同的消息队列里面去读取内容就是可以成功的;

也就是这个msgrcv函数里面的倒数第二个参数需要我们的发送端的这个消息数据的类型号是一样的,这样才可以保证消息的准确传输;

发送端的进程:

接收端的进程:

5.信号量的介绍

5.1基本说明

管道,共享内存和消息队列都是进行这个数据的传输的,一个资源想要被多个进程访问,就是进行同步,信号量就是进行任务之间的同步;

信号量就是一个整数值,A访问资源的时候,检查信号量是不是大于0,B进程也想要访问这个内存,B进程也是需要检查这个进程的信号量是不是大于0的,这个时候进程B处于阻塞状态,直到这个信号量大于0,才会继续运行;

信号量进行工作的时候,如果被设置为1,一个进程访问之后,就会被消耗为0,这个时候其他的进程想要访问这个内存的时候,其他的想要访问的进程就会处于阻塞的状态

5.2进程控制

我们的这个信号量也是划分为无名的信号量和有名的信号量,这个和我们当时学习的管道是一样的逻辑,因为我们的管道也是划分为这个有名管道和无名管道的;

对于这个进程的控制,因为这个信号量是可以进行消耗和发布的,例如这个信号量只是1,我们的父进程使用这个资源,消耗掉了这个信号量,这个时候的父进程就会处于阻塞的状态,因为只有这个信号量大于0 的时候这个才是可以正常运行的,这个时候,我们的子进程负责发布信号量,这个时候我们的子进程什么时候发布这个信号量,我们的父进程就会从这个阻塞状态变为正常的运行状态;

因此这个子进程发布信号量的时刻决定了这个父进程从阻塞状态转换为正常运行状态的时刻,因此这个就间接的实现了我们的子进程对于父进程的控制;

5.3函数介绍

sem_init函数的用法就是创建信号量,用来被其他的进程使用;

sem_wait函数的用法就是检查这个信号量是不是大于0,如果大于0,我们的进程就可以使用,如果不是我们就需要被处于阻塞状态;

sem_post函数作用就是进程发布信号量,让处于阻塞的进程正常运行;

mmap函数就是产生虚拟的地址空间,让这个父进程和子进程共享这个生成的地址空间,从而让这个子进程增加的信号量可以被子进程看到,否则我们的子进程发布的信号量无法被父进程看到;

5.4代码说明

这个代码就是综合上面的函数以及这个父子进程的这个行为,实现的子进程增加信号量,当我们的父进程因为消耗掉信号量处于阻塞状态的时候,能够看到这个子进程的发布而让这个阻塞的进程运行起来;

6.命名信号量(无亲缘关系)

创建一个父进程,消耗信号量,创建一个子进程,发布信号量控制第一个进程的状态:

7.信号量线程同步

这个主要介绍一下区别,就是我们的进程之间是相互独立的,因此我们需要使用这个mmap函数创建一个寻你的空间让两个进程之间的信号量可以相互看到,但是对于进程而言,多个线程公用一个进程的资源,因此这个是可以直接进行这个信号量的传递的,不需要使用这个mmap函数进行信号量的虚拟地址的开辟,两个线程使用的这个信号量本来就是属于相同的进程的,因此他们是可以相互看到的,不需要使用这个mmap函数;

下面的这个代码就是调用这个pthread函数进行现成的创建,使用这个sem_wait函数消耗信号量,使用sem_post去发布信号量;

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Chainlit集成Langchain并使用通义千问实现和数据库交互的网页对话应用增强扩展(text2sql)
  • 8.sklearn-模型保存
  • VirtualBox7.1.0 安装 Ubuntu22.04.5 虚拟机
  • @JsonFormat 和 @DateTimeFormat 的区别
  • JavaScript substring() 方法
  • Redisson 分布式锁的使用详解
  • 将有序数组——>二叉搜索树
  • Leetcode 3290. Maximum Multiplication Score
  • Python 数学建模——高斯核密度估计
  • 【读书笔记-《30天自制操作系统》-22】Day23
  • vue实现二维码生成器应用
  • Kotlin cancel CoroutineScope.launch的任务后仍运行
  • CPU 和 GPU:为什么GPU更适合深度学习?
  • 模仿抖音用户ID加密ID的算法MB4E,提高自己平台ID安全性
  • 【Java】网络编程-地址管理-IP协议后序-NAT机制-以太网MAC机制
  • “Material Design”设计规范在 ComponentOne For WinForm 的全新尝试!
  • android百种动画侧滑库、步骤视图、TextView效果、社交、搜房、K线图等源码
  • JavaScript类型识别
  • macOS 中 shell 创建文件夹及文件并 VS Code 打开
  • mockjs让前端开发独立于后端
  • mysql中InnoDB引擎中页的概念
  • NSTimer学习笔记
  • Perseus-BERT——业内性能极致优化的BERT训练方案
  • Redis字符串类型内部编码剖析
  • SAP云平台里Global Account和Sub Account的关系
  • spring security oauth2 password授权模式
  • 百度地图API标注+时间轴组件
  • 基于Volley网络库实现加载多种网络图片(包括GIF动态图片、圆形图片、普通图片)...
  • 前端每日实战 2018 年 7 月份项目汇总(共 29 个项目)
  • 微信开源mars源码分析1—上层samples分析
  • 中文输入法与React文本输入框的问题与解决方案
  • Java总结 - String - 这篇请使劲喷我
  • ​Java基础复习笔记 第16章:网络编程
  • #、%和$符号在OGNL表达式中经常出现
  • #{}和${}的区别?
  • (14)目标检测_SSD训练代码基于pytorch搭建代码
  • (2)nginx 安装、启停
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (PWM呼吸灯)合泰开发板HT66F2390-----点灯大师
  • (二)Linux——Linux常用指令
  • (附源码)ssm教师工作量核算统计系统 毕业设计 162307
  • (蓝桥杯每日一题)平方末尾及补充(常用的字符串函数功能)
  • (全注解开发)学习Spring-MVC的第三天
  • (三分钟了解debug)SLAM研究方向-Debug总结
  • (十六)串口UART
  • (顺序)容器的好伴侣 --- 容器适配器
  • (转)linux下的时间函数使用
  • .desktop 桌面快捷_Linux桌面环境那么多,这几款优秀的任你选
  • .net MVC中使用angularJs刷新页面数据列表
  • .net开源工作流引擎ccflow表单数据返回值Pop分组模式和表格模式对比
  • // an array of int
  • /dev下添加设备节点的方法步骤(通过device_create)
  • ::什么意思
  • @JSONField或@JsonProperty注解使用
  • @PreAuthorize与@Secured注解的区别是什么?