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…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换
目录 关键点 技术实现1 技术实现2 摘要: 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式(自动驾驶、人工驾驶、远程驾驶、主动安全),并通过实时消息推送更新车…...
实战设计模式之模板方法模式
概述 模板方法模式定义了一个操作中的算法骨架,并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下,重新定义算法中的某些步骤。简单来说,就是在一个方法中定义了要执行的步骤顺序或算法框架,但允许子类…...
智能职业发展系统:AI驱动的职业规划平台技术解析
智能职业发展系统:AI驱动的职业规划平台技术解析 引言:数字时代的职业革命 在当今瞬息万变的就业市场中,传统的职业规划方法已无法满足个人和企业的需求。据统计,全球每年有超过2亿人面临职业转型困境,而企业也因此遭…...
2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案
一、延迟敏感行业面临的DDoS攻击新挑战 2025年,金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征: AI驱动的自适应攻击:攻击流量模拟真实用户行为,差异率低至0.5%,传统规则引…...
