Linux_共享内存通信

目录

1、共享内存原理

2、申请共享内存

2.1 ftok 

2.2 测试shmget、ftok 

2.3 查看系统下的共享内存 

3、关联共享内存

3.1 测试shmat

4、释放共享内存 

4.1 测试shmctl 

5、实现共享内存通信 

6、共享内存的特性

结语 


前言:

        在Linux下,有一种进程间通信方式(IPC)名为共享内存,他是IPC中通信最快的方式(通信方式为全双工),因为他直接在物理内存上创建一块区域并且映射在进程的地址空间中,使得进程使用共享内存就如同直接使用动态申请的空间,因此通信过程少了内核的系统调用步骤,以至于相比于其他IPC模式速度更快,不过也正是因为在通信时不受内核管辖,导致共享内存不具备同步互斥机制,因此需要用户手动处理同步互斥问题。

        但是需要注意的是共享内存虽然使用起来如同动态空间,但是他的底层和动态空间不一样,动态空间具有独立性,只限于单个进程内部的访问,而共享内存允许多个无亲缘进程进行通信,因此他和动态空间是有区别的。

1、共享内存原理

        共享内存的目的就是为了进程间通信,而进程间通信的核心观念是让不同的进程看到同一份资源,所以共享内存必须在物理内存上开辟一块空间,并且映射到进程地址空间中的共享区,具体示意图如下:

        但是共享内存的申请和malloc申请是不一样的,因为共享内存要面向所有进程,要做到这一点就必须调用系统接口,所以要进行共享内存通信必须调用系统接口。

2、申请共享内存

        在物理内存上申请共享内存的接口是shmget,该接口介绍如下:

#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);
//key是用户给这段共享内存设置的名字,一个key对应一个共享内存
//size表示申请共享内存的大小
//shmflg表示权限设置,常用的有IPC_CREAT和IPC_EXCL//调用成功返回非负整数表示共享内存的标识码(给系统看的),失败返回-1

        着重介绍shmflg:

        1、当传递的是IPC_CREAT|IPC_EXCL,表示若以当前key值申请的共享内存不存在,则创建一个并返回新共享内存的标识码。若以当前key值申请的共享内存存在则返回-1,表示申请失败。

        2、当传递的是IPC_CREAT,表示若以当前key值申请的共享内存不存在,则创建一个并返回新共享内存的标识码。若以当前key值申请的共享内存存在则返回该共享内存的标识码。

        所以使用IPC_CREAT|IPC_EXCL可以判断一个key对应的共享内存是否存在,即key值是否被用过,当我们想用一段新的共享内存则可以使用IPC_CREAT|IPC_EXCL。

        key值的作用是判断两个进程的共享内存是否为同一个,两个进程所用的key一样说明他们共用同一个共享内存,反之则否,因此可以理解为key值是用户给一段共享内存起的名字,而shmget返回值是系统给这段共享内存起的名字。

2.1 ftok 

        shmget需要用到key值,key的类型虽然是key_t,但是也可以传一个int类型的值给到key,只不过这么做会导致潜在的重名风险,并且key的值需要程序员自己维护,于是系统提供了一个接口ftok,他像是一个算法,可以计算并返回一个key_t类型的值,该接口介绍如下:

#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);
//接收一个路径和一个整形
//成功返回一个key_t类型的值,失败返回-1

         所以两个进程调用ftok时传参是一样的,那么这两个进程就会获得相同的key值,这样两个进程就能看到同一份资源了,也就完成了通信的前提。

2.2 测试shmget、ftok 

         先用代码测试上述接口,测试代码如下:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <iostream>using namespace std;int main()
{const char *pathname = "/home/zh";int proj_id = 12;int size = 4096;key_t key = ftok(pathname, proj_id);if (key < 0){perror("ftok");return -1;}cout << "key值被成功创建,key:" << key << endl;int shmid = shmget(key, size, IPC_CREAT | IPC_EXCL);if (shmid < 0){perror("shmget");return -1;}cout<<"共享内存标识码被成功创建,shmid:"<<shmid<<endl;return 0;
}

        运行结果:

2.3 查看系统下的共享内存 

        共享内存不同于动态申请空间,动态空间的生命周期随进程。但是对于共享内存而言,若用户不主动释放共享内存,则共享内存会一直存活在系统中,他的生命周期随内核,即内核重启才会清理这些共享内存,在Linux下用指令ipcs -m查看当下系统的共享内存,测试如下:

        并且可以通过指令ipcrm -m shmid删除对应的shmid,测试如下:

3、关联共享内存

         上述接口shmget可以申请一块共享内存,但是申请到了不意味着就可以直接使用共享内存进行通信,要进行通信还要关联共享内存,关联共享内存的接口介绍如下:

