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…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...

CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...

MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...

C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...

莫兰迪高级灰总结计划简约商务通用PPT模版
莫兰迪高级灰总结计划简约商务通用PPT模版,莫兰迪调色板清新简约工作汇报PPT模版,莫兰迪时尚风极简设计PPT模版,大学生毕业论文答辩PPT模版,莫兰迪配色总结计划简约商务通用PPT模版,莫兰迪商务汇报PPT模版,…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)
安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...