system V 共享内存

1.共享内存的原理

要理解共享内存的原理,首先我们得记起进程间通信的前提:必须让不同的进程看到同一份资源(必须由OS提供)

我们都知道进程都会有自己的进程地址空间,然后都会通过页表与物理内存进行映射,如果要进行进程间通信的话,那肯定不止一个进程,最少也得两个进程,所以我们就可以让这两个进程对应的进程地址空间都通过页表映射到同一块物理内存。如果细分步骤的话,那就需要进行如下几步:

①申请物理内存

②把申请的物理内存的地址填充到页表中,然后把在进程地址空间里面对应这块空间的共享区的起始地址返回给上层(通过我们前面学的动静态库的理解)

此时两个进程都共享同一块物理内存,这里就达到了我们进程间通信的前提条件了。然后就可以进程进程间通信了,通信后。

③如果我们要释放这块共享内存的话,就可以通过清空页表中映射到该处物理内存的内容,也就是清除掉进程与这块物理内存之间的映射关系

④直接释放这块物理内存

可是,系统里不仅仅只有这两个进程,如果有非常多组的进程共享多块不同的共享内存,也就是说操作系统里会有多组不同的进程同时使用相同的一块共享内存,所以操作系统一定会允许系统同时存在多个共享内存,那既然有这么多的共享内存,操作系统要不要管理起来呢?哪些进程在用哪些共享内存,操作系统是不是得知道呢?答案当然是要的,既然要管理起来,那么就需要先描述在组织。所以为了方便操作系统管理,我们的共享内存会有一个struct shm这样的数据结构对象来代表这块共享内存,这个结构体对象里面会有这块共享内存的属性,比如什么时候创建的,已经用了多少内存,还剩多少内存,到底有多少的指针指向这块内存呢等等。如果申请一块共享内存,那就创建一个对应的struct shm结构体对象,如果释放了,就把这个结构体对象删除了,所以对共享内存的管理就转变成了对struct shm的增删查改。

理解:

1.共享内存,也要被操作系统管理

2.如何保证第二个之后的参与通信的进程,看到的就是同一个共享内存呢?要保证这一点那么就需要要求共享内存必须要有唯一的标识,那么如何做到呢?怎么给另一个进程呢?

2.快速认识系统接口

①int shmget(key_t key, size_t size, int shmflg);

这里的这个key是什么意思呢?我们如何理解呢?那我们拿进程来说,就是进程创建了共享内存,另一个进程怎么会知道呢?于是就约定了一个共享数字,这个数字是多少并不重要,这个数字只需要满足唯一性,每一个共享内存对应的唯一的数字即可,那么我们就可以规定好是多少,规定好之后就相当于是共享内存的唯一标识,当我们创建好共享内存的时候,我们就可以把这个数字写到对应的共享内存的内核数据结构对象里面,然后因为我们规定了,所以写代码期间我们通信双方就都知道这个数字,所以我们此时创建的时候就可以让系统帮我们去找共享内存对应的那个数字跟我们传进去的key是一样的就可以了,比如说key是1234,那么就可以保证通信双方看到的是同一块共享内存,但是呢,随意去创建数字是容易产生冲突的,所以不建议去随便用一个数字,所以我们建议用这个函数,key_t ftok(const char *pathname, int proj_id);

这个函数的作用就是将pathname和一个proj_id转化成一个共享内存的key.这只是一个算法函数,并不会对系统做任何操作,比如说通过一些散列方式形成特定的key,所以将来使用的是同一个pathname,同一个proj_id,再使用同样的一套算法,我们就一定能形成同样的key,那么在代码中自然也就能通过形成同样的key找到同一块共享内存。所以为了我们想要判断是不是同一块共享内存,我们就需要判断是不是同一个pathname,同一个proj_id,同一套算法.

下面我们来聊聊shmflag这个参数,这个参数能够支撑我们既然创建又能获取共享内存,shmflag可以传的两个重点选项是IPC_CREAT(代表shm不存在就创建,存在就获取并返回),IPC_EXCL(不单独使用,通常和IPC_CREAT一起使用,代表shm不存在就创建,存在就出错返回,保证创建的共享内存是全新的),很显然是两个宏,我们前面学过可以通过位图来传递多个标志位,除了传上面的参数,还可以传权限,通过传权限就能修改我们需要设置的权限。

而创建共享内存之后,如果我们要删除共享内存就需要使用命令:ipcrm -m shmid 删除指定的共享内存。

