共享内存通信效率碾压管道?System V IPC原理与性能实测

个人主页:敲上瘾-CSDN博客

进程通信:

  • 匿名管道:进程池的制作(linux进程间通信,匿名管道... ...)-CSDN博客
  • 命名管道:命名管道——进程间通信-CSDN博客

目录

一、共享内存的原理

二、信道的建立

1.创建共享内存

1.key的作用

2.key的选取

3.shmid的作用

4.key和shmid的区别

5.内存设定的特性

6.shmflg的设定

2.绑定共享内存

3.代码示例

三、利用共享内存通信

1.通信

2.解除绑定

3.销毁共享内存

1.命令行销毁

2.程序中销毁

四、共享内存的生命周期

五、数据安全问题

六、源码

1.comm.hpp

2.server.cc 

3.client.cc


一、共享内存的原理

        共享内存是通过在物理内存上开辟一块空间,然后让需要通信的进程都映射到这一块空间,这样就使它们看到同一块资源了。

        共享内存通信是双向的,也就是说一个进程可以既读又写,使用起来就和C语言的malloc申请到的内存差不多。这种通信方式存在着数据安全问题,会在下文细说。 

二、信道的建立

1.创建共享内存

        创建共享内存使用shmget函数,它的作用是创建或获取共享内存段的系统调用。

        对于shmget的使用来说,虽然操作起来相对简单,但要完全理解其各种参数的设定则较为困难。不过接下来我会进行详细讲解。

shmget声明如下:

#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);
  • 参数key:用户设定任意一个数,用于区分不同共享内存,通常由ftok生成。
  • 参数size:设定共享内存的大小。
  • 参数shmflg:标志位,用于指定共享内存段的创建方式和权限。常见的标志包括:
    • IPC_CREAT如果共享内存段不存在,则创建它。
    • IPC_EXCL与 IPC_CREAT 一起使用,确保创建的共享内存段是新的
    • 权限标志:如 0666,表示所有用户都有读写权限。
  • 返回值: 
    • 成功时返回共享内存段的标识符(shmid)。
    • 失败时返回 -1,并设置 errno 以指示错误类型。

1.key的作用

  • 思考1:在用户层面如何让两个独立进程共享同一块内存?
  • 思考2:在匿名管道和命名管道中,用户层面是如何让两个进程确定同一个资源的?

        问题2很显然,管道的本质是文件,用户通过让两个程序打开同一个文件名来实现看到同一个资源。

因此,共享内存同样需要一个key来充当类似文件名的功能。

2.key的选取

        key参数本质是一个int类型,我们可以直接指定一个数值传入,当然,为了更规范,更专业,我们通常都会使用ftok来生成。

ftok的声明如下:

#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);
  • 参数pathnme:一个存在的文件路径(例如 /tmp/myfile),文件必须存在,否则 ftok 会失败。
  • 参数proj_id:一个整数,用于进一步区分不同的 IPC 对象。
  • 返回值: 
    • 成功:返回生成的 key_t 键值。

    • 失败:返回 -1,并设置 errno 以指示错误原因。

3.shmid的作用

        shmid是一个int类型,由shmget返回,在作用上和物理意义上与文件系统中的fd类似。它的作用主要是让用户找到指定的共享内存。而它的物理意义在这里就不提了,感兴趣的话可以等我的下一篇博文(IPC系统)发布。

4.key和shmid的区别

        key最终成为系统层区分不同IPC的标志,而shmid则是用户层用来区分不同IPC的标志。

5.内存设定的特性

这里的内存设定指的是shmget函数中的参数size。

        当传入的内存不足4096字节(4KB)的倍数时,会扩到4096倍数。但是只会提供size大小的使用空间。这样做可以规避掉一些因为共享内存过多带来的问题。

6.shmflg的设定

对于共享内存,我们可以将程序简单分为创建端和使用端,它们的shmflg设定通常是:

  • 创建端:IPC_CREAT | IPC_EXCL | 0666
  • 使用端:IPC_CREAT