#include <sys/types.h>
#include <sys/shm.h>//关联共享内存
void *shmat(int shmid, const void *shmaddr, int shmflg);
//shmid表示要关联的共享内存标识码
//shmaddr若不为NULL且shmflg不为SHM_RND,表示将共享内存的地址附加到shmaddr处
//shmaddr若为NULL,则该函数的返回值作为共享内存的地址(通常都设为NULL)
//shmflg表示权限设置,通常设为0表示对共享内存可读可写
//调用成功返回指向共享内存的指针,失败返回值(void*)-1//去关联
int shmdt(const void *shmaddr);
//让调用该函数的进程不再关联该共享内存
//shmaddr表示共享内存的地址

        总的来说,调用shmat关联共享内存后,会拿到一个执行该共享内存的指针,通过该指针就可以对共享内存进行读写操作。

3.1 测试shmat

        因为申请共享内存的代码后续会被重复使用,为了后续更好的测试,所以对申请共享内存的接口进行再一层封装,封装成sharemem.hpp文件,代码如下:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <iostream>
#include <unistd.h>using namespace std;const char *pathname = "/home/zh";
int proj_id = 12;
int size = 4096;key_t getkey()//封装ftok
{key_t key = ftok(pathname, proj_id);if (key < 0){perror("ftok");exit(-1);}cout << "key值被成功创建,key:" << key << endl;return key;
}int getshm(int shmflg)//封装shmget
{key_t key = getkey();int shmid = shmget(key, size, shmflg);if (shmid < 0){perror("shmget");exit(-1);}cout << "共享内存标识码被成功创建,shmid:" << shmid << endl;return shmid;
}int creatnewshm()//只想用最新的共享内存来进程通信
{return getshm(IPC_CREAT|IPC_EXCL|0666);//为了能够观察到变化,所以要保证共享内存的权限
}int getoldshm()//获取一个已经存在的共享内存进行通信
{return getshm(IPC_CREAT);
}

         后续的测试只需要包含该文件即可,测试shmat代码如下:

#include "sharemem.hpp"int main()
{int shmid = creatnewshm();cout<<"申请共享内存成功"<<endl;sleep(2);//观察nattch的值char* poi = (char*)shmat(shmid,NULL,0);cout<<"关联共享内存成功"<<endl;sleep(2);//观察nattch的值return 0;
}

        运行结果:

        其中,右侧nattch表示当前有多少个进程在关联该共享内存,当一个进程关联某个共享内存后,该共享内存的nattch+1,并且当该进程结束后,对应的nattch会-1。当然也可以使用shmdt手动去关联。

4、释放共享内存 

        手动释放共享内存的接口是shmctl,该接口本质的功能是控制共享内存,只不过也有删除选项,具体介绍如下:

#include <sys/ipc.h>
#include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);
//shmid表示要释放的共享内存的标识码
//cmd表示该函数执行的具体任务,比如IPC_RMID表示删除任务
//buf表示指向共享内存数据结构的指针,若使用删除任务则该指针置为NULL即可//调用成功返回0,失败返回-1

4.1 测试shmctl 

         测试shmctl的代码如下:

#include "sharemem.hpp"int main()
{int shmid = creatnewshm();cout<<"申请共享内存成功"<<endl;sleep(2);//观察nattch的值char* poi = (char*)shmat(shmid,NULL,0);cout<<"关联共享内存成功"<<endl;sleep(2);//观察nattch的值shmdt(poi);cout<<"成功去关联共享内存"<<endl;sleep(2);//观察nattch的值shmctl(shmid,IPC_RMID,nullptr);cout<<"成功删除共享内存"<<endl;return 0;
}

        运行结果:

        从结果可以看到,无论是去关联测试还是删除共享内存,在右边的监控中都会显示对应的效果。 

5、实现共享内存通信 

         有了上述的接口以及sharemem.hpp文件,就可以实现两个进程的通信了,所以需要写一个客户端进程和一个服务器进程,其中服务器进程创建共享内存,让他们两都关联该共享内存,并且由客户端向服务器发送消息,服务器代码如下:

#include "sharemem.hpp"int main()
{int shmid = creatnewshm();char *poi = (char *)shmat(shmid, nullptr, 0);cout << "关联共享内存成功" << endl;while (true){cout<<"服务器接收:"<<poi<<endl;sleep(1);}shmdt(poi);shmctl(shmid,IPC_RMID,nullptr);return 0;
}

        客户端代码如下:

#include "sharemem.hpp"int main()
{int shmid = getoldshm();char* poi = (char*)shmat(shmid,nullptr,0);cout<<"关联共享内存成功"<<endl;while (true){string message;cout<<"客户端发送:";cin>>message;strcpy(poi,message.c_str());}shmdt(poi);return 0;
}

        测试结果:

        从结果可以发现,共享内存的通信本质就是对一个空间进行内存式的访问,无需调用read、write这些系统接口,直接用内存函数写入数据至内存对方就能够读取到内存里的数据。

