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到,就会一直等待,当读到了才会向下执行代码,这里管道的作用就是为了实现进程同步。
相关文章:
Linux进程通信之共享内存
文章目录 共享内存原理申请共享内存函数(shmget)参数key生成key值示例申请共享内存 挂接到进程地址空间函数(shmat)去关联函数(shmdt)控制共享内存(shmctl)IPC_STATIPC_RMID ipcs其余进程获取该共享内存进程间通信 进程间通信:IPC,…...
接口自动化测试:pytest基础讲解
为什么要做接⼝测试? 只靠前端测试很难确保很⾼的覆盖率。接⼝测试,可以模拟出各种类型的⼊参,包括⼀些在前端模拟不出来的⼊参,还能根据接⼝⽂档的定义,设计出相对完善的⼊参值,在接⼝层保证质量…...
基于JavaWeb+SpringBoot+Vue医院管理系统小程序的设计和实现
基于JavaWebSpringBootVue医院管理系统小程序的设计和实现 源码获取入口Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏[Java 源码获取 源码获取入口 Lun文目录 目录 1系统概述 1 1.1 研究背景 1 1.2研究目的 1 1.3系统设计思想 1 2相关技术 2 2.1微信小程序 2 2.2 …...
收藏这几个开源库,写css你会笑出声
你是否遇到过写css没灵感,写不出酷炫的效果,那这篇文章你一定要看完。知道这几个开源库,它能让你写出炸天的效果并且有效地增加你的摸鱼时长。 1.CSS Inspiration 网址:https://chokcoco.github.io/CSS-Inspiration/#/ CSS Insp…...
给localStorage缓存添加全局监听器
需求:在做单应用页面的时候,每个组件都是独立的,有时候我们a组件里面的东西修改了,需要b组件进行在a组件修改的同时进行响应,就需要监听器,这种时候我们需要定义监听器并且在b组件里面监听,然后…...
blk_mq_init_queue函数学习记录
blk-mq编程,主要要调用两个函数进行初始化工作,blk_mq_init_queue这是第二个。该函数先是申请了struct request_queue结构,这个请求队列后面用于赋值给磁盘那个结构体的相应成员。 struct request_queue *blk_mq_init_queue(struct blk_mq_t…...
高防服务器的工作原理
在当今互联网时代,网络安全问题日益突出,各种网络攻击层出不穷。为了保护企业的网络安全,高防服务器应运而生。那么,你是否了解高防服务器的工作原理呢?下面就让我们一起来探索一下。 高防服务器是一种能够有效抵御各种…...
2023.11.19使用flask制作一个文件夹生成器
2023.11.19使用flask制作一个文件夹生成器 实现功能: (1)在指定路径上建立文件夹 (2)返回文件夹的路径和建立成功与否的提示 main.py import os from flask import Flask, request, jsonify, render_templateapp F…...
【04】ES6:字符串的扩展
一、模板字符串 模板字符串是可以插入表达式的字符串字面量。模板字符串和传统字符串比较,存在以下特点: 1、使用反单引号 传统字符串字面量使用单引号 ‘’ 或者双引号 “”,模板字符串使用反单引号(backquote) …...
Docker可视化管理界面工具Portainer安装
Portainer是Docker容器管理界面工具,可以直观的管理Docker。 部署也很简单: 官方安装文档地址 1、创建数据卷 docker volume create portainer_data2、下载允许容器 docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restartalways -v /v…...
css实现水波纹效果
css实现水波纹效果 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><styl…...
一种全新且灵活的 Prompt 对齐优化技术
并非所有人都熟知如何与 LLM 进行高效交流。 一种方案是,人向模型对齐。 于是有了 「Prompt工程师」这一岗位,专门撰写适配 LLM 的 Prompt,从而让模型能够更好地生成内容。 而另一种更为有效的方案则是,让模型向人对齐。 这也是…...
8:kotlin 类型检查和转换(Type checks and casts)
在运行时可以执行类型检查以检查对象的类型。类型转换将对象强制转换为不同的类型 is 和 !is 可以使用is或者!is来判断实例是不是指定的类型 fun main() {var obj : Any "cast"if (obj is String) {println(obj.length) // 4}obj 123if (obj !is String) { pr…...
命令模式 (Command Pattern)
定义 命令模式(Command Pattern)是一种行为型设计模式,它将一个请求封装为一个对象,从而允许用户使用不同的请求、队列或日志来参数化其他对象。命令模式也支持可撤销的操作。主要目的是将命令的发送者和接收者解耦,引…...
蓝桥杯官网练习题(奇怪的数列)
题目描述 从 X 星截获一份电码,是一些数字,如下: 13 1113 3113132113 1113122113 ⋯ YY 博士经彻夜研究,发现了规律: 第一行的数字随便是什么,以后每一行都是对上一行"读出来" 比如第 2…...
flink的异常concurrent.TimeoutException: Heartbeat of TaskManager with id的解决
背景 在使用flink进行集成测试时,我们会使用MiniClusterWithClientResource类,但是当我们断点导致在某个方法执行的时间比较长时,会有错误发生,那么该如何解决这个错误呢? 处理concurrent.TimeoutException: Heartbe…...
火电安全事故vr模拟仿真培训强交互更真实
VR消防,利用VR虚拟现实技术,将VR和消防教育融合在一起达到寓教于乐的效果, VR消防教育是对于家中、校园内、大型商场、公司办公室等情景产品研发的消防安全培训类VR系统软件,根据互动体验、互动、视角实际操作、视听觉系统多度自然…...
ELK企业级日志分析平台
目录 一、elasticsearch 1、集群部署 2、cerebro部署 3、elasticsearch-head插件部署 4、elasticsearch集群角色分类 二、logstash 1、部署 2、elasticsearch输出插件 3、file输入插件 4、file输出插件 5、syslog 插件 6、多行过滤插件 7、grok过滤 三、kibana数…...
.NET面试题1
1.什么是C#? C#(读作"C sharp")是一种通用的、面向对象的编程语言,由Microsoft开发。它是一种静态类型语言,支持强类型检查和面向对象编程(OOP)的概念。C#主要用于开发Windows应用程序…...
mongodb 日志详情
1 mongodb日志简介 MongoDB的日志包括两个主要部分:操作日志(oplog)和系统日志。 1.1 操作日志 操作日志(oplog)是一个特殊的集合,用于记录所有对数据库进行的操作(如插入、更新和删除&#x…...
大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...
Python 高效图像帧提取与视频编码:实战指南
Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...
