当前位置: 首页 > news >正文

进程间通信(二)/共享内存

⭐前言:在前面的博文中分析了什么的进程间通信和进程间通信的方式之一:管道(匿名管道和命名管道)。接下来分析第二种方式:共享内存。

要实现进程间通信,其前提是让不同进程之间看到同一份资源。所谓共享内存,那就是不同进程之间,可以看到内存中同一块资源,这就是共享内存的概念。

共享内存原理

用户通过操作系统提供的系统调用,让操作系统帮助用户去申请一块空间,跟C语言中malloc函数、C++的new的意思差不多。创建好后,将创建好的内存映射到进程地址空间中,然后返回这个地址的起始地址给用户。最后,当结束通信后,就会取消进程和内存的映射关系去掉,然后释放这段内存空间!

而这段内存,就称为共享内存!进程与内存关联的行为称为挂接。取消进程与内存的映射关系,称为去关联。释放这段内存,叫做释放共享内存。

理解共享内存的开辟

①用户申请开辟共享内存空间的系统接口,是专门为了进程间通信而设计出来的,可以让不同进程同时跟其建立关联。跟malloc,new等等的函数不一样,它们虽然也可以在物理内存上开辟空间,但是只能用于本身进程。

②共享内存是一种通信方式,意味着所有想通信的进程都可以使用它。

③既然共享内存是一种通信方式,因此在OS中,一定存在多个共享内存!

实例代码

共享内存函数

按照上图的步骤:第一步,创建共享内存。以下是创建共享内存的两个函数。

①shmget函数

功能:用来创建共享内存
原型:int shmget(key_t key, size_t size, int shmflg);

头文件:#include<sys/ipc.h>  #include<sys/shm.h>
参数:
        key : 这个共享内存段名字。
        size : 共享内存大小
        shmflg : 由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样

其中重要的两个:

IPC_CREAT:如果不存在,创建之。如果存在,获取之。

IPC_EXCL:无法单独使用。需要与IPC_CREAT结合使用,

IPC_CREAT | IPC_EXCL:如果不存在,创建之。如果存在,出错并返回。如果创建成功,那么一定是一个新的共享内存。

返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回 - 1

shmget函数中的参数key,它能够标定唯一性!因为需要保证一个进程去申请共享内存,另外的进程去获取这个共享内存,它们的共享内存是同一个共享内存!而获取key是通过ftok函数来获取的。

②ftok函数

功能:将一个路径明和一个项目标识符转化成一个IPC的key

原型:key_t ftok(const char* pathname , int proj_id);

头文件:#include<sys/ipc.h> #include<sys/types.h>

参数:

        pathname:传进来的字符串

        proj_id:项目标识符

返回值:成功返回key;失败返回-1

只要不同进程在调用ftok的时候,参数一模一样,获取相同的key,再去调用shmget函数,通过同一个key,就能访问同一个共享内存。

补充说明:

共享内存=物理内存块+共享内存的相关属性

上面谈到,OS中一定存在多个共享内存,而OS必须要对这些用户申请开辟的空间进行管理!即先描述再组织,因此,OS会对开辟的共享内存创建一个数据结构,一个共享内存一个数据结构,然后通过链表链接起来,统一管理。于是,在谈到申请开辟一块共享内存,就需要想到:共享内存 = 物理内存块 + 共享内存的相关属性!

key值被包含在了共享内存的属性中。

共享内存的相关属性被包含在共享内存的数据结构中,而其中的key值也包含在了里面。即key值是在shmget函数创建出来后被设置进入共享内存的属性当中,用来表示该共享内存,并表示该共享内存在内核中的唯一性!

shmid和key的关系区分

shmget函数返回值,假设命名为shmid。那么shmid与key的关系就如同在文件IO中的文件描述符fd和inode的关系一样,inode是一个文件一个inode,表示文件的唯一性,key是一个共享内存一个,表示的是共享内存的唯一性,它们都是底层访问目标的工具。但是上层是不用key或inode的,而是使用shmid和fd这样一个特定的整数来访问。一句话来说,一个是用户的,一个是系统的,两个互不干扰,这是它的好处。

查看共享内存指令

ipcs -m

ipc资源的特征

共享内存的生命周期是随操作系统的,不是随进程的,即使进程终止了,但没有去释放这段共享内存,那么它就会一直存在。

删除共享内存

ipcrm -m shmid

按照上图所示:以下是删除共享内存的函数。

③shmctl函数

功能:用于控制共享内存,即删除共享内存,设置共享内存属性等等
原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);

头文件:#include<sys/ipc.h> #include<sys/shm.h>
参数:
        shmid:由shmget返回的共享内存标识码。
        cmd:将要采取的动作(有三个可取值)

