【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.rhkb.cn/news/322782.html

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

相关文章

C语言二分查找的区间问题

概念 什么是二分查找呢&#xff1f; 二分查找&#xff1a;在有序数组中查找某一特定元素的搜索算法。 二分查找又称折半查找&#xff0c;通过将数组折半&#xff0c;用中间值和查找值作比较&#xff0c;多次使用&#xff0c;直到找到要查找的值。 注意:二分查找的前提是&#…

2024蓝桥杯CTF writeUP--爬虫协议

Dirsearch扫描网站 发现robots.txt文件 访问 直接去最后一个接口 到手

Android 系统版本与SDK API对应关系-2024.5

官网地址&#xff1a;https://developer.android.google.cn/tools/releases/platforms?hlth

深入理解分布式事务⑨ ---->MySQL 事务的实现原理 之 MySQL 中的XA 事务(基本原理、流程分析、事务语法、简单例子演示)详解

目录 MySQL 事务的实现原理 之 MySQL 中的XA 事务&#xff08;基本原理、流程分析、事务语法、简单例子演示&#xff09;详解MySQL 中的 XA 事务1、XA 事务的基本原理1-1&#xff1a;XA 事务模型图&#xff1a;1-2&#xff1a;XA 事务模型的两阶段提交操作&#xff1a;Prepare …

GORM数据库连接池对接Prometheus

一、背景与介绍 Golang的database/sql包定了关于操作数据库的相关接口&#xff0c;但是没有去做对应数据库的实现。这些实现是预留给开发者或者对应厂商进行实现的。 其中让我比较关注的是Golang的sql包有没有实现连接池pool的机制呢? 毕竟Golang是静态语言&#xff0c;类似J…

如何理解vlanif接口无法up的原因?直连不通(PVID问题)?如何排查?

目录 案列一&#xff1a;如何理解vlanif接口无法up的原因&#xff1f; 案例二&#xff1a;vlan接口正常up&#xff0c;同网段&#xff0c;无法ping通&#xff1f;&#xff08;PVID&#xff09; 原因一&#xff1a;pvid&#xff08;native vlan&#xff09;两端不一致——帧的…

Baidu Comate:智能编码,编程效率的革新者

文章目录 一、何为智能编码助手&#xff1f;二、Baidu Comate智能编码助手简介三、Baidu Comate注册四、Baidu Comate体验Comate插件功能1.注释生成代码2.函数注释生成3.行间注释生成4.生成代码解释5. 调优建议 五、插件功能的使用体验感受和建议 &#x1f6a9;结语 一、何为智…

文件快递柜-免费开源-FileCodeBox

像拿快递一样取文件 什么FileCodeBox FileCodeBox 中文名是 文件快递柜&#xff0c;取文件像取快递一样&#xff0c;支持通过匿名口令分享文本&#xff0c;文件。 很多时候&#xff0c;我们都想将一些文件或文本传送给别人&#xff0c;或者跨端传递一些信息&#xff0c;但是我…

QT+网络调试助手+TCP客户端

一、网络调试助手UI界面 编程主要思路&#xff1a; 首先将水平的控件 水平布局 &#xff0c;然后相对垂直的控件 垂直布局 &#xff0c;哪怕是底下的groupBox也需要和里面的内容 水平布局&#xff0c;然后最后框选全部 栅格布局。如果需要界面自适应窗口大小&#xff0c…

品牌舆情监测工作要怎么做?

一个负面舆论的传播&#xff0c;可能在短时间内对企业品牌形象造成巨大损害&#xff0c;甚至引发舆情危机。因此&#xff0c;如何有效地进行品牌舆情监测&#xff0c;成为企业不可忽视的问题。伯乐网络传媒多年网络公关、舆情监测经验&#xff0c;今天就来给大家分享一下。 一、…

探索全新商业模式:循环购的奥秘

