Linux进程通信之共享内存

文章目录

  • 共享内存原理
  • 申请共享内存函数(shmget)
    • 参数key
    • 生成key值示例
    • 申请共享内存
  • 挂接到进程地址空间函数(shmat)
  • 去关联函数(shmdt)
  • 控制共享内存(shmctl)
    • IPC_STAT
    • IPC_RMID
  • ipcs
  • 其余进程获取该共享内存
  • 进程间通信

进程间通信:IPC,Inter Process Communication

共享内存原理

进程之间通信的本质是:让不同的进程,看到同一份资源。
无论是匿名管道还是命名管道的通信,我们都是在内核空间里的缓冲区进行实现的进程通信,对于这种方式在用户输入时我们在内核空间缓冲区中进行通信,然后再写入物理内存,那么我们是否可以做到直接让进程在物理内存中进行进程间的通信呢?
在这里插入图片描述
每一个进程都有一个虚拟内存(地址),他们通过页表将虚拟地址映射到物理地址上,因此我们可以让操作系统帮我们在物理内存申请一段空间,然后通过页表把这份空间映射到虚拟内存的共享区,这样我们就做到了让不同的进程看到了同一份资源。

申请共享内存函数(shmget)

在这里插入图片描述

shmget:shared memory get,就是获取共享内存
参数:
key:这个共享内存段名字
size:需要创建的共享内存的大小
shmflg:通过类似位图实现的多操作整数
IPC_CREAT(单独):如果你申请的共享内存不存在,存在,就获取返回
IPC_CREAT | IPC_EXCL:如果你申请的共享内存不存在,就创建,存在,就出错返回。这是为了确保我们申请的共享内存,一定是一个新的,没有被别人使用
IPC_EXCL:不单独使用
这段共享内存的权限,千万不要忘记或上 | 权限,不然后面就会导致进程挂接不上

//申请共享内存
int shmid = shmget(key, NUM, IPC_CREAT | 0666);//返回值为共享内存标识符

返回值:
共享内存标识符

参数key

key是一个数字,这个值是随机的,关键是这个随机数在内核中具有唯一性,可以让他对不同的进程拥有唯一的标识,第一个进程创建出key之后,后面的进程只要有了这个key,就可以拿着这个key去和其它进程进行共享内存了。举个例子:这个key就相当于是一把钥匙,而这个共享内存就是一把锁,只要不同的进程拿着这个钥匙,就可以打开这个内存了。那么现在的问题是我们怎么去生成这个key值呢,又这么保证它在内核中具有唯一性呢?
系统中什么具有唯一性呢?对,就是路径,路径就有唯一性,因此我们可以通过一个函数把路径作为参数传给这个函数,函数根据一定的算法帮助我们生成这个key。
库里面给我们提供了生成key的函数:ftok()
在这里插入图片描述
参数:
pathname:文件路径
proj_id:项目id,我们给定的一个int值,目的就是为了和文件路径一起通过一系列的算法生成一个唯一key值
作用:ftok函数通过给的路径和int值就可以帮我们生成一个在内核中具有唯一性的值。
返回值:
在这里插入图片描述
成功返回key,失败返回-1并设置错误码
key存在哪里呢?系统中一定有很多的共享内存,操作系统要想把他们管理起来,就要先描述再组织,因此key一定被存在共享内存的描述对象中。

生成key值示例

#include <sys/types.h>
#include <sys/ipc.h>
#include <iostream>using namespace std;#define PATHNAME "./common.cc" 
#define PROJ_ID 0x8888int main()
{key_t key = ftok(PATHNAME, PROJ_ID);if(key == -1){perror("ftok error");exit(0);}cout << key << endl;return 0;
}

在这里插入图片描述
所给的路径,一般是当前文件的路径,当然也可以给其他文件的,但为了保证唯一性我们一般在哪个路径下生成key就使用哪个路径,但前提是所给的文件路径一定是存在的,如果不存在就会生成失败。

申请共享内存

在这里插入图片描述

//申请共享内存,方式1
int shmid = shmget(key, 1024, IPC_CREAT 0666);
//申请共享内存,方式2
int shmid = shmget(key, 1024, IPC_CREAT|IPC_EXCL 0666);

问题:我们绕了一大圈生成的这个shmid和key都是具有唯一性的,那么我们为什么不直接用key呢?非要再去通过给shmget函数传key生成呢?
key是用于在操作系统内标定唯一性,而shmid是共享内存标识符,只存在于我们的进程内,用来表示资源的唯一性
在这里插入图片描述
共享内存的大小一般是4096(4KB)的整数倍,虽然我们这里申请的是1024,其实系统给我们的就是4096,因为操作系统是按4KB为单位对我们进行分配的,需要注意的是我们只能用1024,多给的我们不可以使用,用了就是越界可能会出现错误

