共享内存(System V)——进程通信
个人主页:敲上瘾-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并不用写或读什么有意义的数据。
非常感谢您能耐心读完这篇文章。倘若您从中有所收获,还望多多支持呀!
六、源码
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;
}
相关文章:
共享内存(System V)——进程通信
个人主页:敲上瘾-CSDN博客 进程通信: 匿名管道:进程池的制作(linux进程间通信,匿名管道... ...)-CSDN博客命名管道:命名管道——进程间通信-CSDN博客 目录 一、共享内存的原理 二、信道的建立 …...
ctfhub-HTTP协议
请求方式 它要我们使用CTF**B Method,其实就是ctfhub方式 我们直接抓包试一试,把GET改成CTFHUB,在发送到repeater 在repeater处点击发送,得到响应 302跳转 点击“give me flag"没有任何变化,我们抓个包试试 我们把它发送到repeater&…...
【TMS570LC4357】之工程创建
备注:具体资料请在官网海淘.TMS570LC4357资料 在线文档Hercules Safety MCU Resource Guide — Hercules Safety MCUs Documentation XDS100 Debug Probe (ti.com) Git https://git.ti.com/git/hercules_examples/hercules_examples.git https://git.ti.com/cgit/h…...
一种改进的Estimation-of-Distribution差分进化算法
为了充分利用差分进化(DE)的强大开发和estimation-of-distribution算法(EDA)的强大探索,提出了一种混合estimation-of-distribution算法的改进差分进化IDE-EDA。首先,提出了一种新的协同进化框架࿰…...
正则表达式(复习)
文章目录 一、[]: 一个字符集合二、{}: 重复次数三、特殊符号四、(): 分组五、python代码示例六、注意 正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个…...
大数据学习(61)-Impala与Hive计算引擎
&&大数据学习&& 🔥系列专栏: 👑哲学语录: 承认自己的无知,乃是开启智慧的大门 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一下博主哦ᾑ…...
洛谷每日1题-------Day18__P1320 压缩技术(续集版)
题目描述 设某汉字由 NN 的 0 和 1 的点阵图案组成。 我们依照以下规则生成压缩码。连续一组数值:从汉字点阵图案的第一行第一个符号开始计算,按书写顺序从左到右,由上至下。第一个数表示连续有几个 0,第二个数表示接下来连续有…...
[数据结构]排序之希尔排序( 缩小增量排序 )
希尔排序法又称缩小增量法。希尔排序法的基本思想是: 先选定一个整数,把待排序文件中所有记录分成个 组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工 作。当…...
LORA中 软提示是什么
LORA中 软提示是什么 软提示的原理概述 软提示(Soft Prompt)是提示学习(Prompt Learning)中的一种技术,主要用于引导预训练语言模型在特定任务上的表现。传统的提示学习通常使用硬提示(Hard Prompt),也就是在输入文本中添加固定的离散文本,比如在情感分析任务里,在…...
问问 DeepSeek 什么是网络爬虫
在现代互联网时代,信息的获取和整理变得至关重要,而爬虫(Web Crawler) 是一种自动化工具,帮助我们从网页上提取数据。爬虫在新闻采集、商品比价、天气数据收集等方面应用广泛。 爬虫的工作原理 爬虫的基本工作流程如下…...
进程(下)【Linux操作系统】
文章目录 进程的状态R状态:S状态:D状态:T状态t状态Z状态:孤儿进程X状态: 进程的优先级如果我们要修改一个进程的优先级重置进程优先级 进程切换进程的调度 进程的状态 在内核中,进程状态的表示,…...
Insar结合ISCE2,某一个文件进行并行-stackSentinel.py
stackSentinel.py 依次执行 run_01 到 run_15,记录各自的日志 并行执行 run_16 里的所有命令,仍然记录日志 不知道对不对,测试的时间有点长就给停了 #!/bin/bash# ✅ 适用于 WSL/Linux runfiles_path"/mnt/e/insar_order_test/Stack…...
2.2.3 TCP—UDP-QUIC
文章目录 2.2.3 TCP—UDP-QUIC1. TCP如何做到可靠性传输1. ACK机制2. 重传机制3. 序号机制4. 窗口机制5. 流量机制6. 带宽机制 2. tcp和udp如何选择1. tcp和udp格式对比2. ARQ协议(Automatic Repeat reQuest,自动重传请求)1. ARQ协议的主要类…...
golang从入门到做牛马:第十九篇-Go语言类型转换:数据的“变形术”
在Go语言中,类型转换是一种将一种数据类型的变量转换为另一种类型的变量的操作。类型转换在处理不同类型的数据时非常有用,尤其是在需要将数据从一种类型转换为另一种类型进行计算或存储时。接下来,让我们一起深入了解Go语言中的类型转换。 什么是类型转换:数据的“变形术”…...
【Golang】第一弹-----初步认识GO语言
笔上得来终觉浅,绝知此事要躬行 🔥 个人主页:星云爱编程 🔥 所属专栏:Golang 🌷追光的人,终会万丈光芒 🎉欢迎大家点赞👍评论📝收藏⭐文章 一、Go语言的简单介绍 1、G…...
K8S学习之基础二十三:k8s的持久化存储之nfs
K8S持久化存储之nfs 在 Kubernetes (k8s) 中使用 NFS(Network File System)作为存储解决方案是一种常见的方式,特别是在需要共享存储的场景中。以下是关于如何在 Kubernetes 中使用 NFS 存储的详细说明: 1. 准备 NFS 服务器 …...
【Linux通信篇】深入理解进程间通信——管道
--------------------------------------------------------------------------------------------------------------------------------- 每日鸡汤:找一个对的人,然后好好去爱。一个你跟他在一起,然后又可以舒舒服服做自己的人。 -------…...
「 DelegateUI 」Ant-d 风格的 Qt Qml UI 套件
写在前面:关于为什么要写一套新的UI框架 一方面,Qt Qml 生态中缺乏一套既遵循现代设计规范(自带的功能少且丑,懂得都懂),又能深度整合 Qt 生态的开源组件库。 另一方面,Qt Qml 中也有一些其他方案,例如 FluentUI Qml…...
Redis--Set类型
目录 一、引言 二、介绍 三、命令 1.sadd,smembers,sismember 2.spop,srandmember 3.smove,srem 4.sinter,sinterstore 5.sunion,sunionstore,sdiff,sdiffstore 四、内部编码 1.intset 2.hashtable 五、应用场景 1.使用Set保存用…...
【0013】Python数据类型-列表类型详解
如果你觉得我的文章写的不错,请关注我哟,请点赞、评论,收藏此文章,谢谢! 本文内容体系结构如下: Python列表,作为编程中的基础数据结构,扮演着至关重要的角色。它不仅能够存储一系…...
文件上传靶场(10--20)
目录 实验环境: 具体内容实现: 第十关(双写绕过): 第十一关:(%00截断,此漏洞在5.2版本中) 正确用法 错误用法 思路: 操作过程: 第十二关…...
C# 检查系统是否开启 Hyper - V
C# 检查系统是否开启 Hyper - V 在使用 C# 开发应用程序时,有时需要判断系统是否开启了 Hyper - V 功能。Hyper - V 是 Windows 系统提供的一款虚拟化技术,以下为你介绍几种在 C# 中检查系统是否开启 Hyper - V 的方法。 方法一:通过查询系…...
【前端】BOM DOM
两天更新完毕,建议关注收藏点赞 友情链接: HTML&CSS&LESS&Bootstrap&Emmet Axios & AJAX & Fetch BOM DOM 待整理 js2 Web API 是浏览器提供的一套操作浏览器功能和页面元素的 API ( BOM 和 DOM)。官方文档点击跳转 目录 BOMDOM…...
K8s 1.27.1 实战系列(十一)ConfigMap
ConfigMap 是 Kubernetes 中管理非敏感配置的核心资源,通过解耦应用与配置实现灵活性和可维护性。 一、ConfigMap 的核心功能及优势 1、配置解耦 将配置文件(如数据库地址、日志级别)与容器镜像分离,支持动态更新而无需重建镜像。 2、多形式注入 环境变量:将键值…...
计算机网络——IP、MAC、ARP
一、IP地址 1. 什么是IP地址? IP地址(Internet Protocol Address)是互联网中设备的唯一逻辑标识符,类似于现实生活中的“门牌号”。它分为 IPv4(32位,如 192.168.1.1)和 IPv6(128位…...
代码优化——基于element-plus封装组件:表单封装
前言 今天实现一个基于element-plus表单组件的二次封装,什么是二次封装?查看以下表单,传统表单组件是不是用<el-form>嵌套几个<el-form-item>即可实现,那么一个表单可不可以实现,传入一个对象给封装组件&a…...
C/C++中使用CopyFile、CopyFileEx原理、用法、区别及分别在哪些场景使用
文章目录 1. CopyFile原理函数原型返回值用法示例适用场景 2. CopyFileEx原理函数原型返回值用法示例适用场景 3. 核心区别4. 选择建议5. 常见问题6.区别 在Windows系统编程中,CopyFile和CopyFileEx是用于文件复制的两个API函数。它们的核心区别在于功能扩展性和控制…...
qt 多进程使用共享内存 ,加速数据读写,进程间通信 共享内存
Summary: 项目中我们有时需要使用共享内存共享数据,这样,数据不用进程IO读写,加进数据加载和落地; 程序退出时,再保存到本地;速度提升数十倍; Part1:QSharedMemory Windows平台下进程间通信…...
HTML左右分页2【搬代码】
HTML左右分页2 html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>左右分页布局</title>&l…...
【鸿蒙开发】OpenHarmony调测工具hdc使用教程(设备开发者)
00. 目录 文章目录 00. 目录01. OpenHarmony概述02. hdc简介03. hdc获取04. option相关的命令05. 查询设备列表的命令06. 服务进程相关命令07. 网络相关的命令08. 文件相关的命令09. 应用相关的命令10. 调试相关的命令11. 常见问题12. 附录 01. OpenHarmony概述 OpenHarmony是…...