共享内存的大小,强烈建议设置成为n*4096.

②void *shmat(int shmid, const void *shmaddr, int shmflg);

③int shmdt(const void *shmaddr);

④int shmctl(int shmid, int cmd, struct shmid_ds *buf);

3.直接编写代码

3.1创建key值

comm.hpp代码:

#pragma once#include<iostream>#include<string>const std::string pathname = "/home/sunwenchao/mylesson/lesson30";
const int proj_id = 0x11223344;

server.cc代码:

#include<iostream>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<cstring>
#include"comm.hpp"
int main()
{key_t key = ftok(pathname.c_str(),proj_id);if(key<0){std::cerr<<"errno: "<<errno <<", errstring: "<<strerror(errno)<<std::endl;return  1;}std::cout<<"key: "<<key<<std::endl;return 0;
}

client.cc代码:

#include<iostream>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<cstring>
#include"comm.hpp"
int main()
{key_t key = ftok(pathname.c_str(),proj_id);if(key<0){std::cerr<<"errno: "<<errno <<", errstring: "<<strerror(errno)<<std::endl;return  1;}std::cout<<"key: "<<key<<std::endl;return 0;
}

Makefile代码:

.PHONY:all
all:server clientclient:client.ccg++ -o $@ $^ -std=c++11server:server.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f server client .fifo

运行结果:

3.2创建共享内存

int shmget(key_t key, size_t size, int shmflg);接口测试:

comm.hpp代码:

#pragma once#include<iostream>
#include<cstdlib>
#include<string>const std::string pathname = "/home/sunwenchao/mylesson/lesson30";
const int proj_id = 0x11223344;
const int size = 4096;key_t GetKey()
{key_t key = ftok(pathname.c_str(),proj_id);if(key<0){std::cerr<<"errno: "<<errno <<", errstring: "<<strerror(errno)<<std::endl;exit(1);}std::cout<<"key: "<<key<<std::endl;return key;
}

client.cc代码:

#include<iostream>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<cstring>
#include"comm.hpp"
int main()
{key_t key = GetKey();int shmid = shmget(key,size,IPC_CREAT|IPC_EXCL);if(shmid<0){std::cerr<<"errno: "<<errno <<", errstring: "<<strerror(errno)<<std::endl;return 1;}std::cout<<"shmid: "<<shmid<<std::endl;return 0;
}

server.cc代码:

#include<iostream>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<cstring>
#include"comm.hpp"
int main()
{key_t key = GetKey();int shmid = shmget(key,size,IPC_CREAT|IPC_EXCL);if(shmid<0){std::cerr<<"errno: "<<errno <<", errstring: "<<strerror(errno)<<std::endl;return 1;}std::cout<<"shmid: "<<shmid<<std::endl;return 0;
}

Makefile代码:

.PHONY:all
all:server clientclient:client.ccg++ -o $@ $^ -std=c++11server:server.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f server client .fifo

运行结果:

查看共享内存属性:

此时进程已经退出了,当我们再次运行时:

发现他报错了。说明确实是不存在就创建了,如果存在了就会报错返回。而这里的共享内存跟我们之前讲的文件并不一样,文件是当进程退出了会直接关闭的,但是这里的共享内存想要释放除了重启系统,否则是需要我们用代码手动释放的。

所以我们就可以得出一个结论:共享内存(IPC资源)的生命周期是随内核的!

这些是标准是规定,实现的时候与进程是没有关系的。

3.3进程与共享内存挂接

void *shmat(int shmid, const void *shmaddr, int shmflg);这个接口是为了将shm映射到进程的地址空间当中,让该进程与共享内存挂接。

我们用如下代码来进行测试:

#include<iostream>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>
#include<cstring>
#include"comm.hpp"
int main()
{key_t key = GetKey();int shmid = shmget(key,size,IPC_CREAT|IPC_EXCL|0644);if(shmid<0){std::cerr<<"errno: "<<errno <<", errstring: "<<strerror(errno)<<std::endl;return 1;}sleep(5);std::cout<<"shmid: "<<shmid<<std::endl;std::cout<<"开始将shm映射到进程的地址空间中 "<<std::endl;char* s = (char*)shmat(shmid,nullptr,0);sleep(5);return 0;
}

测试结果:

与此同时我们在另一个SSH渠道用指令while :; do ipcs -m; sleep 1;done来进行检测发现:

 

3.4进程与共享内存断开关联