挂接到进程地址空间函数(shmat)

shmat:shared memory attach
返回值为挂接到虚拟地址中的哪个位置
上面我们只是在物理内存中申请了一份共享内存,但是我们还要去把该共享内存挂接到进程地址空间的共享区中,如何做到呢?通过shmat函数来实现
在这里插入图片描述
参数:
shmid:就是申请共享内存函数shmget的返回值,共享内存标识符
shmaddr:让申请的共享内存挂到共享区的哪一个位置(地址),设置成nullptr就是让系统进行决定
shmflg:进程以什么方式去挂接这个共享内存

shmaddr为NULL,核心自动选择一个地址
shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存
返回值:返回一个void类型的指针,指向在物理内存中申请的共享内存在虚拟地址中共享内存的起始地址
共享内存:指在物理内存中申请的共享内存
共享区:指在虚拟内存中的共享区域

在这里插入图片描述

去关联函数(shmdt)

dt:delete touch
给虚拟内存共享区挂接的物理内存,如果进程结束,系统会自动取消关联,但是我们现在不想等到进程结束,想要在进程还在运行时就去关联,怎么做呢?
通过函数shmdt()就可以做到去关联
在这里插入图片描述
参数:
const void* shmaddr:就是在物理内存申请的共享内存挂接到共享区的起始地址,也就是函数shmat的返回值。
返回值:
成功返回0,失败返回-1,并设置错误码
注意:将共享内存段与当前进程脱离不等于删除共享内存段。

//去关联
shmdt(shmaddr);

控制共享内存(shmctl)

在这里插入图片描述
参数:
shmid:共享内存标识符,就是我们通过shmget函数得到的返回值。
cmd:控制这个函数去做什么
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:
成功返回0,失败返回-1
其中参数cmd有三个取值,如下:
在这里插入图片描述

IPC_STAT

用于获取管理该共享内存结构中的数据,这个共享内存的key值,关联个数等数据

struct shmid_ds shmds;
shmctl(shmid, IPC_STAT, &shmds);
cout << "shm size: " << shmds.shm_segsz << endl;
cout << "shm nattch: " << shmds.shm_nattch << endl;
printf("shm key: 0x%x\n",  shmds.shm_perm.__key);
cout << "shm mode: " << shmds.shm_perm.mode << endl;

IPC_RMID

删除共享内存选项
shmdt函数只是把进程和该共享内存的关系从也表中删除从而取消关联,但是我们在物理内存中申请的共享内存并没有被释放,因此我们可以通过shmctl来释放对应的共享内存

ipcs

如果不通过shmctl来释放对应的共享内存,那么即使进程结束,该共享内存仍然是被占用的,因此我们可以手动的来释放该共享内存,操作如下:
在这里插入图片描述
在这里插入图片描述
此时只是去关联了,但是该共享内存仍然被占用没有被释放
通过ipcrm -m选项 + 对应的共享内存标识符来释放该共享内存
在这里插入图片描述

其余进程获取该共享内存

只需要创建一次共享内存,其余的进程只需拿着这个共享内存的标识符就可以得到这个共享内存了

#include <sys/types.h>
#include <sys/ipc.h>
#include <iostream>
#include <sys/shm.h>
#include <unistd.h>#include "log.hpp"using namespace std;#define PATHNAME "/home/Linux3" 
#define PROJ_ID 0x6666
#define NUM 1024key_t GetKey()
{//生成参数唯一key值key_t key = ftok(PATHNAME, PROJ_ID);if(key == -1){perror("ftok error");exit(0);}return key;
}int GetShareMemHelper(int flag)
{key_t key = GetKey();int shmid = shmget(key, NUM, flag);//返回值为共享内存标识符return shmid;
}//创建第一个共享内存
int CreatShm()
{return GetShareMemHelper(IPC_CREAT |IPC_EXCL | 0666);
}//获取已经创建好的共享内存
int GetShm()
{return GetShareMemHelper(IPC_CREAT);
}
#include "common.hpp"int main()
{int shmid = GetShm();//自定义函数,用于获取已经创建好的共享内存char* shmaddr = (char*)shmat(shmid, nullptr, 0);sleep(5);shmdt(shmaddr);sleep(2);shmctl(shmid, IPC_RMID, nullptr);return 0;
}

进程间通信

