Linux基础知识点(六-共享内存)

一、共享内存基本概念

什么是共享内存?顾名思义,共享内存就是将内存进行共享,它允许多个不相关的进程访问同一个逻辑内存, 直接将一块裸露的内存放在需要数据传输的进程面前,让它们自己使用。因此,共享内存是效率最高的一种IPC通信机制, 它可以在多个进程之间共享和传递数据,进程间需要共享的数据被放在共享内存区域, 所有需要访问该共享区域的进程都要把该共享区域映射到本进程的地址空间中,因此所有进程都可以访问共享内存中的地址, 就好像它们是由用C语言函数malloc分配的内存一样。

但是,这种共享的内存需要进程自己去维护好,如同步、互斥等工作,比如当进程1在读取共享内存的数据时, 进程2却修改了共享内存中的数据,那么必然会造成数据的混乱,进程1读取到的数据就是错误的,因此,共享内存是属于临界资源,在某一时刻最多只能有一个进程对其操作(读/写数据),共享内存一般不能单独使用,而要配合信号量、互斥锁等协调机制,让各个进程在高效交换数据的同时,不会发生数据践踏、破坏等意外。

共享内存的思想非常简单,进程与进程之间虚拟内存空间本来相互独立,不能互相访问的,但是可以通过某些方式, 使得相同的一块物理内存多次映射到不同的进程虚拟空间之中,这样的效果就相当于多个进程的虚拟内存空间部分重叠在一起, 如下图所示:

当进程1向共享内存写入数据后,共享内存的数据就变化了,那么进程2就能立即读取到变化了的数据, 而这中间并未经过内核的拷贝,因此效率极高。

共享内存有以下特点:

  1. 共享内存是进程间通信中效率最高的方式之一

  2. 共享内存是系统出于多个进程之间通讯的考虑,而预留的的一块内存区,因此共享内存是以传输数据为目的的。

  3. 共享内存允许两个或更多进程访问同一块内存,当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。

  4. 共享内存无同步无互斥,需要配合信号量、互斥锁等协调机制

 二、shmget() - 创建共享内存函数

内核提供了shmget()函数的创建或获取一个共享内存对象,并返回共享内存标识符。函数原型如下:

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);

参数说明:

  • key:标识共享内存的键值,可以有以下取值:

    • 0 或 IPC_PRIVATE。当key的取值为IPC_PRIVATE,则函数shmget()创建一块新的共享内存; 如果key的取值为0,而参数shmflg中设置了IPC_PRIVATE这个标志,则同样将创建一块新的共享内存。

    • 大于0的32位整数:视参数shmflg来确定操作,通过ftok()函数可以获得一个独一无二的key值,key值用来让多个进程来访问同一块共享内存,如果想要多个进程访问同一块共享内存,则各进程中的key要相等。

    • #include <sys/types.h>
      #include <sys/ipc.h>
      key_t ftok(const char* pathname, int proj_id);
      pathname为路径,proj_id为id值,ftok函数会根据路径和id
      成功时返回独一无二的key_t值,失败返回-1
  • size:要创建共享内存的大小,所有的内存分配操作都是以页为单位的,所以即使只申请只有一个字节的内存, 内存也会分配整整一页。
  • shmflg:表示创建的共享内存的模式标志参数,在真正使用时需要与IPC对象存取权限mode(如0600)进行“|”运算来确定共享内存的存取权限。 msgflg有多种情况:

    • IPC_CREAT:如果内核中不存在关键字与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符。

    • IPC_EXCL:如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存则报错。

    • SHM_HUGETLB:使用“大页面”来分配共享内存,所谓的“大页面”指的是内核为了提高程序性能,对内存实行分页管理时,采用比默认尺寸(4KB)更大的分页,以减少缺页中断。Linux内核支持以2MB作为物理页面分页的基本单位。

    • SHM_NORESERVE:不在交换分区中为这块共享内存保留空间。

  • 返回值:shmget()函数的返回值是共享内存的ID。

当调用shmget()函数失败时将产生错误代码,有如下取值:

  • EACCES:key指定的共享内存已存在,但调用进程没有权限访问它

  • EEXIST:key指定的共享内存已存在,而msgflg中同时指定IPC_CREAT和IPC_EXCL标志

  • EINVAL:创建共享内存时参数size小于SHMMIN或大于SHMMAX。

  • ENFILE:已达到系统范围内打开文件总数的限制。

  • ENOENT:给定的key不存在任何共享内存,并且未指定IPC_CREAT。

  • ENOMEM:内存不足,无法为共享内存分配内存。

三、shmat() - 内存映射函数

如果一个进程想要访问一个共享内存,那么需要将其映射到进程的虚拟空间中, 然后再去访问它,那么系统提供的shmat()函数就是把共享内存区对象映射到调用进程的地址空间。函数原型如下: 