创建端要保证IPC是最新的,所以需要加IPC_EXCL,然后还需要设定权限。

使用端只需要获取共享内存段的系统调用,因此只用一个IPC_CREAT即可。

2.绑定共享内存

        以上我们完成的只是共享内存的创建,接下来还需要把进程绑定到共享内存,使用函数shmat,其中at指的是单词attach。

shmat的声明:

#include <sys/types.h>
#include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg);
  • 参数shmid:传入从shmget中返回的shmid来指定共享内存。
  • 参数shmaddr:指定共享内存段附加到进程地址空间的位置,通常设为nullptr,系统会自动选择一个合适的地址。
  • 参数shmflg:读写方式,常用的有:
    • SHM_RDONLY:以只读方式附加共享内存段。
    • 0:以读写方式附加共享内存段。
  • 返回值:
    • 成功时,返回共享内存段附加到进程地址空间的起始地址。
    • 失败时,返回 (void *) -1,并设置 errno

3.代码示例

创建端程序:

int main()
{//生成一个keyint key = ftok(".", 48);//创建共享内存int shmid = shmget(key, 4069, IPC_CREAT | IPC_EXCL | 0666);//连接到共享内存void* p = shmat(shmid,nullptr,0);//使用共享内存//... ...return 0;
}

使用端程序:

int main()
{//生成一个相同keyint key = ftok(".", 48);//获取到共享内存的系统调用int shmid = shmget(key, 4069, IPC_CREAT);//连接到共享内存void* p = shmat(shmid,nullptr,0);//使用共享内存//... ...return 0;
}

注意:为了简洁和方便说明问题,以上代码省略了头文件的包含和返回值有效性的判断等等,在实际开发中可不敢省略。 

三、利用共享内存通信

1.通信

        上文我们只是完成了信道的建立,接下来我们进行通信,通过上面的操作,我们已经获取到共享内存的起始地址。

它的用法与C语言的malloc申请的内存用法相同,只是共享内存可以同时被两个进程访问。

如下写端:

int main()
{int key = ftok(".", 48);int shmid = shmget(key, 4069, IPC_CREAT | IPC_EXCL | 0666);void* p = shmat(shmid,nullptr,0);//使用共享内存char* chp = (char*)p;for(int i='a';i<='z';i++){sleep(1);*chp=i;chp++;}return 0;
}

读端: 

int main()
{int key = ftok(".", 48);int shmid = shmget(key, 4069, IPC_CREAT);void* p = shmat(shmid,nullptr,0);//使用共享内存char* chp = (char*)p;while(true){sleep(1);cout<<chp<<endl;}return 0;
}

注意:为了获取到同一个共享内存,我们设定的key必须一致。 

2.解除绑定

        如果进程退出时没有解除绑定,共享内存段仍然会保留在系统的共享内存资源中,直到显式删除(通过 shmctl 或系统重启)。

使用shmdt来解除绑定,其中dt代表单词delete。

shmdt的声明:

int shmdt(const void *shmaddr);

参数shmaddr:需要断开连接的共享内存的起始地址。

返回值:

  • 成功:返回0。
  • 失败:返回-1,并设置errno以指示错误原因。

        一个共享内存,与它绑定的程序的个数是由一个引用计数机制进行维护的,当shmdt成功,引用计数减1。 

3.销毁共享内存

        共享内存不会随程序的结束而销毁,它是随内核的,因此需要显式地进行销毁,可以使用shmctl函数。或在命令行中使用指令进行销毁。

1.命令行销毁

1.1.查看共享内存信息

ipcs -m

如下: 

这里解释一下nattch信息:它表示与该共享内存连接的程序个数。 

1.2.销毁共享内存

ipcrm -m 2

这里需要填入shmid(即这里的2)来指定共享内存。 

2.程序中销毁

在程序中销毁我们使用函数shmctl,其中ctl代表单词control。

shmctl声明如下:

#include <sys/ipc.h>
#include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  • 参数shmid:传入从shmget返回的shmid来指定需要销毁的共享内存。
  • 参数cmd:需要传入一个操作选项,操作选项很多,而IPC_RMID就是用来销毁共享内存的。
  • 参数shmid_ds:这是一个输出型参数,如果你需要获取共享内存的信息,则传入一个shmid_ds类型的指针来接收,如果不是通常传入nullptr即可。
  • 返回值:
    • 成功时返回 0
    • 失败时返回 -1,并设置 errno 以指示错误类型。

注:命令行销毁和程序中销毁效果是一样的,因为命令行销毁底层还是调用了shmctl函数。

四、共享内存的生命周期

        共享内存的生命周期是不随进程的,而是随内核,如果没有显示删除它就会一直存在,尽管相关的进程已经退出。直到重装系统才得以释放。

使用shmctl释放共享内存存在的情况

1.正常释放

        当nattach(引用计数)为0时,即没有进程与它绑定,被正常释放。

2.共享内存段被标记为已删除,但仍有进程附加(shmat)

        共享内存段已经被标记为已删除(不能附加到新的进程),但之前仍有一些进程附加到该共享内存段并正在使用。所以共享内存段不会被立即释放。只有当所有附加的进程都调用 shmdt 分离后,系统才会释放资源。

五、数据安全问题

        共享内存最大的优点就是,相比使用管道技术,它减少了中间复杂转化和拷贝工作,而是直接对物理内存进行访问。

        但它也有一个致命的缺点,相比管道技术,共享内存它的读端和写端是不带有同步机制的,这就很容易使得数据混乱,也就是造成数据不一致问题。

        比如我们写端写入“hello world”,而读端读到的可能是“he”,“ll”,“o wor”,“ld”等等无法预测的奇葩数据。 读端一个劲地读,不会管写端这句话是否已经说完,而且也无法知道。

        当我们不了解锁的情况下想要解决这个问题,可以利用命名管道来解决,因为命名管道带有同步机制,我们用它的write和read函数来保护数据的安全,当然write和read并不用写或读什么有意义的数据。

非常感谢您能耐心读完这篇文章。倘若您从中有所收获,还望多多支持呀!74c0781738354c71be3d62e05688fecc.png

六、源码

1.comm.hpp

#pragma
#include <iostream>
#include <string>
#include <cstdio>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#define SIZE 1024
#define KEY_NUM 0x66
#define PATH "."
#define CREATE "create"
#define USE "user"
#define ERROR(str)   \{                \perror(str); \exit(1);     \}using namespace std;
class Shm
{
private:void Create(int flg){_shmid = shmget(_key, _size, flg);if (_shmid < 0){ERROR("shmget");}printf("shmget success id:%d\n", _shmid);}void Attach(){_start_mem = shmat(_shmid, nullptr, 0);if ((long long)_start_mem < 0){ERROR("shmat");}}void Destroy(){int m = shmdt(_start_mem);if (m < 0){ERROR("shmdt");}int n = shmctl(_shmid, IPC_RMID, nullptr);if (n < 0){ERROR("shmctl");}}public:Shm(string path, int projid, string user): _shmid(-1), _size(SIZE), _usertype(user){_key = ftok(path.c_str(), projid);if (_key < 0){ERROR("ftok");}printf("ftok success id:%d\n", _key);if (_usertype == CREATE)Create(IPC_CREAT | IPC_EXCL | 0666);elseCreate(IPC_CREAT);Attach();}void *VirtualAddr(){return _start_mem;}~Shm(){Destroy();}private:int _shmid;int _size;int _key;string _usertype;void *_start_mem;
};

2.server.cc 

#include"comm.hpp"
int main()
{Shm sm(PATH,KEY_NUM,CREATE);char* ps = (char*)sm.VirtualAddr();while(true){sleep(1);printf("%s\n",ps);}return 0;
}

3.client.cc

#include"comm.hpp"
int main()
{Shm sm(PATH,KEY_NUM,USE);char* ps = (char*)sm.VirtualAddr();for(char ch='0';ch<'z';ch++){sleep(1);*ps = ch;ps++;*ps='\0';}return 0;
}

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

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

相关文章