当在我们的server可执行程序运行过程中,从刚开始没有共享内存,然后通过shmget创建共享内存,然后再通过执行shmat函数让共享内存和当前进程产生关联,至此nattch从0变成了1,然后等到进程退出的时候,nattch又从1变成了0,所以我们这里验证了,nattch是一个计数器,用来对与共享内存产生关联的数量进行计数的。

当然除了进程退出的时候可以让nattch进行--,我们也希望可以不让进程退出的时候实现进程与共享内存直接断开关联,所以我们就可以使用函数int shmdt(const void *shmaddr);我们可以把shmat函数的返回值,也就是共享内存的起始地址作为参数传给该接口,也就是用如下代码:

#include<iostream>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>
#include<cstring>
#include"comm.hpp"
int main()
{key_t key = GetKey();sleep(3);int shmid = shmget(key,size,IPC_CREAT|IPC_EXCL|0644);if(shmid<0){std::cerr<<"errno: "<<errno <<", errstring: "<<strerror(errno)<<std::endl;return 1;}sleep(5);std::cout<<"shmid: "<<shmid<<std::endl;std::cout<<"开始将shm映射到进程的地址空间中 "<<std::endl;char* s = (char*)shmat(shmid,nullptr,0);sleep(5);std::cout<<"开始将shm从进程的地址空间中移除 "<<std::endl;shmdt(s);sleep(10);return 0;
}

运行结果:

我们发现当执行shmdt接口的时候,进程还没退出的时候,nattch从1变成了0.所以我们这里就实现了将shm从进程的地址空间中移除,也就是让进程与共享内存进行断开关联。

3.5从操作系统删除共享内存

 那么我们删除如何去删除我们的创建的共享内存呢?因为我们创建之后每次如果不通过命令ipcrm -m shmid来删除共享内存的话就会引起

这样重复创建的报错,所以我们需要将其用完了共享内存之后进行删除,所以我们就需要使用shmctl这个接口来进行删除共享内存:

#include<iostream>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>
#include<cstring>
#include"comm.hpp"
int main()
{key_t key = GetKey();sleep(3);int shmid = shmget(key,size,IPC_CREAT|IPC_EXCL|0644);if(shmid<0){std::cerr<<"errno: "<<errno <<", errstring: "<<strerror(errno)<<std::endl;return 1;}sleep(5);std::cout<<"shmid: "<<shmid<<std::endl;std::cout<<"开始将shm映射到进程的地址空间中 "<<std::endl;char* s = (char*)shmat(shmid,nullptr,0);sleep(5);std::cout<<"开始将shm从进程的地址空间中移除 "<<std::endl;shmdt(s);sleep(5);shmctl(shmid,IPC_RMID,nullptr);std::cout<<"开始将shm从OS中删除 "<<std::endl;sleep(10);return 0;
}

运行结果:

4.总结4个接口整体完整代码

comm.hpp

#pragma once#include<iostream>
#include<cstdlib>
#include<string>const std::string pathname = "/home/sunwenchao/mylesson/lesson30";
const int proj_id = 0x11223344;
const int size = 4096;key_t GetKey()
{key_t key = ftok(pathname.c_str(),proj_id);if(key<0){std::cerr<<"errno: "<<errno <<", errstring: "<<strerror(errno)<<std::endl;exit(1);}std::cout<<"key: "<<key<<std::endl;return key;
}std::string ToHex(int id)
{char buffer[1024];snprintf(buffer,sizeof(buffer),"0x%x",id);return buffer;
}int CreateShmHelper(key_t key,int flag)
{int shmid = shmget(key,size,flag);if(shmid<0){std::cerr<<"errno: "<<errno <<", errstring: "<<strerror(errno)<<std::endl;exit(2);}return shmid;
}int CreateShm(key_t key)
{return CreateShmHelper(key,IPC_CREAT|IPC_EXCL|0644);
}int GetShm(key_t key)
{return CreateShmHelper(key,IPC_CREAT);}

Makefile

.PHONY:all
all:server clientclient:client.ccg++ -o $@ $^ -std=c++11server:server.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f server client .fifo

client.cc

#include<iostream>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>
#include<cstring>
#include"comm.hpp"
int main()
{key_t key = GetKey();int shmid = GetShm(key);char *s = (char*)shmat(shmid,nullptr,0);char c = 'a';for(;c<='z';c++){s[c-'a'] = c;std::cout<<"write: "<<c<<"done"<<std::endl;sleep(6);}shmdt(s);std::cout<<"detach shm done"<<std::endl;return 0;
}

