进程间的通信 -- 共享内存

一 共享内存的概念

1. 1 共享内存的原理

     之前我们学过管道通信,分为匿名管道和命名管道,匿名管道通过父子进程的属性继承原理来完成父子进程看到同一份资源的目的,而命名管道则是通过路径与文件名来唯一标识管道文件,来让不同的进程之间进行通信。

   而共享内存也是同理, 就是允许两个或多个不相关的进程访问同一段物理内存空间。

1.2 基本概念 

   不同进程之间共享的内存通常为同一段物理内存。进程可以 将同一段物理内存连接到他们自己的地址空间中 ,所有的进程都可以访问共享内存中的地址。如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。

 概念图如下:

 

 二 共享内存的使用

2.1 使用流程

一般而言,共享内存的使用可以分为以下几步:

  1. 创建共享内存段
  2. 关联共享内存(建立通信)
  3. 传输数据
  4. 关闭共享内存区域

2.1.1 创建共享内存段 shmget

创建共享内存接口:shmget

创建一个新共享内存段或者取得一个既有共享内存段的标识符(即由其他进程创建的共享内存段)。这个调用将返回后续调用中需要用到的  共享内存标识符。

接口如下:
 

int shmget(key_t key, size_t size, int shmflg);- 功能: 创建一个新的共享内存段,或者获取一个既有的共享内存段的标识新创建的内存段中的数据都会被初始化为0- 参数:- key: key_t 类型是一个整型,通过这个找到或者创建一个共享内存一般使用16进制标识,非0值- size: 共享内存的大小,以几页的大小创建(大于1的数值,一般为4的整数)- shmflg: 属性- 访问权限- 附加属性: 创建/判断共享内存是不是存在- 创建: IPC_CREAT- 判断共享内存是否存在: IPC_EXCL,需要和 IPC_CREAT一起使用IPC_CREAT | IPC_EXCL | 0664(权限)- IPC_CREAT	如果内核中对应key值的共享内存不存在,则新建一个共享内存并返回该共享内存的句柄;如果已存在,则直接返回该共享内存的句柄– IPC_CREAT IPC_EXCL	如果不存在对应key值的共享内存则新建一个共享内存并返回该共享内存的句柄;如果已存在,则出错返回- 返回值:失败: -1 并设置错误号成功: >0 返回共享内存的引用的ID (int类型),后面操作共享内存都是通过这个值

2.1.2 key值的获取 ftok 

 key值的作用:

问:当我们调用系统接口申请了一块共享内存,我们要保证对应的进程能够访问到同一块共享内存,那么如何做到这一点呢?

答案:每一个共享内存被申请的时候都会有一个key值,这个key值用于标识系统中共享内存的唯一性。

ftok函数的作用:通过数学运算将一个已存在的路径名pathname和一个整数标识符proj_id转换成一个key值,称为IPC键值,在使用shmget函数获取共享内存时,这个key值会被填充进维护共享内存的数据结构当中。需要注意的是,pathname所指定的文件必须存在且可存取。

ftok运算出来的key值可能会产生冲突,不过概率很小。如果产生冲突,就对ftok的参数进行修改即可。

当我们想让两个进程使用共享内存的时候,我们就利用key值寻找到这一块共享内存

key_t ftok(const char* pathname, int proj_id);- 功能: 根据指定的路径名, 和int值, 生成一个共享内存的key- 参数: - pathname: 指定一个存在的路径/home/ubuntu/Linux/a.txt/root- proj_id: int类型的值,但是这个系统调用只会使用其中的1个字节(8bit)范围: 0-255   一般指定一个字符 'a'

2.1.3  共享内存的关联 shmat

我们不是单单把共享内存创建出来就行了,我们还要将需要获取这块共享内存的进程与对应的共享内存关联起来,通信结束后,解除关联,最后进行共享内存的释放。

void* shmat(int shmid, const void* shmaddr, int shmflg);- 功能: 和当前的进程进行关联- 参数: - shmid: 共享内存的标识(ID),由shmget返回值获取- shmaddr: 申请的共享内存的起始地址(虚拟内存的地址,一般不会手动指定,由内核指定,设置为NULL)- shmflg: 对共享内存的操作- 读权限: SHM_RDONLY,必须要有读权限- 读写: 0- 返回值:成功: 返回共享内存的首(起始)地址失败: (void*) -1 设置错误号

 2.1.4 解除共享内存的关联 shmdt

   接触进程和共享内存的关联。

int shmdt(const void* shmaddr);- 功能: 解除当前进程和共享内存的关联- 参数:- shmaddr: 共享内存的首地址- 返回值:成功:  0失败: -1

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 2.1.5 删除共享内存函数  shmctl