【net1】tcp,route,iptables,macvlan

文章目录 1.局域网:CSMA/CD2.互联网:ARP,NAT,路由表比映射表复杂3.tcp协议:telnet,tcpdump,syn/accept队列4.linux的route指令:route add4.1 案例:从ubuntu机器ping 199.199.199.199,配置路由使能通5.防火墙iptables:(ip+tables)对网络上数据包通过表的形式进行规…

如何用Deepseek制作流程图?

使用Deepseek制作流程图&#xff0c;本质上是让AI根据你的需求&#xff0c;生成相关流程图的代码&#xff0c;然后在流程图编辑器中渲染&#xff0c;类似于Python一样&#xff0c;ChatGPT可以生成代码&#xff0c;但仍需在IDE中执行。 你知道绘制流程图最高效的工具是什么吗&a…

基于PySide6与CATIA Automation的批量截图处理系统开发实践

引言 本文完整实现了基于PySide6 GUI框架与CATIA Automation技术的批量截图处理系统。系统支持对CATIA文件&#xff08;.CATPart/.CATProduct&#xff09;的自动化截图、图像优化及批量导出&#xff0c;通过模块化架构设计实现了超过200%的效率提升。本文将从技术架构、核心算…

【PyQt5】【Visual Studio】环境配置

前言 最近爱上搞软件编程&#xff0c;今天我就来教学如何进行Python软件编程PyQt5 下载工具 编程环境配置 Visual Studio Python下载最新版本就行 下载完之后呢&#xff0c;简单配置一下环境&#xff0c;Visual Studio的Python环境配置教程有很多可以自己在网上找 我这有Py…

uniapp+vue实现购物车的左滑删除功能

左滑删除 删除功能利用透明的改变在显示删除按钮实现思路代码效果展示 利用scroll滑动容器来实现代码实现效果展示 我们在移动端的电商平台中&#xff0c;一般都是左滑后然后删除按钮出现&#xff0c;用户可以点击删除按钮来进行该商品的删除&#xff0c;这里我分享两种方法来达…

CSSHTML新特性

HTML5 新特性探秘 在 Web 开发的不断演进中&#xff0c;HTML5 带来了一系列令人振奋的新特性&#xff0c;极大地提升了网页的功能和用户体验。今天&#xff0c;我们就来深入探究一下这些新特性。 语义化标签&#xff1a;让网页结构更清晰 语义化标签是 HTML5 的一大亮点。在…

网络爬虫【简介】

我叫补三补四&#xff0c;很高兴见到大家&#xff0c;欢迎一起学习交流和进步 今天来讲一讲爬虫 一、网络爬虫的定义 网络爬虫&#xff08;Web Crawler&#xff09;&#xff0c;又称为网络蜘蛛、网络机器人等&#xff0c;是一种按照一定规则自动抓取互联网信息的程序或脚本。它…

数字隔离器,如何提升储能系统的安全与效能?

随着全球对光伏、风电等可再生能源需求的持续增长&#xff0c;在全球能源转型的浪潮中&#xff0c;储能技术凭借着可平衡能源供需、提高能源利用效率等优势&#xff0c;已成为实现 “双碳” 目标的核心支撑。据国家能源局公布数据显示&#xff0c;截至2024年底&#xff0c;我国…

AI玩Flappy Bird || 基于Q-Learning和DQN的机器学习

一、游戏介绍 Flappy Bird 游戏需要玩家控制一只小鸟越过管道障碍物。玩家只可以进行“跳跃”或者“不操作”两种操作&#xff0c;即点或不点。点则让小鸟上升一段距离&#xff0c;不点小鸟继续下降。若小鸟碰到障碍物或地面&#xff0c;则游戏失败。 本项目目的是开发一个深层…

【Linux内核系列】:文件系统收尾以及软硬链接详解

&#x1f525; 本文专栏&#xff1a;Linux &#x1f338;作者主页&#xff1a;努力努力再努力wz &#x1f4aa; 今日博客励志语录&#xff1a; 世界上只有一种个人英雄主义&#xff0c;那么就是面对生活的种种失败却依然热爱着生活 内容回顾 那么在之前的学习中&#xff0c;我们…