server.cc

#include<iostream>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>
#include<cstring>
#include"comm.hpp"
int main()
{key_t key = GetKey();std::cout<<"key : "<<ToHex(key)<<std::endl;// sleep(3);int shmid = CreateShm(key);// sleep(5);std::cout<<"shmid: "<<shmid<<std::endl;std::cout<<"开始将shm映射到进程的地址空间中 "<<std::endl;char* s = (char*)shmat(shmid,nullptr,0);// sleep(5);while(true){//直接读取std::cout<<"共享内存的内容:"<<s<<std::endl;sleep(1);}std::cout<<"开始将shm从进程的地址空间中移除 "<<std::endl;shmdt(s);// sleep(5);shmctl(shmid,IPC_RMID,nullptr);std::cout<<"开始将shm从OS中删除 "<<std::endl;// sleep(10);return 0;
}

运行结果:

可是我们发现一个进程写一次之后,另一个进行会读很多次,也就是没有看到有同步机制。但是如果我们想要让其有同步机制我们也可以通过管道来进行实现同步机制的。

5.总结共享内存的特点

1.共享内存的通信方式不会提供同步机制,共享内存是直接裸露给所有的使用者的,一定要注意共享内存的使用安全问题

2.共享内存是所有进程间通信速度最快的

3.共享内存可以提供较大的空间 

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

springboot214基于springboot的多媒体素材库的开发与应用

多媒体素材库的设计与实现 摘要 近年来&#xff0c;信息化管理行业的不断兴起&#xff0c;使得人们的日常生活越来越离不开计算机和互联网技术。首先&#xff0c;根据收集到的用户需求分析&#xff0c;对设计系统有一个初步的认识与了解&#xff0c;确定多媒体素材库的总体功…

vue源码分析之nextTick源码分析-逐行逐析-错误分析

nextTick的使用背景 在vue项目中&#xff0c;经常会使用到nextTick这个api&#xff0c;一直在猜想其是怎么实现的&#xff0c;今天有幸研读了下&#xff0c;虽然源码又些许问题&#xff0c;但仍值得借鉴 核心源码解析 判断当前环境使用最合适的API并保存函数 promise 判断…

SD-WAN组网:打造跨国企业无缝网络连接体验

在数字化转型的时代&#xff0c;越来越多的企业迈向国际化&#xff0c;然而&#xff0c;由于自建网络架构的限制和跨域网络的复杂性&#xff0c;企业在不同地理位置的站点之间难以实现高效的数据互通和协作。这就是为什么SD-WAN成为跨国企业组网的理想选择的原因。 跨国企业常见…

你真的了解nsight compute中的Roofline Charts吗?

此文章是之前看nsight-compute官网的时候做的笔记。因为这个记笔记的时候使用的文档记录下来会更直观好看&#xff0c;所以本次的笔记采用了截图的方式。中英文是方便沟通记忆&#xff0c;概念对齐。

HashMap 源码学习-jdk1.8

1、一些常量的定义 这里针对MIN_TREEIFY_CAPACITY 这个值进行解释一下。 java8里面&#xff0c;HashMap 的数据结构是数组 &#xff08;链表或者红黑树&#xff09;&#xff0c;每个数组节点下可能会存在链表和红黑树之间的转换&#xff0c;当同一个索引下面的节点超过8个时…

网安播报 | AI生成代码对组织和软件供应链构成了重大风险

1、AI生成代码对组织和软件供应链构成了重大风险 根据Veracode最新发布的软件安全报告&#xff0c;42%的应用程序和71%的组织中普遍存在软件安全债务&#xff0c;而AI生成代码的激增将导致安全债务问题恶化并对软件供应链构成重大风险。更令人担忧的是&#xff0c;46%的组织持续…

短视频新媒体的福音:视频抽插帧AI效率是人工的100倍以上

进入全民短视频时代&#xff0c;人像视频的拍摄也正在迈向专业化。随着固化审美的瓦解&#xff0c;十级磨皮的网红滤镜被打破&#xff0c;多元化的高级质感成为新的风向标&#xff0c;“美”到每一帧是人们对动态视频提出的更高要求。 目前&#xff0c;大部分手机均可记录主流的…

ABB触摸屏维修工控机显示屏维修CP405 A1/A0

