【Linux—进程间通信】共享内存的原理、创建及使用

什么是共享内存

      共享内存是一种计算机编程中的技术,它允许多个进程访问同一块内存区域,以此作为进程间通信(IPC, Inter-Process Communication)的一种方式。这种方式相对于管道、套接字等通信手段,具有更高的效率,因为数据不需要在用户空间和内核空间之间进行复制,也不需要经过序列化和反序列化的复杂过程。

特点:

  • 高速度:由于省去了数据复制和上下文切换的开销,共享内存提供了非常高的数据交换速度。
  • 低延迟:适用于需要快速响应和大数据量传输的场景。
  • 同步需求:虽然高效,但多个进程同时访问同一块内存可能会导致数据不一致。因此,需要使用如互斥锁、信号量等同步工具来确保数据的正确性和完整性。
  • 生命周期管理:共享内存段需要显式创建、映射到进程地址空间、使用后断开连接,并在不再需要时销毁,以避免资源泄露。
  • 共享内存在系统中可以存在多个,供不同进程之间进行通信

共享内存的原理

每一个进程都有属于自己的进程地址空间,假设操作系统在物理内存开辟了一段空间,该进程可以创建一段虚拟内存,将这段虚拟内存的起始与结束地址通过页表与物理内存的空间构建联系

如果另一个进程,也通过上述方式,通过页表映射到同一段物理内存,那就实现了让多个进程看到同一段空间,这样当一个进程向这段物理空间写入数据,另一个进程就可以马上从这段空间读取数据了,就可以实现进程间的通信了

共享内存的使用

(一)创建共享内存

#原型int shmget(key_t key, size_t size, int shmflg);
#参数key:用户自定义共享内存的标识size:共享内存大小shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
#返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

【解释】:

# key:由于进程间具有独立性,所以共享内存一定不是某一个进程自己创建的,而是进程通过函数调用让操作系统创建的,而为了使另一个进程可以找到该共享内存,每一个共享内存一定有一个唯一性的标识,但是这个标识一定不能是操作系统自己独立生成的,因为这样只有要创建共享内存的那个进程能找到该共享内存。所以用户可以通过key自己设定唯一的标识,key一般使用函数调用生成

#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);

pathname为文件路径,proj_id为项目id,这两个都是由用户自己设定的,该函数会通过特定的算法将参数形成唯一的key值

#size:共享内存的大小,建议设置为4096个字节的倍数,例如:当我们将共享内存设置为4097个字节,OS会申请4096*2大小的空间,由于我们申请的是4097个字节,剩下的4095个字节我们不能使用,就会浪费掉

#shmflg: 标志位参数有两种:IPC_CREAT、IPC_EXCL,常用的反方式有两种

  • IPC_CREAT: 如果要创建的共享内存不存在那就创建,如果存在就返回该共享内存
  • IPC_CREAT | IPC_EXCL:如果创建的共享内存不存在那就创建,如果存在就报错
  • 在使用时后面一般还要加上权限,防止进程无法与共享内存联系(注意)

ps:第二种使用方法可以保证每次创建的共享内存都是新创建的,所以在使用上,IPC_CREAT | IPC_EXCL一般用于创建共享内存,IPC_CREAT一般用于获取共享内存

#返回值:共享内存创建成功就返回该共享内存的shmid,失败就返回-1

key和shmid都是标识共享内存的唯一性字段,不过key是用户自定义的,用于让内核区分shm唯一性的,用户不能通过key进行对shm管理,而shmid是有内核返回的一个值,是让用户对共享内存进行管理的id值

(二)删除共享内存

  • 查看所有的共享内存:
ipcs -m

  • 利用指令删除共享内存:
ipcrm -m shmid

  • 代码删除共享内存:

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删除共享内存段

ps.删除共享内存关心第三个参数,可以直接把他设置为nullptr

(三)将共享内存连接到进程地址空间

shmat函数

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

(四)将共享内存从进程地址空间脱离