int shmctl(int shmid, int cmd, struct shmid_ds* buf);- 功能: 对共享内存进行操作。 删除共享内存,共享内存要删除才会消失, 创建共享内存的进程被销毁了对共享内存是没有任何影响的。- 参数:- shmid: 共享内存的ID- cmd: 要做的操作- IPC_STAT: 获取共享内存的当前状态- IPC_SET: 设置共享内存的状态- IPC_RMID: 标记共享内存被销毁- buf: 需要设置或者获取的共享内存的属性信息- IPC_STAT: buf存储数据- IPC_SET: buf中需要初始化数据,设置到内核中- IPC_RMID: 没有用,NULL

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

注意:一般有几个进程关联 这一块共享内存,就需要解除关联多少次,但是,删除共享内存只需要删除一次就好了(其实可以删除多次,看补充)。

2.1.6 示例 

 接下来我们进行一段示例,操作内容如下:

要求:使用代码创建一个共享内存, 支持 A.B 两个进程进行通信。

 进程A 向共享内存当中写 “i am process A”。

 进程B 从共享内存当中读出内容,并且打印到标准输出。

  因为两个进程中有大量重复代码,因此,我们封装一个头文件,以此来复用。

shared.h内容如下:
 

#include<iostream>
#include<cassert>
#include<cstring>
#include<cerrno>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<cstdlib>
#include<unistd.h>
using namespace std;#define PATHNAME "./shared2"//ftok的第一个参数,是一个合法路径
#define PROJ_IO 'a'//ftok的第二个参数
#define MAXSIZE 4096//创建的共享内存的大小
//1.获取key值代码
key_t GetKey()
{key_t k = ftok(PATHNAME, PROJ_IO);//如果返回值小于0 则创建失败if(k < 0){std::cerr << errno << ":" << strerror(errno) << std::endl;exit(1);}return k;
}//2.创建共享内存段代码
int GetShmget(key_t k,int flags){int shmid = shmget(k, MAXSIZE, flags);if(shmid < 0){std::cerr << errno << ":" << strerror(errno) << std::endl;exit(2);}return shmid;
}//获取共享内存
int Getshm(key_t k){return GetShmget(k, IPC_CREAT);//没有就创建,有就获取
}//创建共享内存
int Createshm(key_t k){return GetShmget(k, IPC_CREAT | IPC_EXCL | 0600);//没有创建,有就报错,这里创建内存需要给对应的权限
}//3. 关联共享内存,返回共享内存的空间起始位置
void* attachshm(int shmid){void* p = shmat(shmid, nullptr, 0);//因为linux系统是64位,一个地址是8个字节,所以要变成8个字节大小的数据类型做对比if((long long)p == -1L){std::cerr << errno << ":" << strerror(errno) << std::endl;exit(4);}return p;
}//4.解除共享内存的关联
void detachshm(void* p){//如果失败报错if(shmdt(p) == -1){std::cerr << errno << ":" << strerror(errno) << std::endl;}
}//5 shmctl删除共享内存
void delshm(int shmid){if(shmctl(shmid, IPC_RMID, nullptr) == -1){std::cerr << errno << ":" << strerror(shmid) << std::endl;exit(3);}
}

A.cpp:

#include"shared.hpp"int main(){//先生成一个key值key_t k=GetKey();printf("A 获取key值成功: %d\n",k);//创建共享内存,有就获取标识码shmidint shmid=Getshm(k);printf("A 获取共享内存块成功\n");//关联共享内存块char* p = (char*)attachshm(shmid);if (p == (void*)-1) {perror("shmat failed");delshm(shmid);printf("A 删除共享内存成功\n");exit(EXIT_FAILURE);}printf("A 关联共享内存块成功\n");//开始写入:const char* str = "i am process A";snprintf(p, MAXSIZE, "%s", str);//去关联detachshm(p);printf("A 去关联成功\n");return 0;
}

B.cpp

 #include"shared.hpp"int main(){//先生成一个key值key_t k=GetKey();printf("B 获取key值成功: %d\n",k);//创建共享内存,有就获取标识码shmidint shmid=Getshm(k);printf("B 获取共享内存块成功\n");//关联共享内存块char* p = (char*)attachshm(shmid);if (p == (void*)-1) {perror("shmat failed");delshm(shmid);printf("B 删除共享内存成功\n");exit(EXIT_FAILURE);}printf("B 关联共享内存块成功\n");//开始读出:sleep(5);printf("attach sucess, address p: %s\n",p);//去关联detachshm(p);printf("B 去关联成功\n");//删除共享内存delshm(shmid);printf("B 删除共享内存成功\n");return 0;
}

 代码结果:

2.2 共享内存的命令行操作

2.2.1 共享内存的查看 – ipcs指令

 ​  报告进程间通信设施的状态,包括共享内存、消息队列以及信号量等等

ipcs用法:

ipcs -a //打印当前系统中所有进程间通信方式的信息