【eNSP实战】三层交换机使用ACL实现网络安全

拓图 要求&#xff1a; vlan1可以访问Internetvlan2和vlan3不能访问Internet和vlan1vlan2和vlan3之间可以互相访问PC配置如图所示&#xff0c;这里不展示 LSW1接口vlan配置 vlan batch 10 20 30 # interface Vlanif1ip address 192.168.40.2 255.255.255.0 # interface Vla…

Trae与Builder模式初体验

说明 下载的国际版&#xff1a;https://www.trae.ai/ 建议 要选新模型 效果 还是挺不错的&#xff0c;遇到问题反馈一下&#xff0c;AI就帮忙解决了&#xff0c;真是动动嘴&#xff08;打打字就行了&#xff09;&#xff0c;做些小的原型效果或演示Demo很方便呀&#xff…

Canoe Panel常用控件

文章目录 一、Panel 中控件分类1. 指示类控件2. 功能类控件3. 信号值交互类控件4. 其他类控件 二、控件使用方法1. Group Box 控件2. Input/Output Box控件3. Static Text控件4. Button控件5. Switch/Indicator 控件 提示&#xff1a;Button 和 Switch 的区别参考 一、Panel 中…

睡不着运动锻炼贴士

在快节奏的现代生活中&#xff0c;失眠似乎已成为许多人的“夜间伴侣”。夜晚辗转反侧&#xff0c;白天精神不振&#xff0c;这样的恶性循环让许多人苦不堪言。其实&#xff0c;除了调整作息和饮食习惯&#xff0c;适当的运动也是改善睡眠的一剂良药。今天&#xff0c;就让我们…

java数据结构(复杂度)

一.时间复杂度和空间复杂度 1.时间复杂度 衡量一个程序好坏的标准&#xff0c;除了能处理各种异常&#xff0c;还有就是时间效率&#xff0c;当然&#xff0c;对于一些配置好的电脑数据处理起来就是比配置低的高&#xff0c;但从后期发展来看&#xff0c;当数据量足够庞大时&…

NAT和NAPT的介绍

一、NAT的介绍以及作用 二、NAPT的介绍以及作用 三、NAT vs NAPT 一、NAT的介绍以及作用 1.1 NAT的介绍 NAT&#xff08;Network Address Translation&#xff09;是一种广泛应用于互联网的技术&#xff0c;主要用于解决IPv4地址耗尽问题&#xff0c;同时提供网络安全和网络…

VSCode通过SSH免密远程登录Windows服务器

系列 1.1 VSCode通过SSH远程登录Windows服务器 1.2 VSCode通过SSH免密远程登录Windows服务器 文章目录 系列1 准备工作2 本地电脑配置2.1 生成密钥2.2 VS Code配置密钥 3. 服务端配置3.1 配置SSH服务器sshd_config3.2 复制公钥3.3 配置权限&#xff08;常见问题&#xff09;3.…

大模型训练全流程深度解析

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。https://www.captainbed.cn/north 文章目录 1. 大模型训练概览1.1 训练流程总览1.2 关键技术指标 2. 数据准备2.1 数据收集与清洗2.2 数据…

export、export default 和 module.exports 深度解析

文章目录 1. 模块系统概述1.1 模块系统对比1.2 模块加载流程 2. ES Modules2.1 export 使用2.2 export default 使用2.3 混合使用 3. CommonJS3.1 module.exports 使用3.2 exports 使用 4. 对比分析4.1 语法对比4.2 使用场景 5. 互操作性5.1 ES Modules 中使用 CommonJS5.2 Com…

AI芯片设计

目的&#xff1a;未来的时代&#xff0c;一定会是AI的时代&#xff0c;那么&#xff0c;AI时代的三个重要组成部分&#xff0c;我要参与其中之一&#xff01; 参考视频&#xff1a;AI芯片设计第一讲_哔哩哔哩_bilibili 端处理 云端