动作:

①IPC_STAT:获取共享内存属性

②IPC_SET:设置共享内存属性

③IPC_RMID:删除共享内存
        buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

按照上图所示,以下是将共享内存映射到进程地址空间的函数。

④shmat函数

功能:将共享内存段连接到进程地址空间
原型:void *shmat(int shmid, const void *shmaddr, int shmflg);

头文件:#include<sys/shm.h>   #include<sys/types.h>
参数:
        shmid: 共享内存标识,即想和哪个共享内存关联起来
        shmaddr:指定连接的地址。就是想把这个共享内存映射到哪个进程地址空间中,给出这个进程地址。
        shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY


返回值:成功返回一个指针,指向共享内存;失败返回-1

使用完后,不直接删除共享内存,而是先去关联。以下是去关联的函数。

⑤shmdt函数

功能:将共享内存段与当前进程脱离
原型:int shmdt(const void *shmaddr);

头文件:#include<sys/shm.h>   #include<sys/types.h>
参数:shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段

示例代码代码如下:

代码思路:创建一段共享内存,创建两个没有亲属关系的进程,client进程负责写入,server进程负责读取。

头文件comm.hpp:

#ifndef _COMM_HPP_
#define _COMM_HPP_
#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <sys/ipc.h>
#include <sys/shm.h>#define PATHNAME "."
#define PROJ_ID 0X66//设置共享内存大小:建议为4KB的整数倍
//因为系统分配共享内存是以4KB为单位的!
#define MAX_SIZE 4096//获取key
key_t getKey()
{//通过ftok函数获取keykey_t k = ftok(PATHNAME,PROJ_ID);//获得同一个keyif(k < 0){std::cerr<<errno<<":"<<strerror(errno)<<std::endl;exit(1);}return k;
}
//创建共享内存
int getShmHelper(key_t k,int flags)
{//通过shmget函数创建共享内存。//第一个参数是key,第二个参数是共享内存的大小。第三个参数是权限标志int shmid = shmget(k,MAX_SIZE,flags);//创建共享内存if(shmid<0){std::cerr<<errno<<":"<<strerror(errno)<<std::endl;exit(2);}return shmid;
}
//通过封装函数给用户去使用,只需传入key值即可。
//获取共享内存,不一定要新的,因为不用调用它的进程去创建新的
int getShm(key_t k)
{return getShmHelper(k,IPC_CREAT);
}
//创建共享内存,使用IPC_CREAT | IPC_EXCL,确定创建的共享内存一定是新的。需要给权限0600
int createShm(key_t k)
{return getShmHelper(k,IPC_CREAT | IPC_EXCL | 0600);
}//进程地址空间与共享内存相联
void* attachShm(int shmid)
{//通过shmat函数将共享内存段连接到进程地址空间//传入shmid和指定连接的进程地址的地址,但是这个一般不填,系统会自动去填//第三个参数是权限标志,是对内存只读还是读写。//在Linux系统中,一般是64位。我们这里需要将shmat函数返回的指针判断是否关联成功//强行转化为longlongvoid *men = shmat(shmid,nullptr,0);if((long long)men==-1L){std::cerr<<errno<<":"<<strerror(errno)<<std::endl;exit(3);}return men;//返回起始地址}void detachShm(void* start)
{//通过shmdt函数去关联if(shmdt(start)==-1){std::cerr<<errno<<":"<<strerror(errno)<<std::endl;}
}void delShm(int shmid)
{//通过shmctl函数删除共享内存//第一个参数是函数是需要对哪个共享内存操作,那个共享内存//第二个参数是需要进行什么样的操作//第三个参数一般给nullptrif(shmctl(shmid,IPC_RMID,nullptr)==-1){std::cerr<<errno<<":"<<strerror(errno)<<std::endl;}
}

负责写入的进程程序代码client.cc:

#include"comm.hpp"
#include<unistd.h>int main()
{//第一步:创建key,创建共享内存key_t k = getKey();//获取keyprintf("key: 0x%x\n",k);//查看key值int shmid = getShm(k);//创建共享内存printf("shmid:%d\n",shmid);//查看shmid//第二步:关联内存和进程地址空间char* start = (char*)attachShm(shmid);printf("attach success,address start: %p\n",start);//查看起始地址//开始使用//写下需要往共享内存段写入的数据const char* message = "hello server,我是另一个进程,正在和你通信";pid_t id = getpid();int cnt = 1;while(true){sleep(5);//写入到共享内存段,将共享内存段当字符串,不需要额外char buffer[];snprintf(start,MAX_SIZE,"%s[pid:%d][消息编号:%d]",message,id,cnt++);}//去关联detachShm(start);//这个工程项目不需要删除共享内存return 0;
}

负责读取的进程的程序代码server.cc

#include"comm.hpp"
#include<unistd.h>int main()
{key_t k = getKey();//获取key值printf("key: 0x%x\n",k);//查看key值int shmid = createShm(k);//创建共享内存,必须是新的printf("shmid: %d\n",shmid);//查看共享内存//关联char* start = (char*)attachShm(shmid);printf("attach success, address start: %p\n", start);//使用while(true){//读取共享内存中的数据printf("client say: %s\n",start);//获取共享内存中的属性数据(部分)struct shmid_ds ds;shmctl(shmid,IPC_STAT,&ds);printf("获取属性: size: %d, pid: %d, myself: %d, key: 0x%x",\ds.shm_segsz, ds.shm_cpid, getpid(), ds.shm_perm.__key);sleep(1);}//去关联detachShm(start);//删除共享内存delShm(shmid);return 0;
}

 结果如下:在第一个五秒时,共享内存中没有任何数据。第二个五秒,消息编号为1。第三个五秒,消息编号为2......

 对于从内核数据结构中获取共享内存的属性,发现没有直接显示key值。但实际上key值是在这个内核数据结构中里面的另外一个结构体里面。

共享内存的优缺点

优点:所有使用共享内存的进程通信,速度是最快的!能大大减少数据拷贝的次数!并且生命周期是随系统的!那么,如果我们考虑到同样一份代码,分别使用管道和共享内存的话,并且考虑键盘输入和显示器输出,那么管道有几次拷贝?共享内存有几次拷贝?

 如图,管道的话,需要创建buffer来获取数据,然后通过管道进行通信。而共享内存不需要,因为共享内存可以作为字符串空间,直接写入和读取数据。因此,根据上图所示,管道是6次拷贝,共享内存是4次拷贝。当然,代码不同,拷贝的次数也不会同。

缺点:共享内存没有同步和互斥!

相关文章:

进程间通信(二)/共享内存

⭐前言&#xff1a;在前面的博文中分析了什么的进程间通信和进程间通信的方式之一&#xff1a;管道&#xff08;匿名管道和命名管道&#xff09;。接下来分析第二种方式&#xff1a;共享内存。 要实现进程间通信&#xff0c;其前提是让不同进程之间看到同一份资源。所谓共享内存…...

电路模型和电路定律——“电路分析”

各位CSDN的uu们你们好呀&#xff0c;今天小雅兰的内容是我这学期的专业课噢&#xff0c;首先就学习了电路模型和电路定律&#xff0c;包括电路和电路模型、电流和电压的参考方向、电功率和能量、电路元件、电阻元件、电压源和电流源、基尔霍夫定律。那么现在&#xff0c;就让我…...

软件工程 | 第一章:软件工程学概述

软件工程学概述一、前言二、软件危机1.典型表现2.产生原因3.消除危机途径三、软件工程1.概述2.软件本质特征3.软件工程基本原理4.软件工程方法学1️⃣传统方法学2️⃣面向对象方法学四、软件生命周期五、结语一、前言 本文将讲述软件工程导论的第一章相关知识点&#xff0c;主…...

前端开发页面HEAD作用

文档类型 为每个 HTML 页面的第一行添加标准模式(standard mode)的声明, 这样能够确保在每个浏览器中拥有一致的表现。 <!DOCTYPE html> 语言属性 为什么使用 lang="zh-cmn-Hans" 而不是我们通常写的 lang="zh-CN" 呢? 请参考知乎上的讨论: …...

CSS开发技巧——行为技巧

CSS开发技巧——行为技巧 使用overflow-scrolling支持弹性滚动 iOS页面非body元素的滚动操作会非常卡(Android不会出现此情况)&#xff0c;通过overflow-scrolling:touch调用Safari原生滚动来支持弹性滚动&#xff0c;增加页面滚动的流畅度 场景&#xff1a;iOS页面滚动 使用t…...

PX4之代码结构

PX4开源飞控是目前主流的开源飞控项目&#xff0c;被很多公司作为飞控开发的参考。也广泛被用于现在流行的evtol验证机的飞控&#xff0c;进行初步的飞行验证。可能大多数AAM以及UAM都离不开PX4。 项目代码可以从github下载 $ git clone --recursive GitHub - PX4/PX4-Autopil…...

【C++11】可变参数模板(函数模板、类模板)

在C11之前&#xff0c;类模板和函数模板只能含有固定数量的模板参数。C11增强了模板功能&#xff0c;允许模板定义中包含0到任意个模板参数&#xff0c;这就是可变参数模板。可变参数模板的加入使得C11的功能变得更加强大&#xff0c;而由此也带来了许多神奇的用法。 可变参数模…...

centos安装高版本cmake

之前centos版本为cmake version 2.8.12.2采用yum remove卸载后重装还是这个版本,看来centos下面就是这个最新了,这说明centos煞笔。于是自己下载cmake包,然后安装。 官方cmake链接地址(3.16)(其他版本自己找,链接给你了) 1,wget下载 2,解压: tar -zxf cmake-3.16.0.…...

重温一下C#的时间类型,并简单写一个定时器功能

&#x1f389;&#x1f389; 时间是一个非常抽象的概念&#xff0c;本篇文章我们不深究目前电脑上的时候是如何保持全网同步。主要是讲讲在使用C#编程语言里的时间类型。最后使用定时任务简单写一个提醒功能&#xff0c;比如&#xff1a;每天10点准时打开一次csdn首页&#xff…...

MYSQL查询语句执行顺序

SQL语句定义的顺序 (1) SELECT (2)DISTINCT <select_list> (3) FROM <left_table> (4) <join_type> JOIN <right_table> (5) ON <join_condition> (6) WHERE <where_condition> (7) GROUP BY <group_by_list> (8) WITH {C…...

总结:电容在电路35个基本常识

1 电压源正负端接了一个电容&#xff0c;与电路并联&#xff0c;用于整流电路时&#xff0c;具有很好的滤波作用&#xff0c;当电压交变时&#xff0c;由于电容的充电作用&#xff0c;两端的电压不能突变&#xff0c;就保证了电压的平稳。 当用于电池电源时&#xff0c;具有交流…...

Kroger EDI 855 采购订单确认报文详解

本文着重讲述Kroger EDI项目中&#xff0c;供应商发给Kroger的X12 855EDI 规范报文&#xff08;采购订单确认&#xff09;解读。 在此前的文章如何读懂X12报文中&#xff0c;我们对X12已经做了详细的介绍&#xff0c;大家可以以此为基础&#xff0c;深入了解855采购订单确认报…...

HANA SDA-远程数据源访问

我们需要把其他系统的数据拿过来&#xff0c;到BW里和财务的数据集成。 HANA SDA就是不复制数据&#xff0c;建立虚拟表&#xff08;virtual table&#xff09;来映射到远程数据源。通过这个虚拟表访问其他系统的数据。 对虚拟表的操作现在也可以查询&#xff0c;更新&#xff…...

【AUTOSAR】:OS-Hook

OS-Hook OS-HookPINIC类型1、Os_ErrKernelPanic1.1、Os_HookCallPanicHook1.1.1、OS_PANICHOOK1.1.1.1、Os_PanicHook1.1.1.2、Os_Hal_CoreFreezeOs_Hal_NOPOS-Hook 延伸阅读 延伸阅读 PINIC类型 1、Os_ErrKernelPanic...

Open3d入门

目录 点云数据 1 主成分分析 1.1 Method 1.2 Results 2 表面法线估计 2.1 Method 2.2 Results 3 体素网格下采样 3.1 Method 3.2 Results 点云数据 常用数据下载&#xff08;免积分&#xff09; 1 主成分分析 1.1 Method 对点云进行主成分分析&#xff08;PCA&…...

linux部署zookeeper

linux部署zookeeper 1、单机部署zk ZooKeeper服务器是用Java创建的&#xff0c;它需要在JVM上运行&#xff0c;所以需要使用JDK1.6及以上版本&#xff0c;一般都是jdk1.8。 选择自己安装本地的jdk&#xff0c;而不是centos自带的openjdk。 查看本地安装的jdk&#xff1a; j…...

Junit4升级Junit5汇总

Junit4升级Junit5汇总目录MockMvcBuildersUnnecessaryStubbingException目录 记录Junit4升级到Junit5中遇到的问题和结局方案 MockMvcBuilders 问题&#xff1a; 将Junit4的RunWith和Rule都改成ExtendWith后出现setup函数中MockMvcBuilders的参数不正确 ExtendWith({Spring…...

Axios二次封装和Api的解耦

目录 一、axios三种基本写法 二、axios的二次封装 三、Api的解耦 一、axios三种基本写法 1&#xff09;get方法&#xff08;是最简单的&#xff09;&#xff1a; 写法二&#xff1a; 2&#xff09;post&#xff1a; 3&#xff09;axios请求配置 默认是get请求&#xff0c;如…...

SpringAOP从入门到源码分析大全,学好AOP这一篇就够了(一)

文章目录系列文档索引一、认识AOP1、AOP的引入原因2、AOP常见使用场景日志场景统计场景安防场景性能场景3、AOP概念AOP 的概念Aspect 概念&#xff08;切面&#xff09;Join point 概念&#xff08;连接点&#xff09;Pointcut 概念&#xff08;切入点&#xff09;Advice 概念&…...

【单目标优化算法】樽海鞘群算法(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

基于XGBoost与SHAP的分子气味预测:从特征工程到可解释性分析

1. 项目概述与核心价值在香水设计、食品风味工业乃至环境监测领域&#xff0c;一个核心且持久的挑战是&#xff1a;如何从分子的化学结构出发&#xff0c;准确预测其气味&#xff1f;这不仅仅是化学家或调香师的直觉游戏&#xff0c;更是一个复杂的、高维度的模式识别问题。传统…...

销售怎么通过各种方法获取电话号码

第一种就是那个用爬虫电话号码&#xff0c;然后再打电话给客户。第二种是在别人的挪车电话看车挪车电话&#xff0c;然后再打电话找客户。第三就是。扫楼一顿顿的扫&#xff0c;第四就是这个那种商店&#xff0c;一个个的去问陌拜地推一个个的问店子要不要贷款&#xff0c;去问…...

GitLab External Wiki代理权限绕过漏洞深度解析

1. 这个漏洞不是“修个补丁”就能完事的——它暴露的是 GitLab 权限模型里一个被长期忽视的逻辑断层GitLab 安全漏洞 CVE-2025-2614&#xff0c;光看编号容易误以为是又一个常规的越权或 XSS 类型漏洞。但我在实际复现和审计过程中发现&#xff0c;它根本不是配置疏漏或代码拼写…...

FM3773 低功耗离线式恒流/恒压 PSR 控制器

概述 FM3773 是一种高性能的交流/直流用于电池充电器和适配器的电源控制器&#xff0c;内置 850V 功率三极管。该设备采用脉冲频率调制&#xff08;PFM&#xff09;的方法来建立非连续导通模式&#xff08;DCM&#xff09;反激式电源。 FM3773 提供精确的恒定电压&#xff0c;恒…...

2026长沙智能家居品牌实测,这些本地老牌值得选

2026年&#xff0c;长沙的智能家居市场已经从“概念热”转向“落地战”。我走访了长沙多个本地服务商&#xff0c;实测了不同品牌在别墅、酒店、大平层等场景的真实表现。今天&#xff0c;结合数据与案例&#xff0c;分享几个值得关注的本地品牌&#xff0c;尤其是深耕8年以上的…...

终极Node.js Mock工具:Mockery入门到精通实战教程

终极Node.js Mock工具&#xff1a;Mockery入门到精通实战教程 【免费下载链接】mockery Simplifying the use of mocks with Node.js 项目地址: https://gitcode.com/gh_mirrors/mock/mockery Mockery是Node.js生态中简化Mock使用的终极工具&#xff0c;它为开发者提供了…...

真可用!美团数字人模型开源,MV、电商等统统拿下

美团开源的数字人视频生成框架 LongCat-Video-Avatar 刚刚更新到 1.5 版本。是真能用。这版更新把音频编码器换了&#xff0c;推理步数砍到8步&#xff0c;在770人、13240条主观评分的大规模评测里&#xff0c;雷达图面积全面领先。音频编码器换血&#xff0c;8步出图LongCat-V…...

【DeepSeek灰度发布黄金法则】:20年SRE亲授7步零故障上线实战框架

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;DeepSeek灰度发布策略全景图 DeepSeek模型服务的灰度发布并非简单的流量切分&#xff0c;而是一套融合可观测性、渐进式验证与多维熔断机制的工程化闭环体系。其核心目标是在保障线上推理稳定性的同时&…...

原神私服新纪元:KCN-GenshinServer图形化服务端全功能解析

原神私服新纪元&#xff1a;KCN-GenshinServer图形化服务端全功能解析 【免费下载链接】KCN-GenshinServer 基于GC制作的原神一键GUI多功能服务端。 项目地址: https://gitcode.com/gh_mirrors/kc/KCN-GenshinServer 你是否曾想过拥有一个完全由自己掌控的提瓦特大陆&am…...

如何用Untrunc拯救损坏视频?2025年终极MP4修复工具完全指南

如何用Untrunc拯救损坏视频&#xff1f;2025年终极MP4修复工具完全指南 【免费下载链接】untrunc Restore a damaged (truncated) mp4, m4v, mov, 3gp video. Provided you have a similar not broken video. 项目地址: https://gitcode.com/gh_mirrors/unt/untrunc 当你…...