#include <sys/type.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);

参数说明

  • shmid:共享内存ID,通常是由shmget()函数返回的。

  • shmaddr:如果不为NULL,则系统会根据shmaddr来选择一个合适的内存区域, 如果为NULL,则系统会自动选择一个合适的虚拟内存空间地址去映射共享内存。

  • shmflg:操作共享内存的方式:

    • SHM_RDONLY:以只读方式映射共享内存。

    • SHM_REMAP:重新映射,此时shmaddr不能为NULL。

    • NULLSHM:自动选择比shmaddr小的最大页对齐地址。

shmat()函数调用成功后返回共享内存的起始地址,这样子我们就能操作这个共享内存了。

共享内存的映射有以下注意的要点:

  • 共享内存只能以只读或者可读写方式映射,无法以只写方式映射。

  • shmat()第二个参数shmaddr一般都设为NULL,让系统自动找寻合适的地址。但当其确实不为空时, 那么要求SHM_RND在shmflg必须被设置,这样的话系统将会选择比shmaddr小而又最大的页对齐地址(即为SHMLBA的整数倍)作为共享内存区域的起始地址。 如果没有设置SHM_RND,那么shmaddr必须是严格的页对齐地址。

四、shmdt() - 解除映射函数

shmdt()函数与shmat()函数相反,是用来解除进程与共享内存之间的映射的,在解除映射后, 该进程不能再访问这个共享内存。函数原型如下:

#include <sys/type.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);

参数说明

  • shmaddr:映射的共享内存的起始地址。

shmdt()函数调用成功返回0,如果出错则返回-1,并且将错误原因存于errno中。

虽然shmdt()函数很简单,但是还是有注意要点的:该函数并不删除所指定的共享内存区, 而只是将先前用shmat()函数映射好的共享内存脱离当前进程,共享内存还是存在于物理内存中。

五、shmctl() -获取或设置属性函数

内核提供了shmctl()用于获取或者设置共享内存的相关属性。函数原型如下: 

#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数说明

  • shmid:共享内存标识符。

  • cmd:函数功能的控制命令,其取值如下:

    • IPC_STAT:获取属性信息,放置到buf中。

    • IPC_SET:设置属性信息为buf指向的内容。

    • IPC_RMID:删除该共享内存。

    • IPC_INFO:获得关于共享内存的系统限制值信息。

    • SHM_INFO:获得系统为共享内存消耗的资源信息。

    • SHM_STAT:与IPC_STAT具有相同的功能,但shmid为该SHM在内核中记录所有SHM信息的数组的下标, 因此通过迭代所有的下标可以获得系统中所有SHM的相关信息。

    • SHM_LOCK:禁止系统将该SHM交换至swap分区。

    • SHM_UNLOCK:允许系统将该SHM交换至swap分。

  • buf:共享内存属性信息结构体指针,设置或者获取信息都通过该结构体,shmid_ds结构如下:

注意:选项SHM_LOCK不是锁定读写权限,而是锁定SHM能否与swap分区发生交换。 一个SHM被交换至swap分区后如果被设置了SHM_LOCK,那么任何访问这个SHM的进程都将会遇到页错误。 进程可以通过IPC_STAT后得到的mode来检测SHM_LOCKED信息。

共享内存写程序

#include <sys/types.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>#define PATHNAME "."
#define PROJ_ID 0x666
#define BUFSIZE 4096int main()
{void *shm = NULL;char buffer[BUFSIZE + 1];//用于保存输入的文本int shmid;//获取key值key_t key = ftok(PATHNAME, PROJ_ID);printf("key value: %d\n", key);//创建共享内存shmid = shmget(key, 4096, 0644 | IPC_CREAT);if(shmid == -1){fprintf(stderr, "shmget failed\n");exit(EXIT_FAILURE);}//将共享内存连接到当前进程的地址空间shm = shmat(shmid, (void*)0, 0);if(shm == (void*)-1){fprintf(stderr, "shmat failed\n");exit(EXIT_FAILURE);}printf("Memory attached at %p\n", shm);printf("Enter some text: ");while(1)//向共享内存中写数据{//向共享内存中写入数据ssize_t s = read(0, buffer, BUFSIZE - 1);if(s > 0){buffer[s - 1] = 0;strncpy(shm, buffer, 4096);//输入了end,退出循环(程序)if(strncmp(buffer, "end", 3) == 0)break;}     }//把共享内存从当前进程的地址空间分离if(shmdt(shm) == -1){fprintf(stderr, "shmdt failed\n");exit(EXIT_FAILURE);}sleep(1);exit(EXIT_SUCCESS);
}

 共享内存读程序

