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://www.mzph.cn/news/698052.shtml

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

相关文章

图纸透明加密:保护机械图纸安全的新方法

随着信息技术的不断发展&#xff0c;机械制造行业对于图纸安全的需求越来越高。机械图纸是企业的核心竞争力之一&#xff0c;泄露可能导致严重的商业损失和技术风险。为了解决这一问题&#xff0c;图纸透明加密成为了一种新的保护机械图纸安全的方法。本文将介绍图纸透明加密的…

面试字节跳动,我被怼的好狠,怎一个惨字了得

人们都说&#xff0c;这个世界上有两种人注定单身&#xff0c;一种是太优秀的&#xff0c;另一种是太平凡的。 我一听呀&#xff1f;那我这岂不是就不优秀了吗&#xff0c;于是毅然决然和女朋友分了手。 人们都说&#xff0c;互联网寒冬来了&#xff0c;这个时候还在大面积招人…

从零开始手写mmo游戏从框架到爆炸(二十一)— 战斗系统二

导航&#xff1a;从零开始手写mmo游戏从框架到爆炸&#xff08;零&#xff09;—— 导航-CSDN博客 上一章&#xff08;从零开始手写mmo游戏从框架到爆炸&#xff08;二十&#xff09;— 战斗系统一-CSDN博客&#xff09;我们只是完成了基本的战斗&#xff0c;速度属性并没有…

R语言数据分析(三)

R语言数据分析&#xff08;三&#xff09; 文章目录 R语言数据分析&#xff08;三&#xff09;一、可视化步骤1.1 创建ggplot1.2 添加美学和图层1.3 简化代码 二、可视化分布2.1 分类变量2.2 数值变量 三、可视化关系3.1 数值变量和分类变量3.2 两个分类变量3.3 两个数值变量3.…

虚拟机器centos7无法识别yum 命令异常处理笔记

问题现象 启动虚拟机后执行ipconfig 提示未找到该命令,然后执行yum install -y net-tools提示 curl#6 - "Could not resolve host: mirrorlist.centos.org; 未知的错误"的错误 [roothaqdoop~]# ifconfig -bash: ifconfig: 未找到命令 [roothadoop~]# yum install …

超级抽象的前端2

vue3的调用方法失败的原因 function validateConfirm(rule, value, callback) {if (value ! form.password) {callback(new Error(两次输入的密码不一致))} else {callback()}function showAgreement() {dialogVisible.value true}function submitForm() {// 这里是提交表单的…

逆推求期望DP

我的开始的想法&#xff1a;状态设置 dp[i][j] 为玩了 i 个职业 j 个阵营的期望天数&#xff0c;初始值很好解决 dp[1][1]1 &#xff0c;但是有个问题&#xff0c;每对 (i,j) 除了边界那里&#xff0c;会由三个地方来决定这一个(i-1,j-1)(i,j-1)(i-1,j)&#xff0c;所以&#x…

【spring】 ApplicationListener的使用及原理简析

文章目录 使用示例&#xff1a;原理简析&#xff1a; 前言&#xff1a;ApplicationListener 是spring提供的一个监听器&#xff0c;它可以实现一个简单的发布-订阅功能&#xff0c;用有点外行但最简单通俗的话来解释&#xff1a;监听到主业务在执行到了某个节点之后&#xff0c…

【ACM出版】第五届计算机信息和大数据应用国际学术会议(CIBDA 2024)

第五届计算机信息和大数据应用国际学术会议&#xff08;CIBDA 2024&#xff09; 2024 5th International Conference on Computer Information and Big Data Applications 重要信息 大会官网&#xff1a;www.ic-cibda.org 大会时间&#xff1a;2024年3月22-24日 大会地点&#…

Atcoder ABC341 A-D题解

比赛链接:ABC341 Problem A: 先签个到。 #include <bits/stdc.h> using namespace std; int main() {int n;cin>>n;for(int i0;i<n;i)cout<<"10"<<endl;cout<<"1"<<endl;return 0; } Problem B: 继续签。 #i…

week04day03(爬虫 beautifulsoup4、)

一. 使用bs4解析网页 下载bs4 - pip install beautifulsoup4 使用的时候 import bs4专门用于解析网页的第三方库 在使用bs4的时候往往会依赖另一个库lxml pip install lxml 网页代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><…

【Python笔记-设计模式】对象池模式

一、说明 用于管理对象的生命周期&#xff0c;重用已经创建的对象&#xff0c;从而减少资源消耗和创建对象的开销 (一) 解决问题 主要解决频繁创建和销毁对象所带来的性能开销问题。如数据库连接、线程管理、网络连接等&#xff0c;对象的创建和销毁成本相对较高&#xff0c…

IC卡批量加密软件使用

IC卡出厂是默认的密码FFFFFFFFFFFF空白卡&#xff0c;IC卡在门禁、电梯、食堂消费、洗浴一卡通等系统上使用前是需要初始化的&#xff0c;即加密的同时写入基础数据。 为什么要用批量加密软件呢&#xff0c;以为需要加密的卡有几百张&#xff0c;几千张&#xff0c;数量比较多&…

shiro 整合 springmvc 实战及源码详解

序言 前面我们学习了如下内容&#xff1a; 5 分钟入门 shiro 安全框架实战笔记 shiro 整合 spring 实战及源码详解 相信大家对于 shiro 已经有了最基本的认识&#xff0c;这一节我们一起来学习写如何将 shiro 与 springmvc 进行整合。 spring mvc 整合源码 maven 依赖 版…

水务界的“数字蝶变”:水务公司重构自我,开启智慧供水新时代

历经六十余载的稳健前行&#xff0c;某水务公司已发展成为国有一档企业中的供水行业佼佼者&#xff0c;不仅主营业务突出&#xff0c;更拥有完善的产业链条。然而&#xff0c;面对供水业务24小时连续作业的高要求&#xff0c;以及业务管理需求的日益复杂化&#xff0c;公司意识…

【Django开发】0到1开发美多shop项目:Celery短信和用户注册。全md文档笔记(附代码,已分享)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论django商城项目开发相关知识。本项目利用Django框架开发一套前后端不分离的商城项目&#xff08;4.0版本&#xff09;含代码和文档。功能包括前后端不分离&#xff0c;方便SEO。采用Django Jinja2模板引擎 Vue.js实现…

网页403错误(Spring Security报异常 Encoded password does not look like BCrypt)

这个错误通常表现为"403 Forbidden"或"HTTP Status 403"&#xff0c;它指的是访问资源被服务器理解但拒绝授权。换句话说&#xff0c;服务器可以理解你请求看到的页面&#xff0c;但它拒绝给你权限。 也就是说很可能测试给定的参数有问题&#xff0c;后端…

学习Redis基础篇

1.初识Redis 1.认识NoSQL 2.认识Redis 3.连接redis命令 4.数据结构的介绍 5.通用命令 2.数据类型 1.String类型 常见命令&#xff1a;例子&#xff1a;set key value

Vue3实现页面顶部进度条

Vue3页面增加进度条 新建进度条组件新建bar.ts导航守卫中使用 Vue3项目使用导航守卫给页面增加进度条 新建进度条组件 loadingBar.vue <template><div class"wraps"><div ref"bar" class"bar"></div></div> <…

VSCODE上使用python_Django_创建最小项目

接上篇 https://blog.csdn.net/weixin_44741835/article/details/136135996?csdn_share_tail%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22136135996%22%2C%22source%22%3A%22weixin_44741835%22%7D VSCODE官网&#xff1a; Editing Python …