ipcs -m //打印出使用共享内存进行进程间通信的信息(**常用**)

ipcs -q //打印出使用消息队列进行进程间通信的信息

ipcd -s //打印出使用信号进行进程间通信的信息

2.2.2  共享内存的删除- ipcrm 指令

ipcrm用法(rm:remove)

ipcrm -M shmkey //移除用shmkey创建的共享内存段

ipcrm -m shmid //移除用shmid标识的共享内存段

ipcrm -Q msgkey //移除用msqkey创建的消息队列

ipcrm -q msqid //移除用msqid标识的消息队列

ipcrm -S semkey //移除用semkey创建的信号

ipcrm -s semid //移除用semid标识的信号

三 补充说明

 问题1:操作系统如何知道共享内存被读诵进程关联- 共享内存委会一个结构体  struct shmid_ds 这个结构体中有一个成员 shm_nattach- shm_nattach 记录了关联的进程个数
 问题2:可不可以对共享内存进行多次删除 shmctl- 可以的- 因为 shmctl 仅是标记删除共享内存,不是直接删除- 什么时候真正删除呢?当和共享内存关联的进程数为0的时候,就真正被删除- 当共享内存的key为0的时候, 共享内存被标记删除了- 因此,需要合理衡量共享内存的删除,不要在其它进程还在使用时删除

 

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

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

相关文章

typescript 的常用方式

文章目录 前言一、绑定props 默认值的方式&#xff1a;withDefaults1.vue2 的props设置默认值2.vue3 的props设置默认值(1) 不设置默认值的写法(2) 设置默认值的写法&#xff08;分离模式&#xff09;(3) 设置默认值的写法&#xff08;组合模式&#xff09; 二、定义一个二维数…

Matlab在同一张图中如何加入多个图例

根据代码最终画出的图片如下&#xff1a; 其实原理很简单&#xff0c;就是在一张figure中画多个坐标轴&#xff0c;每个坐标轴都有对应的图例&#xff0c;之后再将多余坐标轴隐藏&#xff0c;只保留一个即可。 代码如下&#xff1a; clear all; close all;dd_linewidth 1;a …

maven archetype 项目原型

拓展阅读 maven 包管理平台-01-maven 入门介绍 Maven、Gradle、Ant、Ivy、Bazel 和 SBT 的详细对比表格 maven 包管理平台-02-windows 安装配置 mac 安装配置 maven 包管理平台-03-maven project maven 项目的创建入门 maven 包管理平台-04-maven archetype 项目原型 ma…

Spring学习笔记(六)利用Spring的jdbc实现学生管理系统的用户登录功能

一、案例分析 本案例要求学生在控制台输入用户名密码&#xff0c;如果用户账号密码正确则显示用户所属班级&#xff0c;如果登录失败则显示登录失败。 &#xff08;1&#xff09;为了存储学生信息&#xff0c;需要创建一个数据库。 &#xff08;2&#xff09;为了程序连接数…

php源码 单色bmp图片取模工具 按任意方式取模 生成字节数组 自由编辑点阵

http://2.wjsou.com/BMP/index.html 想试试chatGPT4生成&#xff0c;还是要手工改 php 写一个网页界面上可以选择一张bmp图片&#xff0c;界面上就显示这张bmp图片&#xff0c; 点生成取模按钮&#xff0c;在图片下方会显示这张bmp图片的取模数据。 取模规则是按界面设置的&a…

Linux 的交换空间(swap)是什么?有什么用?

目录 swap是什么&#xff1f;swap有什么用&#xff1f;swap使用典型场景如何查看你的系统是否用到交换空间呢&#xff1f;查看系统中swap in/out的情况 swap是什么&#xff1f; swap就是磁盘上的一块区域。它和Windows系统中的交换文件作用类似&#xff0c;但是它是一段连续的…

03、MongoDB -- MongoDB 权限的设计

目录 MongoDB 权限的设计演示前准备&#xff1a;启动 mongodb 服务器 和 客户端 &#xff1a;1、启动单机模式的 mongodb 服务器2、启动 mongodb 的客户端 MongoDB 权限的设计1、MongoDB 的每个数据库都可以保存用户&#xff0c;不止admin数据库可以保存用户。2、保存用户的数据…

Linux 学习笔记(8)

