【Linux】进程间通信——System V共享内存

【Linux】进程间通信——System V共享内存

🔥 个人主页:大耳朵土土垚🔥 所属专栏:Linux系统编程
这里将会不定期更新有关Linux的内容,欢迎大家点赞,收藏,评论🥳🥳🎉🎉🎉

文章目录

前言

System V是一种在Linux系统中用于进程间通信(IPC)的机制。它提供了几种不同的通信方式,包括共享内存、消息队列和信号量。以下是关于Linux进程间通信System V共享内存的详细解释:

1. 基本原理

System V共享内存是IPC(进程间通信)机制的一部分,它允许两个或多个进程共享一段物理内存。这段内存可以被所有参与的进程读取和写入。这种方式通常比较高效,因为进程可以直接对内存进行读写操作,而不需要通过内核进行数据传输。如下图所示:

在这里插入图片描述

2. 数据结构

System V共享内存可以在内存中创建多个,所以操作系统需要借助一种数据结构来管理它。System V共享内存使用struct shmid_ds结构体来描述共享内存的状态和属性。该结构体包括共享内存的键值key用来唯一标识共享内存,还包括了共享内存的权限、大小、创建时间、最后访问时间等信息。

注意键值key是由用户定义的标识符,不是系统生成的,这是因为进程间需要通过key来确定和使用共享内存,如果是由系统创建的,那么其他进程就无法得知该共享内存的key,从而无法使用。如果是用户自己定义的,那么用户想让哪个进程使用都可以。

3. 创建与使用

创建System V共享内存段的主要函数有shmgetshmatshmdtshmctl

    • 该函数用于创建一个新的共享内存段或者获取一个已有的共享内存段。
    • 参数key是一个用户定义的标识符,通常通过ftok()函数生成。
    • key_t ftok(const char *pathname, int proj_id);
    • 参数size指定了共享内存段的大小(以字节为单位)。
    • 参数shmflg我们主要学习两种:IPC_CREATIPC_EXCL
    • shmget返回值是一个共享内存标识符,如果出错则返回-1。

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

因为用户不确定哪些key值已经被使用了,哪些没被使用,所以操作系统提供了一个函数,可以帮助用户生成唯一的标识符。
ftok函数将给定的文件路径名(pathname)和项目ID(proj_id)根据算法转换为一个键值(key_t),用于创建或访问System V IPC资源(如消息队列、共享内存、信号量等)。这样不同进程间只需知道文件路径和项目ID就可以确定共享内存的标识符key了。
IPC_CREATIPC_EXCL都是宏,IPC_CREAT表示如果通过key值标识的共享内存不存在就创建;存在就获取该共享内存并返回它。
IPC_EXCL单独使用无意义,通常是和IPC_CREAT搭配使用,IPC_CREAT|IPC_EXCL,使用逻辑运算符或连接:表示如果通过key值标识的共享内存不存在就创建;存在就出错返回。
注意这个标识符不是key,而是给用户用的一个标识共享内存的标识符。

例如,创建一个共享内存:

#include<iostream>#include<string>#include<sys/ipc.h>#include<sys/shm.h>const std::string gpath ="/home/tutu/ProcessCommunicate/sharemem";constint ProcessId =0;const size_t gsize =4096;intmain(){ key_t key =ftok(gpath.c_str(),ProcessId);//获取唯一key值int shmid =::shmget(key,gsize,IPC_CREAT|IPC_EXCL);if(shmid <0) std::cout<<"共享内存创建失败..."<<std::endl; std::cout<<"共享内存key值:"<<key<<", "<<"shmid:"<<shmid<<std::endl;return0;}

结果如下:

在这里插入图片描述

我们还可以通过命令行命令来管理共享内存:

ipcs -m:用来查看系统的共享内存:

在这里插入图片描述
虽然Server进程已经运行完了,但是共享内存还是存在;这是因为只要创建了共享内存没被删除,那么共享内存的生命周期就随内核!

ipcrm -m shmid:用来删除共享内存:

在这里插入图片描述
  1. int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  • 对共享内存段执行特定的控制操作。
  • 参数shmid是由shmget()返回的共享内存标识符。
  • 参数cmd指定要执行的操作,例如IPC_RMID移除共享内存段,IPC_STAT获取共享内存段的状态等。
  • 参数buf是一个指向shmid_ds结构的指针,用来存储或修改共享内存段的信息。
  • 成功时返回0,出错时返回-1。

例如,使用shmctl删除一个共享内存:

#include<iostream>#include<string>#include<unistd.h>#include<sys/ipc.h>#include<sys/shm.h>const std::string gpath ="/home/tutu/ProcessCommunicate/sharemem";constint ProcessId =0;const size_t gsize =4096;intmain(){//1.创建共享内存 key_t key =ftok(gpath.c_str(),ProcessId);int shmid =::shmget(key,gsize,IPC_CREAT|IPC_EXCL);if(shmid <0) std::cout<<"共享内存创建失败..."<<std::endl; std::cout<<"共享内存key值:"<<key<<", "<<"shmid:"<<shmid<<std::endl;sleep(5);//2. 删除共享内存if(shmctl(shmid,IPC_RMID,nullptr)==0) std::cout<<"删除共享内存成功..."<<std::endl;return0;}

结果如下:

在这里插入图片描述
    • 将共享内存段连接到调用进程的地址空间。
    • 参数shmid是由shmget()返回的共享内存标识符。
    • 参数shmaddr指定了共享内存段附加的位置,通常设置为NULL让系统选择适当的地址。
    • 参数shmflg可以包含标志如SHM_RNDSHM_RDONLY,这里我们设置为0即可。
    • 成功时返回指向共享内存段的指针,出错时返回(void *) -1

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

共享内存创建后必须挂接到进程中,获取到虚拟地址后才可以通过地址进行写入和读取

例如,将创建的共享内存挂接到进程中:

#include<iostream>#include<string>#include<unistd.h>#include<sys/ipc.h>#include<sys/shm.h>const std::string gpath ="/home/tutu/ProcessCommunicate/sharemem";constint ProcessId =0;const size_t gsize =4096;intmain(){//1.创建共享内存 key_t key =ftok(gpath.c_str(),ProcessId);int shmid =::shmget(key,gsize,IPC_CREAT|IPC_EXCL);if(shmid <0) std::cout<<"共享内存创建失败..."<<std::endl; std::cout<<"共享内存key值:"<<key<<", "<<"shmid:"<<shmid<<std::endl;sleep(5);//2. 挂接共享内存void* ret =shmat(shmid,nullptr,0);if((longlong)ret ==-1) std::cout<<"共享内存挂接失败..."<<std::endl;//3. 删除共享内存if(shmctl(shmid,IPC_RMID,nullptr)==0) std::cout<<"删除共享内存成功..."<<std::endl;else std::cout<<"共享内存挂接成功..."<<std::endl;return0;}

结果如下:

在这里插入图片描述
发现共享内存挂接失败,这是因为我们在创建共享内存时没有设置权限,导致我们没有权限挂接,所以在创建共享内存时必须带上权限:shmget(key,gsize,IPC_CREAT|IPC_EXCL|0600);,与文件权限一致0600表示拥有者可读可写,所以正确代码如下:
#include<iostream>#include<string>#include<unistd.h>#include<sys/ipc.h>#include<sys/shm.h>const std::string gpath ="/home/tutu/ProcessCommunicate/sharemem";constint ProcessId =0;const size_t gsize =4096;intmain(){//1.创建共享内存 key_t key =ftok(gpath.c_str(),ProcessId);int shmid =::shmget(key,gsize,IPC_CREAT|IPC_EXCL|0600);if(shmid <0) std::cout<<"共享内存创建失败..."<<std::endl; std::cout<<"共享内存key值:"<<key<<", "<<"shmid:"<<shmid<<std::endl;sleep(5);//2. 挂接共享内存void* ret =shmat(shmid,nullptr,0);if((longlong)ret ==-1) std::cout<<"共享内存挂接失败..."<<std::endl;else std::cout<<"共享内存挂接成功..."<<std::endl;//3. 删除共享内存if(shmctl(shmid,IPC_RMID,nullptr)==0) std::cout<<"删除共享内存成功..."<<std::endl;return0;}
在这里插入图片描述


使用ipcs -m可以看到(注意这里没有删除共享内存才可以看到):

在这里插入图片描述
  1. int shmdt(const void *shmaddr);
    • 断开与共享内存段的连接。
    • 参数shmaddrshmat()返回的指针。
    • 成功时返回0,出错时返回-1。
#include<iostream>#include<string>#include<unistd.h>#include<sys/ipc.h>#include<sys/shm.h>const std::string gpath ="/home/tutu/ProcessCommunicate/sharemem";constint ProcessId =0;const size_t gsize =4096;intmain(){//1.创建共享内存 key_t key =ftok(gpath.c_str(),ProcessId);int shmid =::shmget(key,gsize,IPC_CREAT|IPC_EXCL|0600);if(shmid <0) std::cout<<"共享内存创建失败..."<<std::endl; std::cout<<"共享内存key值:"<<key<<", "<<"shmid:"<<shmid<<std::endl;sleep(5);//2. 挂接共享内存void* ret =shmat(shmid,nullptr,0);if((longlong)ret ==-1) std::cout<<"共享内存挂接失败..."<<std::endl;else std::cout<<"共享内存挂接成功..."<<std::endl;//3.断联共享内存if(shmdt(ret)==0) std::cout<<"断开共享内存成功..."<<std::endl;//4. 删除共享内存if(shmctl(shmid,IPC_RMID,nullptr)==0) std::cout<<"删除共享内存成功..."<<std::endl;return0;}