#include <sys/types.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>#define PATHNAME "."
#define PROJ_ID 0x666int main(void)
{char *shm = NULL;//分配的共享内存的原始首地址int shmid;//共享内存标识符//获取key值key_t key = ftok(PATHNAME, PROJ_ID);printf("key value: %d\n", key);//创建共享内存shmid = shmget(key, 4096, IPC_CREAT|IPC_EXCL|0666);if(shmid == -1){fprintf(stderr, "shmget failed\n");exit(EXIT_FAILURE);}//将共享内存连接到当前进程的地址空间shm = shmat(shmid, 0, 0);if(shm == (void*)-1){fprintf(stderr, "shmat failed\n");exit(EXIT_FAILURE);}printf("\nMemory attached at %p\n", shm);while(1)//读取共享内存中的数据{printf("You wrote: %s", (char*)shm);printf("\n");sleep(1);//输入了end,退出循环(程序)if(strncmp(shm, "end", 3) == 0)break;}//把共享内存从当前进程的地址空间中分离if(shmdt(shm) == -1){fprintf(stderr, "shmdt failed\n");exit(EXIT_FAILURE);}//删除共享内存if(shmctl(shmid, IPC_RMID, 0) == -1){fprintf(stderr, "shmctl(IPC_RMID) failed\n");exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);
}

注意:上述代码为了简化并没有加信号量、互斥锁来控制各进程访问共享内存 ,实际项目中需要添加信号量、互斥锁等协调机制。

若发送进程不是通过end字符退出(如Ctrl+C或Ctrl+D),则不会触发读进程主动删除共享内存,在这种情况下可通过 "ipcs -m" 命令查看到该共享内存依然存在,通过 "ipcrm -m [shmid]" 即可删除。

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

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

相关文章

C语言中灵活多变的动态内存,malloc函数 free函数 calloc函数 realloc函数

文章目录 &#x1f680;前言&#x1f680;管理动态内存的函数✈️malloc函数✈️free函数✈️calloc函数✈️realloc函数 &#x1f680;在使用动态内存函数时的常见错误✈️对NULL指针的解引用✈️ 对动态开辟空间的越界访问✈️对非动态开辟内存使用free释放✈️使用free释放一…

VirtualBox + Redhat7.6 +Oracle19C 数据库安装

软件工具&#xff1a; 虚拟化工具&#xff1a;VirtualBox-6.1.26-145957-Win.exe操作系统镜像&#xff1a;rhel-server-7.6-x86_64-dvd.iso远程连接工具&#xff1a;XmanagerPowerSuite-7.0.0004r.exe、SecureCRT 8.5.3数据库版本镜像&#xff1a;LINUX.X64_193000_grid_home.…

机器学习的几个需求层次

机器学习不是“屠龙之技”&#xff0c;它从诞生开始就立足于解决实际问题。你要解决什么样的问题&#xff0c;才决定你需要学习什么样的知识&#xff0c;以及学到什么程度。知道自己需要什么&#xff0c;带着目的去学习确实才是最有效率的。 结合实际问题&#xff0c;分享我对…

机器学习与深度学习——使用paddle实现随机梯度下降算法SGD对波士顿房价数据进行线性回归和预测

文章目录 机器学习与深度学习——使用paddle实现随机梯度下降算法SGD对波士顿房价数据进行线性回归和预测一、任务二、流程三、完整代码四、代码解析五、效果截图 机器学习与深度学习——使用paddle实现随机梯度下降算法SGD对波士顿房价数据进行线性回归和预测 随机梯度下降&a…

微服务整合:构建高效灵活的分布式系统

随着软件开发的不断演进和业务的复杂性增加&#xff0c;微服务架构已经成为一种流行的解决方案。然而&#xff0c;当涉及到多个微服务之间的整合时&#xff0c;我们需要谨慎考虑如何实现高效、灵活的分布式系统。 微服务架构的流行使得软件开发变得更加灵活和可扩展。然而&…

Android aar打包集成问题处理合集

一 概要 aar打包提供给使用方时&#xff0c;可能出现各种问题。譬如&#xff1a;接受方集成后发现编译报错。一般情况下主要有&#xff1a; 1.构建、kotlin语言、插件版本不一致&#xff1b; 2.相应的依赖版本不一致&#xff1b; 3. so库的ndk不一致或者不全&#xff1b;&…

Neuro Contamination - Cyberpunk Gaming Music Futuristic Glitchy Sci-fi

无论是展示赛博朋克未来的电影场景&#xff0c;还是介绍高科技武器&#xff0c;你的音乐选择都至关重要。这首曲子的灵感来自科幻小说&#xff0c;旨在让你的观众想象未来的感觉。 潜在用例&#xff1a;科幻游戏、赛博朋克游戏、电影预告片、动作场景和产品广告。 非常适合充…

【GitHub】ssh: connect to host github.com port 22: Connection refused