此时我们就可以向该共享内存写入了,而不是先写入缓冲区,再讲缓冲区数据拷贝到内存,其它进程就直接从该共享内存进行读取就可以了。
但是此时会有一个问题,就是共享内存对于不同进程而言不是同步的,即写进程还没写,读进程就从共享内存中读,此时读到的为空,或者上次的,就一直在那打印,因此我们要让进程同步,等写进程写入了,读进程才开始读,这里举个例子,我们可以通过管道来实现进程同步,因为read函数,如果没有读取到就会一直等待

char c = 'c';
while(1)
{fgets(shmaddr, 1024, stdin);write(fd, &c, 1);//向命名管道写入信息,用来通知读进程可以从共享内存中读取了//通过命名管道使得进程a,b同步}
Init init;
int fd = open(FIFO_FILE, O_RDONLY);
char c;
while (1)
{ssize_t n = read(fd, &c, 1);//等待通知信息,如果没有就退出if(n <= 0){break;}cout << shmaddr;
}

创建一个管道,当向共享内存写入时,该进程通过write函数向管道中写入数据,对于读进程如果一直没有read到,就会一直等待,当读到了才会向下执行代码,这里管道的作用就是为了实现进程同步。

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

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

相关文章

在openSUSE-Leap-15.5-DVD-x86_64中使用deepin-wine-6.0.0.19再使用金山打字通2016

在openSUSE-Leap-15.5-DVD-x86_64中使用deepin-wine-6.0.0.19再使用金山打字通2016 在openSUSE Software官网输入关键字deepin-wine搜索得到fedora-deepin-wine6的作者是xuthus5 https://software.opensuse.org/package/fedora-deepin-wine6 在百度贴吧fedora吧的《fedora下的…

C++11『lambda表达式 ‖ 线程库 ‖ 包装器』

✨个人主页&#xff1a; 北 海 &#x1f389;所属专栏&#xff1a; C修行之路 &#x1f383;操作环境&#xff1a; Visual Studio 2022 版本 17.6.5 文章目录 &#x1f307;前言&#x1f3d9;️正文1.lambda表达式1.1.仿函数的使用1.2.lambda表达式的语法1.3.lambda表达式的使用…

数据结构-深度优先搜索Java实现

目录 一、引言二、算法步骤三、原理演示递归实现非递归实现&#xff08;使用堆栈&#xff09; 四、代码实战五、结论 一、引言 深度优先搜索&#xff08;DFS&#xff09;是一种在图或树中进行搜索的算法&#xff0c;它沿着树的深度遍历树的节点&#xff0c;尽可能深的搜索树的分…

使用C++从0到1实现人工智能神经网络及实战案例

引言 既然是要用C++来实现,那么我们自然而然的想到设计一个神经网络类来表示神经网络,这里我称之为Net类。由于这个类名太过普遍,很有可能跟其他人写的程序冲突,所以我的所有程序都包含在namespace liu中,由此不难想到我姓刘。在之前的博客反向传播算法资源整理中,我列举…

CTF-PWN-QEMU-前置知识

文章目录 QEMU 内存管理(QEMU 如何管理某个特定 VM 的内存)MemoryRegion gpa->hpaFlatView&#xff1a;表示MR 树对应的地址空间FlatRange&#xff1a;存储不同MR对应的地址信息AddressSpace&#xff1a;不同类型的 MemoryRegion树RAMBlock总体简化图 QEMU 设备模拟 &#x…

【Java进阶开发实战】用Java中的Base64数据加密与解密处理

简介 ​ Base64编码,是我们程序开发中经常使用到的编码方法。它是一种基于用64个可打印字符来表示二进制数据的表示方法。它通常用作存储、传输一些二进制数据编码方法, 也是MIME(多用途互联网邮件扩展,主要用作电子邮件标准)中一种可打印字符表示二进制数据的常见编码方法…

Proteus下仿真AT89C51报“串行口通信失败,请检查电平适配是否正确。”解决办法

在Proteus下进行AT89C51串行口仿真时&#xff0c;如果遇到“串行口通信失败&#xff0c;请检查电平适配是否正确”的错误提示&#xff0c;以下是一些解决办法&#xff1a; 1. 了解AT89C51和外部设备的电平要求&#xff1a; 首先&#xff0c;了解AT89C51和外部设备之间的电平…

【华为OD机试python】分班【2023 B卷|100分】

【华为OD机试】-真题 !!点这里!! 【华为OD机试】真题考点分类 !!点这里 !! 题目描述 幼儿园两个班的小朋友在排队时混在了一起,每位小朋友都知道自己是否与前面一位小朋友是否同班,请你帮忙把同班的小朋友找出来。 小朋友的编号为整数,与前一位小朋友同班用Y表示,不同班…

C语言——文件操作

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 我辈皆凡人&#xff0c;用一生铺就的…

C++的new / delete 与 C语言的malloc/realloc/calloc / free 的讲解

在C语言中我们通常会使用malloc/realloc/calloc来动态开辟的空间&#xff0c;malloc是只会开辟你提供的空间大小&#xff0c;并不会初始化内容&#xff1b;calloc不但会开辟空间&#xff0c;还会初始化&#xff1b;realloc是专门来扩容的&#xff0c;当你第一次开辟的空间不够用…

目标检测YOLO实战应用案例100讲-基于YOLO的小目标检测改进算法(续)

目录 3.3基于混合注意力的多尺度特征融合改进方法 3.3.1整体网络架构 3.3.2特征金字塔的构建

Vue 2.0源码分析-实例挂载的实现

Vue 中我们是通过 $mount 实例方法去挂载 vm 的&#xff0c;$mount 方法在多个文件中都有定义&#xff0c;如 src/platform/web/entry-runtime-with-compiler.js、src/platform/web/runtime/index.js、src/platform/weex/runtime/index.js。因为 $mount 这个方法的实现是和平台…

Python 使用tkinter复刻Windows记事本UI和菜单功能(三)

上一篇&#xff1a;Python 使用tkinter复刻Windows记事本UI和菜单功能&#xff08;二&#xff09;-CSDN博客 下一篇&#xff1a;敬请耐心等待&#xff0c;如发现BUG以及建议&#xff0c;请在评论区发表&#xff0c;谢谢&#xff01; 本文章完成了记事本的新建、保存、另存、打…

【技巧】前端开发技巧 增加前端的请求缓存 提高开发效率

定义变量 /*** 开发缓存 开关* 说明* 方便开发使用 提升开发效率* true 打开缓存* false 关闭缓存 这里上线的时候必须改为* type {boolean}*/ const cacheFlag true/*** 排除某个url 方便开发时的数据实时生效* 这里根据开发到哪个功能 实时变更&#xff0c; 比如开…

京东数据分析(京东大数据):2023年10月京东手机行业品牌销售排行榜

鲸参谋监测的京东平台10月份手机市场销售数据已出炉&#xff01; 根据鲸参谋平台的数据显示&#xff0c;今年10月份&#xff0c;京东平台手机行业的销量约340万&#xff0c;环比增长约11%&#xff0c;同比则下滑约2%&#xff1b;销售额为108亿&#xff0c;环比增长约17%&#x…

请你说一下Vue中v-if和v-for的优先级谁更高

v-if 与 v-for简介 v-ifv-forv-if & v-for使用 v-if 与 v-for优先级比较 vue2 中&#xff0c;v-for的优先级高于v-if 例子进行分析 vue3 v-if 具有比 v-for 更高的优先级 例子进行分析 总结 在vue2中&#xff0c;v-for的优先级高于v-if在vue3中&#xff0c;v-if的优先级高…

RubyMine 2023:提升Rails/Ruby开发效率的强大利器

在Rails/Ruby开发领域&#xff0c;JetBrains RubyMine一直以其强大的功能和优秀的性能而备受开发者的青睐。现如今&#xff0c;我们迎来了全新的RubyMine 2023版本&#xff0c;它将为开发者们带来更高效的开发体验和无可比拟的工具支持。 首先&#xff0c;RubyMine 2023提供了…

Java-使用poi-tl根据word模板动态生成word

作者wangsz&#xff0c;想写一些关于word的工具&#xff0c;所以就写了这篇文章 1.首先&#xff0c;先导入所需要的依赖&#xff08;poi相关依赖即可&#xff09; <!-- POI --><dependency><groupId>org.apache.poi</groupId><artifactId>poi&l…

【libGDX】使用Mesh绘制立方体

1 前言 本文主要介绍使用 Mesh 绘制立方体&#xff0c;读者如果对 Mesh 不太熟悉&#xff0c;请回顾以下内容&#xff1a; 使用Mesh绘制三角形使用Mesh绘制矩形使用Mesh绘制圆形 在绘制立方体的过程中&#xff0c;主要用到了 MVP &#xff08;Model View Projection&#xff0…

目标检测YOLO系列从入门到精通技术详解100篇-【目标检测】计算机视觉(最终篇)

目录 知识储备 KITTI数据集 1.KITTI数据集概述 2.数据采集平台 3.Dataset详述 算法原理