6、共享内存的特性

        1、共享内存不需要调用系统接口实现进程间通信,只需要调用内存函数对内存进行读写即可实现进程间通信。

        2、共享内存本身没有同步互斥的概念,体现在上面的运行结果中读端会一直读内容(说明没有同步),并不会因为写端还未写而阻塞住。并且读端和写端可以同时访问共享内存(说明没有互斥)。

        3、共享内存在读写效率上更为高效,因为少了write和read这些步骤,即少了一层拷贝。 

结语 

        以上就是关于共享内存通信的讲解,共享内存作为IPC的其中一种方式,相对于其他通信方式,他有利有弊,在实际应用里先熟悉他的接口以及使用共享内存的步骤:申请共享内存(包括创建key值)、关联共享内存(shmat)、去关联(shmdt)、释放共享内存(shmctl) ,通过以上步骤可以实现完整的共享内存通信。

        最后如果本文有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!!

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

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

相关文章

解决前后端同一个端口跨域问题

前端起了一个代理 如果url是api开头的自动代理访问8080端口&#xff08;解决前后端端口不一致要么是前端代理&#xff0c;要么是后端加过滤器&#xff09; proxy:{/api:{target:http://localhost:8080,changeOrigin : true,// 替换去掉路径上的api// rewrite:(path)>path.r…

【信息学奥赛】CSP-J/S初赛07 排序算法及其他算法在初赛中的考察

本专栏&#x1f449;CSP-J/S初赛内容主要讲解信息学奥赛的初赛内容&#xff0c;包含计算机基础、初赛常考的C程序和算法以及数据结构&#xff0c;并收集了近年真题以作参考。 如果你想参加信息学奥赛&#xff0c;但之前没有太多C基础&#xff0c;请点击&#x1f449;专栏&#…

生产力工具|viso常用常见科学素材包

一、科学插图素材网站 一图胜千言&#xff0c;想要使自己的论文或重要汇报更加引人入胜&#xff1f;不妨考虑利用各类示意图和科学插图来辅助研究工作。特别是对于新手或者繁忙的科研人员而言&#xff0c;利用免费的在线科学插图素材库&#xff0c;能够极大地节省时间和精力。 …

信创-办公软件应用工程师认证

随着国家对信息技术自主创新的战略重视程度不断提升&#xff0c;信创产业迎来前所未有的发展机遇。未来几年内&#xff0c;信创产业将呈现市场规模扩大、技术创新加速、产业链完善和国产化替代加速的趋势。信创人才培养对于推动产业发展具有重要意义。应加强高校教育、建立人才…

适用于 Windows 11/10/8/7/Vista/XP 的最佳免费分区软件

无论您使用的是 SSD、机械磁盘还是任何类型的 RAID 阵列&#xff0c;硬盘驱动器都是 Windows 计算机中不可或缺的组件。在将文件保存到全新磁盘之前&#xff0c;您应该初始化它&#xff0c;创建分区并使用文件系统格式化。在运行计算机一段时间后&#xff0c;您需要收缩、扩展、…

基于深度学习的图像背景剔除

在过去几年的机器学习领域&#xff0c;我一直想打造真正的机器学习产品。 几个月前&#xff0c;在参加了精彩的 Fast.AI 深度学习课程后&#xff0c;似乎一切皆有可能&#xff0c;我有机会&#xff1a;深度学习技术的进步使许多以前不可能实现的事情成为可能&#xff0c;而且开…

Ubuntu DNS服务配置 深度解析

测试方法 resolvectl status dig alidns.com 修改实践 直接用接口配置&#xff0c;没用 /etc/resolv.conf&#xff0c;有效 /etc/netplan/01-network-manager-all.yaml,无效 /etc/systemd/resolved.conf&#xff0c;见link&#xff0c;为全局配置 [Resolve] DNS1.1.1.1 Fa…

Rufus 制作启动盘 | 便携的工作空间

唠唠闲话 最近服务器硬盘故障多&#xff0c;在修复过程中&#xff0c;学习了一些操作&#xff0c;这里做个记录。本期主要介绍 U盘启动盘的制作&#xff0c;以及持久化存储。 U 盘启动盘 镜像选择 Ubuntu 的版本命名遵循 “Adjective Animal” 的模式&#xff0c;即 “形容…

【Linux进阶】磁盘分区3——目录树,挂载