#功能:将共享内存段与当前进程脱离
#原型int shmdt(const void *shmaddr);
#参数shmaddr: 由shmat所返回的指针
#返回值:成功返回0;失败返回-1

注意:将共享内存段与当前进程脱离不等于删除共享内存段


补充:共享内存不具有进程之间的同步机制,假设一个进程负责读,一个进程负责写,就算共享内存中没有写入数据,读进程还是会一直读,这就可能会发生写进程才写了一半的数据就被读走了,造成数据不一致问题。我们可以利用管道解决这个问题,因为管道具有同步机制,我们让写端写完以后,通过管道传输信号,只有读端通过管道接受到信号以后,才会进行对共享内存的读取

(向管道中写的信号是什么不重要,只要向共享内存中写后,向管道中发送信息,在接收到管道信号后,才读取共享内存的内容,这样就可以让共享内存也存在向管道一样的同步机制,写端写一条,读端读一条)

代码完整使用

shm.hpp

#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>#define Creater 1
#define User 2
const char *pathname = "/home/zyq/mydir/dir/Shm";
int proj_id = 0x666;class Shm
{
private:key_t GetComkey(){key_t key = ftok(_pathname, proj_id);if (key < 0){perror("ftok");return -1;}return key;}int GetShmid(key_t key, int size, int flag){int shmid = shmget(_key, size, flag);if (shmid < 0)perror("shmget");return shmid;}void AttachShm(){_addrshm = shmat(_shmid, nullptr, 0);if (_addrshm == nullptr){perror("shmat");}}void DetachShm(){if (_addrshm != nullptr){int n = shmdt(_addrshm);if (n < 0)perror("shmdt");}}public:Shm(const char *pathname, int proj_id, int who): _pathname(pathname), _proj_id(proj_id), _who(who), _addrshm(nullptr){_key = GetComkey();if (who == Creater){GetCreatershmid();}else{GetUsershmid();}//将共享内存连接到进程地址空间AttachShm();std::cout << "_key:" << _key << std::endl;std::cout << "_shmid:" << _shmid << std::endl;}~Shm(){//删除进程地址空间shmctl(_shmid,IPC_RMID,nullptr);//将共享内存脱离进程地址空间DetachShm();}bool GetCreatershmid(){_shmid = GetShmid(_key, 4096, IPC_CREAT | IPC_EXCL|0666 );if (_shmid < 0)return false;else{std::cout << "Create shm done!" << std::endl;return true;}}bool GetUsershmid(){_shmid = GetShmid(_key, 4096, IPC_CREAT|0666);if (_shmid < 0)return false;else{std::cout << "Get shm done!" << std::endl;return true;}}void* Addr(){return _addrshm;}
private:key_t _key;int _shmid;const char *_pathname;int _proj_id;int _who;void *_addrshm;
};

server.cc

#include "shm.hpp"
#include "namedpipe.hpp"
int main()
{// 创建共享内存并连接Shm shm(pathname, proj_id, Creater);char *shmaddr = (char *)shm.Addr();// 创建管道NamedPipe fifo(path, Creater);fifo.OpenforRead();while (true){//读共享内存前先获取唤醒信号std::string str;fifo.ReadNamedPipe(&str);std::cout << "shm content:" << shmaddr << std::endl;sleep(1);}sleep(10);return 0;
}

client.cc

#include"shm.hpp"
#include"namedpipe.hpp"
int main()
{//获取共享内并连接Shm shm(pathname,proj_id,User);char* shmaddr=(char*)shm.Addr();//获取管道NamedPipe fifo(path,User);fifo.OpenforWrite();char ch='A';while(ch<'Z'){shmaddr[ch-'A']=ch;//写完以后,向管道发送唤醒信息//向管道中写的内容不重要,主要是利用管道的同步机制std::cout<<"add "<<ch<<" into shm"<<std::endl;std::string str="WakeupRead";fifo.WriteNamedPipe(str);ch++;sleep(2);}sleep(10);return 0;
}

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

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

相关文章

一文入门交叉编译

前言: 在阅读本文之前&#xff0c;你哦需要了解makefile文件的编写规则&#xff0c;这里我们推荐两篇入门: Makefile 规则-CSDN博客 Makefile 快速入门-CSDN博客 编译定义 编译是指将源代码文件&#xff08;如C/C文件&#xff09;经过预处理、编译、汇编和链接等步骤&#x…

如何从0深入PostgreSQL内核写一个执行器算子?

如何从0深入PostgreSQL内核写一个执行器算子&#xff1f; 大家好&#xff0c;我叫光城&#xff0c;昨天分享了一个主题&#xff1a;如何从0深入PostgreSQL内核写一个执行器算子&#xff1f;今天来总结一下&#xff0c;本篇文章的直播回放可以在b站观看&#xff0c;点击原文或者…

[PS小技能学习]抠图和切图

详情见视频教程&#xff1a;PS小技巧--抠图与切图 今天我们来学习如何使用PS对表情包合辑进行抠图和裁剪保存 1、首先&#xff0c;将图片导入&#xff0c;双击图层新建一个图层 2、然后点击工具栏的魔棒工具&#xff0c;再点击顶部菜单栏的添加到选区 3、点击图片的空白区域即…

IMU状态预积分功能实现与测试

IMU状态预积分功能实现与测试 前言实现IMU状态预积分类测试程序验证预积分与直接积分的效果结果 前言 预积分&#xff1a;是一种十分常见的IMU数据处理方法。 与传统的IMU运动学积分不同&#xff0c;预积分可以将一段时间内的IMU测量数据累积&#xff0c;建立预积分测量&#…

两院院士泌尿外科专家吴阶平教授

吴阶平&#xff08;1917-2011&#xff09;&#xff0c;男&#xff0c;江苏常州人&#xff0c;1933年天津汇文中学毕业&#xff0c;保送到北平燕京大学医预科&#xff0c;1937年毕业于北平燕京大学获理学士学位&#xff0c;1942年毕业于北平协和医学院获医学博士学位&#xff0c…

银行卡归属地查询API接口快速对接

银行卡归属地查询API接口指的是通过银行卡号查询该银行卡详细信息&#xff0c;包括银行卡名称、卡种、卡品牌、发卡行、编号以及归属地等信息&#xff0c;支持一千多家银行返回归属地信息&#xff0c;那么银行卡归属地查询API接口如何快速对接呢&#xff1f; 首先找到有做银行…

SpringBoot集成Kafka开发

4.SpringBoot集成Kafka开发 4.1 创建项目 4.2 配置文件 application.yml spring:application:name: spring-boot-01-kafka-basekafka:bootstrap-servers: 192.168.2.118:90924.3 创建生产者 package com.zzc.producer;import jakarta.annotation.Resource; import org.spri…

Thread类及常见方法

目录 1.Thread类概念 2.Thread的常见构造方法 3.Thread的几个常见属性 4.启动一个线程—start( ) 5.中断一个线程 1.使用自定义的变量来作为标志位 2.使用interrupt() 3.观察标志位是否被清除 6.等待一个线程-join() 7.获取当前线程引用 8.休眠当前线程 1.Thread类概…

GitHub Copilot 简单使用

因为公司安全原因&#xff0c;并不允许在工作中使用GitHub Copilot&#xff0c;所以&#xff0c;一直没怎么使用。最近因为有一些其它任务&#xff0c;所以&#xff0c;试用了一下&#xff0c;感觉还是很不错的。&#xff08;主要是C和Python编程&#xff09; 一&#xff1a;常…

探索洗牌算法的魅力与杨辉三角的奥秘:顺序表的实际运用

目录 目录 前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; 洗牌算法 准备工作 买一副牌 洗牌 发牌 测试整体 &#x1f3af;&#x1f3af;很重要的一点 杨辉三角 总结 前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; Hello, Hello~ …

06_电子设计教程基础篇(学习视频推荐)

文章目录 前言一、基础视频1、电路原理3、模电4、高频电子线路5、电力电子技术6、数学物理方法7、电磁场与电磁波8、信号系统9、自动控制原理10、通信原理11、单片机原理 二、科普视频1、工科男孙老师2、达尔闻3、爱上半导体4、华秋商城5、JT硬件乐趣6、洋桃电子 三、教学视频1…

分布式与一致性协议之Raft算法与一致哈希算法(一)

Raft算法 Raft与一致性 有很多人把Raft算法当成一致性算法&#xff0c;其实它不是一致性算法而是共识算法&#xff0c;是一个Multi-Paxos算法&#xff0c;实现的是如何就一系列值达成共识。并且&#xff0c;Raft算法能容忍少数节点的故障。虽然Raft算法能实现强一致性&#x…

相机知识的补充

一&#xff1a;镜头 1.1MP的概念 相机中MP的意思是指百万像素。MP是mega pixel的缩写。mega意为一百万&#xff0c;mega pixel 指意为100万像素。“像素”是相机感光器件上的感光最小单位。就像是光学相机的感光胶片的银粒一样&#xff0c;记忆在数码相机的“胶片”&#xff…

如何使用Go语言进行并发安全的数据访问?

文章目录 并发安全问题的原因解决方案1. 使用互斥锁&#xff08;Mutex&#xff09;示例代码&#xff1a; 2. 使用原子操作&#xff08;Atomic Operations&#xff09;示例代码&#xff1a; 3. 使用通道&#xff08;Channels&#xff09; 在Go语言中&#xff0c;进行并发编程是常…

buuctf-misc-23.FLAG

23.FLAG 题目&#xff1a;stegsolve得出PK-zip文件&#xff0c;改后缀名为zip,解压后查看文件类型为ELF 使用kali-strings或者ida获取flag 点击Save Bin将其另存为一个zip文件 而后解压我们另存的这个1234.zip文件后&#xff0c;可以得到 我们用ida打开它&#xff0c;打开后就…

《QT实用小工具·五十》动态增删数据与平滑缩放移动的折线图

1、概述 源码放在文章末尾 该项目实现了带动画、带交互的折线图&#xff0c;包含如下特点&#xff1a; 动态增删数值 自适应显示坐标轴数值 鼠标悬浮显示十字对准线 鼠标靠近点自动贴附 支持直线与平滑曲线效果 自定义点的显示类型与大小 自适应点的数值显示位置 根据指定锚点…

stm32f103c8t6学习笔记(学习B站up江科大自化协)-PWR电源控制

PWR简介 PVD可用在电池供电或安全要求比较高的设备&#xff0c;如果供电电压在逐渐下降&#xff0c;在电压过低的情况下可能会导致内外电路出现不确定的错误。为了避免不必要的错误&#xff0c;可以在电源电压过低的情况下&#xff0c;提前发出警告并关闭较为危险的设备 关闭的…

Java发送请求-http+https的

第一步&#xff1a;建议ssl连接对象&#xff0c;信任所有证书 第二步&#xff1a;代码同时支持httphttps 引入源码类 是一个注册器 引入这个类&#xff0c;和它的方法create 注册器&#xff0c;所以对http和https都进行注册&#xff0c;参数为id和item&#xff0c;其中http的…

【C++题解】1039. 求三个数的最大数

问题&#xff1a;1039. 求三个数的最大数 类型&#xff1a;多分支结构 题目描述&#xff1a; 已知有三个不等的数&#xff0c;将其中的最大数找出来。 输入&#xff1a; 输入只有一行&#xff0c;包括3个整数。之间用一个空格分开。 输出&#xff1a; 输出只有一行&#…

uni-app scroll-view隐藏滚动条的小细节 兼容主流浏览器

开端 想写个横向滚动的列表适配浏览器&#xff0c;主要就是隐藏一下滚动条在手机上美观一点。 但是使用uni-app官方文档建议的::-webkit-scrollbar在目标标签时发现没生效。 .scroll-view_H::-webkit-scrollbar{display: none; }解决 F12看了一下&#xff0c;原来编译到浏览…