结果如下:

在这里插入图片描述

创建、挂接、断联以及删除共享内存原理如下:

在这里插入图片描述

✨进程间通信实例

  • 基于以上4个函数,我们就可以封装一个ShareMem类来进行进程间通信:
#pragmaonce#include<iostream>#include<string>#include<cstdio>#include<sys/types.h>#include<sys/ipc.h>#include<sys/shm.h>#include<stdalign.h>#include<unistd.h>const std::string gpath ="/home/tutu/ProcessCommunicate/sharemem";int gprojId =0x6666;// 操作系统,申请空间,是按照块为单位的:4KB,1KB, 2KB, 4MBint gshmsize =4096; mode_t gmode =0600;classShareMem{private:voidCreateShmHelper(int flag){ _key =::ftok(gpath.c_str(), gprojId);if(_key <0){ std::cout <<"创建共享内存时ftok出错..."<< std::endl;return;} _shmid =::shmget(_key, gshmsize, flag);// 不存在返回-1if(_shmid <0){ std::cout <<"创建共享内存时shmget出错..."<< std::endl;return;}}public:ShareMem():_shmid(-1),_key(0),_pshm(nullptr){}// 创建共享内存voidCreateShareMem(){if(_shmid <0)CreateShmHelper(IPC_CREAT | IPC_EXCL | gmode);}//获取共享内存voidGetShareMem(){if(_shmid <0)CreateShmHelper(IPC_CREAT);}//挂接共享内存voidAttachShareMem(){if(_shmid>0) _pshm =::shmat(_shmid,nullptr,0);}//去关联共享内存voidDetachShareMem(){if(_pshm)::shmdt(_pshm);}//删除共享内存voidDeleteShareMem(){if(_shmid >0)::shmctl(_shmid, IPC_RMID,nullptr);}//获取共享内存地址void*GetShareMemAddr(){return _pshm;}private:int _shmid; key_t _key;void* _pshm;}; ShareMem shm;
  • 创建共享内存并写入内容:
因为共享内存的指针是void*,可以是任意类型,也就是说共享内存实际上是通过指针进行读取和写入的,所以这里我们将它强制转化为字符指针进行写入读取。
intmain(){//1.创建共享内存 shm.CreateShareMem();//2.挂接共享内存 shm.AttachShareMem();//3. 获取共享内存地址char*str =(char*)shm.GetShareMemAddr();//强转为字符指针//4. 写入数据到共享内存int n =0;while(n <=10){ str[n]= n+'0'; n++;sleep(2);}//5.断开共享内存 shm.DetachShareMem();//6.删除共享内存 shm.DeleteShareMem();return0;}
  • 获取共享内存并读取内容:
intmain(){//1.获取共享内存 shm.GetShareMem();//2.挂接共享内存 shm.AttachShareMem();//3. 获取共享内存地址char*str =(char*)shm.GetShareMemAddr();//强转为字符指针//4. 读取共享内存内容int n =10;while(n--){printf("%s\n",str);sleep(2);}//5.断开共享内存 shm.DetachShareMem();//6.删除另一个进程删即可return0;}
每隔2s读取一次内容并打印

同时运行两个进程,结果如下:

在这里插入图片描述

4. 共享内存特点

  1. 通信速度最快
这是因为相较于管道通信需要通过文件缓冲区操作而言,共享内存只需通过指针操作即可
  1. 不同进程间共享内存块,但是没有任何保护机制
所以共享内存需要由用户自己完成保护,可以通过命名管道、加锁等操作。

5. 结语

System V共享内存是一种高效的进程间通信(IPC)机制,它允许多个进程共享一块物理内存区域,从而避免了数据的复制,显著提高了数据传输速率。以上就是有关进程间通信中System V共享内存的所有内容啦~ 完结撒花 ~🥳🎉🎉

Read more

Agent Skill黄金三层结构与五步法打造指南:让AI帮你自动生成若依框架代码!

Agent Skill黄金三层结构与五步法打造指南:让AI帮你自动生成若依框架代码!

文章介绍Agent Skill的设计原理与实现方法,重点讲解黄金三层结构(元数据层、指令层、资源层)和五步法打造技能包(定边界、显性化经验、工具与脚本、引入控制流、迭代与反馈)。通过若依代码生成器改造案例,展示如何将项目规范、代码模板打包成可复用Skill,让AI按预设规则自动生成符合规范的代码,提升开发效率并减少重复工作。 读完这篇文章,你将学会: ✅ 什么是 Agent Skill ✅ 设计技能的黄金三层结构 ✅ 五步法打造你的第一个技能包 ✅ 实践拆解:将若依代码生成器改为Agent Skill 前几天有小伙伴在 [Antigravity 进阶指南: 3 种方式复刻 Kiro Spec 模式]那篇文章下留言,想要那个示例里的Spec模式 Skill 包。 我想了想,与其直接给大家丢一个 Skill 文件,不如和大家聊聊什么时候需要创建以及怎么创建Agent Skill。 在让 AI 帮我们生成Skill之前,我们需要先理解 Skill

By Ne0inhk
手把手教你降低AI率:10条指令打破机器逻辑,外加3个降ai工具推荐

手把手教你降低AI率:10条指令打破机器逻辑,外加3个降ai工具推荐

最近很多同学遇到一个共同的问题:论文明明是自己写的,或者已经手动修改了好几轮,但AIGC检测的结果依然很高。 这并非因为你没有努力修改,而是因为目前的检测算法已经发生了变化。过去的查重系统主要关注文字的重复率,而现在的降ai检测系统主要关注在AI生成的文本逻辑过于连贯、结构过于工整,而人类的真实写作往往包含逻辑上的停顿、观点的反复以及主观情绪的表达。 因此,想要有效降低ai率,仅仅进行同义词替换是无效的。我们需要从逻辑和语气层面入手,打破AI生成的完美结构。 本文将直接分享10个具体的改写指令,帮助你调整文章的逻辑与语气。同时,为了解决部分同学时间紧迫的问题,我也实测了3款市面上主流的降ai率工具,希望能为大家提供实质性的参考,帮助大家把数值从AI率99.9%降至5.7%。 一、 为什么手动修改依然无法通过检测? 在介绍具体方法之前,我们需要明确一个事实:许多网传的aigc免费降重方法之所以失效,是因为它们没有触及算法的核心。 目前的检测系统主要依据以下两个特征进行判定: 逻辑过于顺畅:AI生成的文章通常采用直线型逻辑,从论据直接推导至结论,缺乏人类思考时的犹豫和反复

By Ne0inhk
Flutter 组件 google_generative_language_api 适配鸿蒙 HarmonyOS 实战:生成式 AI 集成,构建大语言模型调度与全场景智能推理治理架构

Flutter 组件 google_generative_language_api 适配鸿蒙 HarmonyOS 实战:生成式 AI 集成,构建大语言模型调度与全场景智能推理治理架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 google_generative_language_api 适配鸿蒙 HarmonyOS 实战:生成式 AI 集成,构建大语言模型调度与全场景智能推理治理架构 前言 在鸿蒙(OpenHarmony)生态迈向全场景 AI 赋能、涉及高效的语义理解、自动化内容生成及严苛的端云协同智能隐私保护背景下,如何实现一套既能深度对接 Google 生成式语言模型(如 Gemini、PaLM)、又能保障异步请求高响应性且具备多模态输入处理能力的“AI 调度中枢”,已成为决定应用智能化水平与用户体验代差的关键。在鸿蒙设备这类强调分布式协同与端侧算力按需分配的环境下,如果应用依然采用低效的 REST 手写拼接,由于由于 payload 结构复杂性,极易由于由于“协议解析异常”导致鸿蒙应用在大模型推理环节发生由于由于由于由于通讯阻塞。 我们需要一种能够统一模型调用语义、支持流式(Streaming)响应且符合鸿蒙异步异步并发范式的

By Ne0inhk
【OpenClaw从入门到精通】第01篇:保姆级教程——从零开始搭建你的第一个本地AI助理(2026实测版)

【OpenClaw从入门到精通】第01篇:保姆级教程——从零开始搭建你的第一个本地AI助理(2026实测版)

摘要:本文聚焦2026年开源AI代理工具OpenClaw的本地部署与实操,从核心概念拆解入手,先厘清OpenClaw、Gateway、Skills、ClawHub的关联,再明确硬件系统要求与大模型API-Key准备要点,通过官方一键安装脚本完成本地部署,并配置阿里云百炼API实现大模型对接。以“让AI助理抓取开源中国热门项目”的虚拟实战案例,详细演示Skills调用流程,同时梳理部署中“命令找不到”“API-Key配置失败”等高频问题的解决方法。内容兼顾新手友好性与实操参考性,所有步骤均基于公开技术文档验证,案例为虚拟构建,代码仅作示例未上传GitHub,可指导读者快速搭建本地AI助理并验证核心功能。 优质专栏欢迎订阅! 【DeepSeek深度应用】【Python高阶开发:AI自动化与数据工程实战】【YOLOv11工业级实战】 【机器视觉:C# + HALCON】【大模型微调实战:平民级微调技术全解】 【人工智能之深度学习】【AI 赋能:Python 人工智能应用实战】【数字孪生与仿真技术实战指南】 【AI工程化落地与YOLOv8/v9实战】【C#工业上位机高级应用:高并发通信+性

By Ne0inhk