本地使用git上传GitHub仓库时发现的一个报错&#xff0c;以为是本机连不上github了&#xff0c;ping过后发现能够正常访问&#xff0c;于是上网找到了一个很完美的解决方案 原因&#xff1a;22端口被占用或被防火墙屏蔽 解决方法&#xff1a;切换GitHub的443端口 1.首先找到…

YOLOv8改进 | 检测头篇 | DynamicHead原论文一比一复现 (不同于网上版本,全网首发)

一、本文介绍 本文给大家带来的改进机制是DynamicHead(Dyhead),这个检测头由微软提出的一种名为“动态头”的新型检测头,用于统一尺度感知、空间感知和任务感知。网络上关于该检测头我查了一些有一些魔改的版本,但是我觉得其已经改变了该检测头的本质,因为往往一些细节上才…

json解析本地数据,使用JSONObject和JsonUtility两种方法。

json解析丨网址、数据、其他信息 文章目录 json解析丨网址、数据、其他信息介绍一、文中使用了两种方法作为配置二、第一种准备2.代码块 二、第二种总结 介绍 本文可直接解析本地json信息的功能示例&#xff0c;使用JSONObject和JsonUtility两种方法。 一、文中使用了两种方法…

分类预测 | Python实现基于SVM-RFE-LSTM的特征选择算法结合LSTM神经网络的多输入单输出分类预测

分类预测 | Python实现基于SVM-RFE-LSTM的特征选择算法结合LSTM神经网络的多输入单输出分类预测 目录 分类预测 | Python实现基于SVM-RFE-LSTM的特征选择算法结合LSTM神经网络的多输入单输出分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 基于SVM-RFE-LSTM的特征…

自定义指令:让 Vue 开发更有趣(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

WPF Blend for visual studio使用

Blend for visual studio介绍 VS自带的Blend for visual studio是专门用来做WPF、Metro等的界面设计的可视化工具&#xff0c;其功能和PS类似。其目的让做界面和后台的程序分开&#xff0c;能快速绘制形状和路径、修改对象样式、动态显示对象(动画)、显示数据等高级操作。VS与B…

FTP服务器安装、远程访问以及安全配置项

一、安装ftp 1、首先&#xff0c;确保您的系统已经更新到最新的软件包版本。运行以下命令来更新软件包列表并安装更新的软件包&#xff1a; sudo yum update 2、安装vsftpd服务器软件包&#xff1a; sudo yum install vsftpd 3、安装完成后&#xff0c;启动vsftpd服务并将…

音视频通信

文章目录 一、音视频通信流程二、流媒体协议1、RTSP2、RTMP3、HLS4、WebRTC 一、音视频通信流程 音视频通信完整流程有如下几个环节&#xff1a;采集、编码、前后处理、传输、解码、缓冲、渲染等。 每一个细分环节&#xff0c;还有更细分的技术模块。比如&#xff0c;前后处…

认识机器学习【woodwhales.cn】

为了更好的阅读体验&#xff0c;建议移步至笔者的博客阅读&#xff1a;认识机器学习 生活中的问题1&#xff1a;居民家庭生活用气价格 北京燃气小程序在线咨询&#xff0c;查询北京居民家庭生活用气价格 上图价格梯度&#xff0c;可以由文字转换成表格&#xff1a; 第一档用气…

linux 内核编译安装

一、配置 默认配置 make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- omap2plus_defconfig原配置 make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- oldconfig 重新配置 make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- menuconfig二 kernel zImage make ARCHarm CRO…

ajax/axios/fetch区别及webSocket通信原理

ajax 不符合现在前端MVVM的浪潮基于原⽣的XHR开发&#xff0c;XHR本⾝的架构不清晰jQuery整个项⽬太⼤&#xff0c;单纯使⽤ajax却要引⼊整个jQuery axios 从 node.js 创建 http 请求⽀持 Promise API客户端⽀持防⽌CSRF提供了⼀些并发请求的接⼝ fetch 更加底层&#xff…

react+umi+antd项目搭建配置

官方文档链接&#xff1a;UmiJS官方文档React官方文档Ant Design官方文档 一、项目搭建 对于react umi项目搭建&#xff0c;umi官方文档已经写的很清晰了&#xff0c;具体请查看上面的umi官方文档链接。这里我不再详细说明&#xff0c;着重说明一些需要注意的地方。 1. 首先…

Python基础知识:整理2 列表的相关操作

1. 查找某元素在列表中的下标索引 2. 在具体的位置插入一个元素 3. 在列表的尾部追加元素 4. 追加元素方式2 5. 删除元素 6. 删除元素方式2 7. 删除元素方式3 8. 清空列表 9. 统计某个元素在列表中出现的次数 10. 统计列表中所有元素的数量 11. 定义空列表