八、 启动引导 1 、 Linux 的启动流程 1) BIOS 自检 2) 启动 GRUB/LILO 3) 运行 Linux kernel 并检测硬件 4) 挂载根文件系统 5) 运行 Linux 系统的第一个进程 init( 其 PID 永远为 1 &#xff0c;是所有其它进程的父进程 ) 6) init 读取系统引导配置文件…

GD25Q32驱动

GD25Q32是一款基于SPI的Flash芯片&#xff0c;容量为32/84M bytes。它的引脚如下&#xff1a; 该芯片支持多种SPI操作方式&#xff0c;包括&#xff1a;Standard SPI(标准SPI)、Dual SPI(双线 SPI)和Quad SPI(四线 SPI) 。有关SPI的介绍可以参考&#xff1a; SPI通信原理-CSDN…

flutter 文字一行显示,超出换行

因为app有多语言&#xff0c;中文和其他语言长度不一致&#xff0c;可能导致英文会很长。 中文样式 英文样式 代码 Row(mainAxisAlignment: MainAxisAlignment.end,crossAxisAlignment: CrossAxisAlignment.end,children: [Visibility(visible: controller.info.fee ! null,ch…

探寻2024年国内热门低代码平台排行!| 功能特点一览

低代码开发是一项革命性的技术&#xff0c;主要目的是尽量避免程序研发的复杂性&#xff0c;让外行开发者也能加入到应用程序的搭建中。低代码平台的核心概念和构成部分通常包括用户界面和拖拽设计、预构件和模块、自动化工作内容与数据库集成和扩展应用&#xff0c;应用低代码…

U盘弹出提示“该设备正在使用中”:原因与解决方案

在日常使用U盘时&#xff0c;我们可能会遇到一个问题&#xff1a;当尝试安全弹出U盘时&#xff0c;系统提示“该设备正在使用中”。这种情况可能会让用户感到困惑&#xff0c;担心数据是否安全或是否会导致U盘损坏。本文旨在探讨这一现象背后的原因&#xff0c;并提供相应的解决…

【前端素材】推荐优质后台管理系统网页Stisla平台模板(附源码)

一、需求分析 1、系统定义 后台管理系统是一种用于管理和控制网站、应用程序或系统的管理界面。它通常被设计用来让网站或应用程序的管理员或运营人员管理内容、用户、数据以及其他相关功能。后台管理系统是一种用于管理网站、应用程序或系统的工具&#xff0c;通常由管理员使…

铅冶炼作业VR虚拟现实互动培训平台降低实操风险

在钢铁工业中&#xff0c;焦炉作业是一个关键的环节&#xff0c;也是一项技术要求高、操作复杂的任务。传统焦炉作业的培训通常需要在实际的焦炉上进行&#xff0c;这不仅对学员的身体素质和心理素质提出了较高的要求&#xff0c;而且也存在一定的安全风险。基于VR虚拟现实制作…

React富文本编辑器开发(三)

现在我们的编辑器显示的内容很单一&#xff0c;这自然不是我们的目标&#xff0c;让呈现的内容多元化是我们的追求。这就需要让编辑器能够接收多元素的定义。从初始数据的定义我们可以推断数据的格式远不止一种&#xff0c;那么其它类型的数据如何定义及呈现的呢&#xff0c;我…

手把手教你免费用Flashduty做消息通知

为什么需要消息通知&#xff1f; 如果有重要的情况发生&#xff0c;希望能通过各种媒介通知我们。可以举几个例子&#xff1a; 家里燃气费没有了&#xff0c;希望能有短信或者app通知api频繁500报错&#xff0c;希望及时感知&#xff0c;及时修复公司网站是https自签名证书&a…

白话大模型① :AI分析能做什么?在实际落地中会碰到什么问题?

白话大模型系列共六篇文章&#xff0c;将通俗易懂的解读大模型相关的专业术语。本文为第一篇&#xff1a;AI分析能做什么&#xff1f;在实际落地中会碰到什么问题&#xff1f; 作者&#xff1a;星环科技 人工智能产品部 我们使用一个简单的应用实例来解析人工智能分析都在做什…

若依框架使用mars3d的环境配置,地球构建

因项目需要&#xff0c;原本使用过的cesium依赖&#xff0c;现在想使用火星科技mars3d的一些功能&#xff0c;所以需要引入mars3d依赖&#xff0c;整个过程非常的坎坷&#xff0c;以至于我都不知道到底是哪些部分是标准的。。。先把我认为对的记录一下&#xff1a; 1.vue.conf…

sqlserver保存微信Emoji表情

首先将数据库字段&#xff0c;设置类型为 nvarchar(200)一个emoji表情&#xff0c;占4字节就可以了&#xff0c;web前端展示不用改任何东西&#xff0c;直接提交数据保存&#xff1b;回显也会没有问题&#xff0c;C#代码不用做任何处理&#xff1b; 不哭不闹要睡觉&#x1f31…

【机器学习300问】24、模型评估的常见方法有哪些?

一、为什么要对模型进行评估&#xff1f; 对机器学习和神经网络的模型进行评估是至关重要的&#xff0c;原因如下&#xff1a; 得知模型的泛化能力 模型评估的主要目的是了解模型在未见过的数据上的表现&#xff0c;即其泛化能力。这是因为模型的性能在训练数据上可能会过拟合…