ABB人机界面维修常见故障&#xff1a;黑屏白屏花屏&#xff0c;按触摸屏无反应或反应慢触摸不好&#xff0c;内容错乱&#xff0c;进不了系统界面&#xff0c;无背光背光暗&#xff0c;有背光无字符&#xff0c;不能通信&#xff0c;按键无无反应等均可维修。 此维修检测方法有…

信号信号槽

三、信号槽 概念 信号和槽是两种函数&#xff0c;这是Qt在C基础上新增的特性&#xff0c;类似于其他技术中的回调的概念。 信号槽通过程序员提前设定的“约定”&#xff0c;可以实现对象之间的通信&#xff0c;有两个先决条件。 通信的对象都是在QOBject类中派生出来的。 QOBje…

springboot212球队训练信息管理系统

球队训练信息管理系统设计与实现 摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装球队训练信息管理系统软件…

代理IP为什么会有延迟?

在当今信息高速发展的时代&#xff0c;随着代理IP在数据采集、网络安全和匿名浏览等领域的应用&#xff0c;已成为网络技术中不可或缺的一环。然而&#xff0c;用户在使用代理IP时经常会遇到一个问题——延迟。 那我们要如何解决这个问题呢&#xff1f; 这需要从代理IP的原理说…

第七章 正则表达式

目录 1.1. 概念&#xff1a; 1.2. 基本正则表达式 1.2.1. 常见元字符 1.2.2. POSIX字符类 1.2.3. 示例 1.3. 扩展正则表达式 1.3.1. 概念 1.3.2. 示例 1.1. 概念&#xff1a; 在进行程序设计的过程中&#xff0c;用户会不可避免地遇到处理某些文本的情况。有的时候&am…

android 15

https://android-developers.googleblog.com/2024/02/first-developer-preview-android15.html android 15的预览版出了&#xff0c;这个版本的发布计划大概是这样的&#xff08;大约是今年8月发布最终版本&#xff09; https://developer.android.com/about/versions/15/over…

Day34 线程Thread

文章目录 1.什么是线程1.1 概念1.2 进程和线程的区别1.3 线程资源 2.函数接口2.1 创建线程 : pthread_create2.2 退出线程: pthread_exit2.3 回收线程资源 1.什么是线程 1.1 概念 线程是一个轻量级的进程&#xff0c;为了提高系统的性能引入线程。 在同一个进程中可以创建的多…

【YOLO v5 小目标改进】SPD-Conv

SPD-Conv 提出背景SPD-Conv YOLO v5 小目标改进定义 SPD-Conv导入SpaceToDepth模块修改 .yaml 文件 提出背景 论文&#xff1a;https://arxiv.org/pdf/2208.03641v1.pdf 代码&#xff1a;https://github.com/labsaint/spd-conv 文章提出一个新的卷积神经网络(CNN)构建块&…

echarts多y轴样式重叠问题

1、主要属性设置 yAxis: [{//y轴1nameTextStyle: {align: "right",padding: 0}},{//y轴2nameTextStyle: {align: "left",padding: 0}},{//y轴3axisLabel: {margin: 50},nameTextStyle: {align: "left",padding: [0, 0, 0, 50]},axisPointer: {l…

二轮充电桩协议商用通讯协议

文章目录 一、基础通信协议框架二、命令的定义各数据单位定义&#xff1a; 三、设备上传3.0.1、设备心跳包&#xff08;01 指令&#xff09;3.0.2、设备注册包&#xff08;20 指令&#xff09;3.0.3、设备获取服务器时间&#xff08;22 指令&#xff09;3.1、设备心跳包&#x…

基于springboot+vue的靓车汽车销售网站(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

ChatGPT在数据处理中的应用

ChatGPT在数据处理中的应用 今天的这篇文章&#xff0c;让我不断体会AI的强大&#xff0c;愿人类社会在AI的助力下走向更加灿烂辉煌的明天。 扫描下面二维码注册 ​ 数据处理是贯穿整个数据分析过程的关键步骤&#xff0c;主要是对数据进行各种操作&#xff0c;以达到最终的…

【Git】:标签功能

标签功能 一.标签操作二.推送远程标签 标签 tag &#xff0c;可以简单的理解为是对某次commit的⼀个标识&#xff0c;相当于起了⼀个别名。例如&#xff0c;在项⽬发布某个版本的时候&#xff0c;针对最后⼀次commit起⼀个v1.0这样的标签来标识⾥程碑的意义。这有什么⽤呢&…