你是否曾经遇到过这样的疑问&#xff1a;为何有的商家会推出“消费1000送2000”的优惠活动&#xff1f;每天还有钱可以领取&#xff0c;甚至还能提现&#xff1f;这背后究竟隐藏着怎样的商业逻辑&#xff1f;今天&#xff0c;作为你们的私域电商顾问&#xff0c;我将带大家深入…

Android 高版本实现沉浸式状态栏

目前实现的android高版本沉浸式状态栏分为两类&#xff1a; 1、是纯透明状态栏&#xff1b; 2、是纯透明状态栏&#xff0c;但是状态栏字体是黑色&#xff1b; 将状态栏的代码封装到BaseActivity中更方便使用&#xff1a; BaseActivity: public abstract class BaseActivit…

腾讯游戏海外扩张,增持芬兰游戏开发商股份持股比例增至14.8%

易采游戏网5月8日消息&#xff0c;近日腾讯再次出手&#xff0c;大幅增持了芬兰知名游戏开发商Remedy Entertainment的股份&#xff0c;持股比例猛增至14.8%。这一举动引起了业界和投资者的广泛关注。 据了解&#xff0c;腾讯此次增持是在2024年4月24日完成的。根据芬兰法律规…

Web实操(6),基础知识学习(24~)

1.[ZJCTF 2019]NiZhuanSiWei1 &#xff08;1&#xff09;进入环境后看到一篇php代码&#xff0c;开始我简单的以为是一题常规的php伪协议&#xff0c;多次试错后发现它并没有那么简单&#xff0c;它包含了基础的文件包含&#xff0c;伪协议还有反序列化 &#xff08;2&#x…

ES与关系数据库的同步练习(hotel_admin)

目录 1 es与数据库同步的方法2 实践2.1 任务介绍2.2 MQ方面操作2.2.1 声明交换机队列并且绑定2.2.2 hotel_admin端web层设置mq发送消息2.3 hotel_demo端监听接受消息并执行es操作 1 es与数据库同步的方法 方式一&#xff1a;同步调用 优点&#xff1a;实现简单&#xff0c;粗…

Python爬虫基础知识学习(以爬取某二手房数据、某博数据与某红薯(书)评论数据为例)

一、爬虫基础流程 爬虫的过程模块化&#xff0c;基本上可以归纳为以下几个步骤&#xff1a; 1、分析网页URL&#xff1a;打开你想要爬取数据的网站&#xff0c;然后寻找真实的页面数据URL地址&#xff1b; 2、请求网页数据&#xff1a;模拟请求网页数据&#xff0c;这里我们介…

Qt QImageReader类介绍

1.简介 QImageReader 是用于读取图像文件的类。它提供了读取不同图像格式的功能&#xff0c;包括但不限于 PNG、JPEG、BMP 等。QImageReader 可以用于文件&#xff0c;也可以用于任何 QIODevice&#xff0c;如 QByteArray &#xff0c;这使得它非常灵活。 QImageReader 是一个…

不走寻常路!酷开科技不断升级酷开系统满足消费者日益增长的需求

在科技日新月异的今天&#xff0c;人们对生活品质的要求越来越高。为此&#xff0c;酷开科技不断升级酷开系统&#xff0c;以满足消费者日益增长的需求。为了让消费者体验更好的服务&#xff0c;在酷开系统中设立了酷开会员&#xff0c;满足消费者的更多需求。丰富的特权和定制…

mysql其它补充

exist和in的区别 exists 用于对外表记录做筛选。 exists 会遍历外表&#xff0c;将外查询表的每一行&#xff0c;代入内查询进行判断。 当 exists 里的条件语句能够返回记录行时&#xff0c;条件就为真&#xff0c;返回外表当前记录。反之如果 exists 里的条件语句不能返回记…

46. UE5 RPG 增加角色受击反馈

在前面的文章中&#xff0c;我们实现了对敌人的属性的初始化&#xff0c;现在敌人也拥有的自己的属性值&#xff0c;技能击中敌人后&#xff0c;也能够实现血量的减少。 现在还需要的就是在技能击中敌人后&#xff0c;需要敌人进行一些击中反馈&#xff0c;比如敌人被技能击中后…