Linux安装模式下&#xff0c;磁盘分区的选择&#xff08;极重要&#xff09; 在Windows 系统重新安装之前&#xff0c;你可能会事先考虑&#xff0c;到底系统盘C盘要有多大容量&#xff1f;而数据盘D盘又要给多大容量等&#xff0c;然后实际安装的时候&#xff0c;你会发现其实…

二重积分 - 包括计算方法和可视化

二重积分 - 包括计算方法和可视化 flyfish 计算在矩形区域 R [ 0 , 1 ] [ 0 , 2 ] R [0, 1] \times [0, 2] R[0,1][0,2] 下&#xff0c;函数 z 8 x 6 y z 8x 6y z8x6y 的二重积分。这相当于计算曲面 z 8 x 6 y z 8x 6y z8x6y 与 xy 平面之间的体积。 二重积分…

Windows 11内置一键系统备份与还原 轻松替代Ghost

面对系统崩溃、恶意软件侵袭或其他不可预见因素导致的启动失败&#xff0c;Windows 7~Windows 11内置的系统映像功能能够迅速将您的系统恢复至健康状态&#xff0c;确保工作的连续性和数据的完整性。 Windows内置3种备份策略 U盘备份&#xff1a;便携且安全 打开“创建一个恢…

AI与测试相辅相成

AI助力软件测试 1.AI赋能软件测试 使用AI工具来帮助测试人员提高测试效率&#xff0c;提供缺陷分析和缺陷预测。 语法格式 设定角色 具体指示 上下文格式 例: 角色&#xff1a;你是一个测试人员 内容&#xff1a;请帮我生成登录案例的测试用例 ​ 1.只有输入正确账号和密码才…

重载一元运算符

自增运算符 #include<iostream> using namespace std; class CGirl { public:string name;int ranking;CGirl() { name "zhongge"; ranking 5; }void show() const{ cout << "name : "<<name << " , ranking : " <…

LeetCode热题100刷题3:3. 无重复字符的最长子串、438. 找到字符串中所有字母异位词、560. 和为 K 的子数组

3. 无重复字符的最长子串 滑动窗口、双指针 class Solution { public:int lengthOfLongestSubstring(string s) {//滑动窗口试一下//英文字母、数字、符号、空格,ascii 一共包含128个字符vector<int> pos(128,-1);int ans 0;for(int i0,j0 ; i<s.size();i) {//s[i]…

uniapp启动页面鉴权页面闪烁问题

在使用uni-app开发app 打包完成后如果没有token&#xff0c;那么就在onLaunch生命周期里面判断用户是否登录并跳转至登录页。 但是在app中页面会先进入首页然后再跳转至登录页&#xff0c;十分影响体验。 处理方法&#xff1a; 使用plus.navigator.closeSplashscreen() 官网…

CSF视频文件格式转换WMV格式(2024年可用)

如果大家看过一些高校教学讲解视频的话&#xff0c;很可能见过这样一个难得的格式&#xff0c;".csf "&#xff0c;非常漂亮 。 用暴风影音都可以打开观看&#xff0c;会自动下载解码。 但是一旦我们想要利用或者上传视频的时候就麻烦了&#xff0c;一般网站不认这…

Python 可视化 web 神器:streamlit、Gradio、dash、nicegui;低代码 Python Web 框架:PyWebIO

官网&#xff1a;https://streamlit.io/ github&#xff1a;https://github.com/streamlit/streamlit API 参考&#xff1a;https://docs.streamlit.io/library/api-reference 最全 Streamlit 教程&#xff1a;https://juejin.cn/column/7265946243196436520 Streamlit-中文文档…

Python脚本:将Word文档转换为Excel文件

引言 在文档处理中&#xff0c;我们经常需要将Word文档中的内容转换成其他格式&#xff0c;如Excel&#xff0c;以便更好地进行数据分析和报告。针对这一需求&#xff0c;我编写了一个Python脚本&#xff0c;能够批量处理指定目录下的Word文档&#xff0c;将其内容结构化并转换…

船舶雷达与导航系统选择7/8防水插座的原因分析

概述 船舶雷达与导航系统在现代航海中扮演着至关重要的角色&#xff0c;它们为船舶提供准确的导航信息&#xff0c;确保航行的安全和效率。在这些系统中&#xff0c;7/8防水插座的使用尤为重要&#xff0c;因为它们能够在恶劣的海上环境中提供稳定的电力和信号连接。接下来&am…

vue2(vue-cli3x[vue.config.js])使用cesium新版(1.117.0)配置过程

看来很多解决方法都没有办法&#xff0c;最后终于。呜呜呜呜 这里我用的是vue-cli去搭建的项目的vue2 项目&#xff0c;其实不建议用vue2搭配cesium。因为目前cesium停止了对vue2的版本更新&#xff0c;现在默认安装都是vue3版本&#xff0c;因此需要控制版本&#xff0c;否则…