【Linux】System V 共享内存
文章目录
- 1. 共享内存示意图
- 2. 共享内存数据结构
- 3. 共享内存函数
- `shmget`
- `shmat`
- `shmdt`
- `shmctl`
- 4. 实例代码测试共享内存
- 5. 共享内存相关命令
- 6. System V 消息队列(了解)
- 7. System V 信号量(了解)
共享内存区是最快的 IPC 形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。
1. 共享内存示意图

2. 共享内存数据结构
struct shmid_ds
{struct ipc_perm shm_perm; /* operation perms */int shm_segsz; /* size of segment (bytes) */__kernel_time_t shm_atime; /* last attach time */__kernel_time_t shm_dtime; /* last detach time */__kernel_time_t shm_ctime; /* last change time */__kernel_ipc_pid_t shm_cpid; /* pid of creator */__kernel_ipc_pid_t shm_lpid; /* pid of last operator */unsigned short shm_nattch; /* no. of current attaches */unsigned short shm_unused; /* compatibility */void *shm_unused2; /* ditto - used by DIPC */void *shm_unused3; /* unused */
};
3. 共享内存函数
shmget
功能:用来创建共享内存
原型int shmget(key_t key, size_t size, int shmflg);
参数key:这个共享内存段名字size:共享内存大小shmflg:由九个权限标志构成,它们的用法和创建文件时使用的 mode 模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1
shmat
功能:将共享内存段连接到进程地址空间
原型void* shmat(int shmid, const void* shmaddr, int shmflg);
参数shmid:共享内存标识shmaddr:指定连接的地址shmflg:它的两个可能取值是 SHM_RND 和 SHM_RDONLY
返回值:成功返回一个指针,指向共享内存的地址;失败返回-1
- 说明:
shmaddr 为 NULL,核心自动选择一个地址。
shmaddr 不为 NULL 且 shmflg 无 SHM_RND 标记,则以 shmaddr 为连接地址。
shmaddr 不为 NULL 且 shmflg 设置了 SHM_RND 标记,则连接的地址会自动向下调整为 SHMLBA 的整数倍;公式:shmaddr - (shmaddr % SHMLBA)。
shmflg = SHM_RDONLY,表示连接操作用来只读共享内存。
shmdt
功能:将共享内存段与当前进程脱离
原型int shmdt(const void* shmaddr);
参数shmaddr:由 shmat 所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段
shmctl
功能:用于控制共享内存
原型int shmctl(int shmid, int cmd, struct shmid_ds* buf);
参数shmid:由 shmget 返回的共享内存标识码cmd:将要采取的动作(有三个可取值)buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

4. 实例代码测试共享内存
- 使用共享内存通信,一定是一个进程创建新的 shm,另一个直接获取共享内存即可。
代码结构:
$ ls
Comm.hpp Fifo.hpp Makefile ShmClient.cc ShmServer.cc$ cat Makefile
.PHONY:all
all:shm_client shm_servershm_server:ShmServer.ccg++ -o $@ $^ -std=c++11
shm_client:ShmClient.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f shm_client shm_server
Fifo.hpp:
#ifndef __COMM_HPP__
#define __COMM_HPP__#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <cassert>using namespace std;#define Mode 0666
#define Path "./fifo"class Fifo
{
public:Fifo(const string &path = Path): _path(path){umask(0);int n = mkfifo(_path.c_str(), Mode);if (n == 0){cout << "mkfifo success" << endl;}else{cerr << "mkfifo failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;}}~Fifo(){int n = unlink(_path.c_str());if (n == 0){cout << "remove fifo file " << _path << " success" << endl;}else{cerr << "remove failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;}}private:string _path; // 文件路径 + 文件名
};class Sync
{
public:Sync(): rfd(-1), wfd(-1){}void OpenReadOrDie(){rfd = open(Path, O_RDONLY);if (rfd < 0)exit(1);}void OpenWriteOrDie(){wfd = open(Path, O_WRONLY);if (wfd < 0)exit(1);}bool Wait(){bool ret = true;uint32_t c = 0;ssize_t n = read(rfd, &c, sizeof(uint32_t));if (n == sizeof(uint32_t)){std::cout << "server wakeup, begin read shm..." << std::endl;}else if (n == 0){ret = false;}else{return false;}return ret;}void Wakeup(){uint32_t c = 0;ssize_t n = write(wfd, &c, sizeof(c));assert(n == sizeof(uint32_t));std::cout << "wakeup server..." << std::endl;}~Sync(){}private:int rfd;int wfd;
};#endif
Comm.hpp:
#pragma once#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <string>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>using namespace std;const char *pathname = "/home/ubuntu";
const int proj_id = 0x66;// 在内核中,共享内存的大小是以4KB为基本单位的,你只能用你申请的大小。建议申请大小是N*4KB
const int defaultsize = 4096; // 单位是字节std::string ToHex(key_t k)
{char buffer[1024];snprintf(buffer, sizeof(buffer), "0x%x", k);return buffer;
}key_t GetShmKeyOrDie()
{key_t k = ftok(pathname, proj_id);if (k < 0){std::cerr << "ftok error, errno: " << errno << ", error string: " << strerror(errno) << std::endl;exit(1);}return k;
}int CreateShmOrDie(key_t key, int size, int flag)
{int shmid = shmget(key, size, flag);if (shmid < 0){std::cerr << "shmget error, errno: " << errno << ", error string: " << strerror(errno) << std::endl;exit(2);}return shmid;
}int CreateShm(key_t key, int size)
{// IPC_CREAT: 不存在就创建,存在就获取// IPC_EXCL: 没有意义// IPC_CREAT | IPC_EXCL: 不存在就创建,存在就出错返回return CreateShmOrDie(key, size, IPC_CREAT | 0666);
}int GetShm(key_t key, int size)
{return CreateShmOrDie(key, size, IPC_CREAT);
}void DeleteShm(int shmid)
{int n = shmctl(shmid, IPC_RMID, nullptr);if (n < 0){std::cerr << "shmctl error" << std::endl;}else{std::cout << "shmctl delete shm success, shmid: " << shmid << std::endl;}
}void ShmDebug(int shmid)
{struct shmid_ds shmds;int n = shmctl(shmid, IPC_STAT, &shmds);if (n < 0){std::cerr << "shmctl error" << std::endl;return;}std::cout << "shmds.shm_segez: " << shmds.shm_segsz << std::endl;std::cout << "shmds.shm_nattch: " << shmds.shm_nattch << std::endl;std::cout << "shmds.shm_ctime: " << shmds.shm_ctime << std::endl;std::cout << "shmds.shm_perm.__key: " << ToHex(shmds.shm_perm.__key) << std::endl;
}void *ShmAttach(int shmid)
{// 核心自动选择一个地址void *addr = shmat(shmid, nullptr, 0);if ((long long int)addr == -1){std::cerr << "shmat error" << std::endl;return nullptr;}return addr;
}void ShmDetach(void *addr)
{int n = shmdt(addr);if (n < 0){std::cerr << "shmdt error" << std::endl;}
}
ShmServer:
#include "Comm.hpp"
#include "Fifo.hpp"int main()
{// 1. 获取keykey_t key = GetShmKeyOrDie();std::cout << "key: " << ToHex(key) << std::endl;// 2. 创建共享内存int shmid = CreateShm(key, defaultsize);std::cout << "shmid: " << shmid << std::endl;// 4. 将共享内存和进程进行挂接(关联)char *addr = (char *)ShmAttach(shmid);std::cout << "Attach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;// 0. 先引入管道Fifo fifo;Sync syn;syn.OpenReadOrDie();// 可以进行通信了for (;;){if (!syn.Wait())break;std::cout << "shm content: " << addr << std::endl;}ShmDetach(addr);std::cout << "Detach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;// 3. 删除共享内存DeleteShm(shmid);return 0;
}
ShmClient:
#include "Comm.hpp"
#include "Fifo.hpp"int main()
{key_t key = GetShmKeyOrDie();std::cout << "key: " << ToHex(key) << std::endl;int shmid = GetShm(key, defaultsize);std::cout << "shmid: " << shmid << std::endl;char *addr = (char *)ShmAttach(shmid);std::cout << "Attach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;memset(addr, 0, defaultsize);Sync syn;syn.OpenWriteOrDie();// 可以进行通信了for (char c = 'A'; c <= 'Z'; c++) // shm没有使用系统调用{addr[c - 'A'] = c;syn.Wakeup();sleep(1);}ShmDetach(addr);std::cout << "Detach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;sleep(5);return 0;
}
结果演示:


5. 共享内存相关命令
- 共享内存,如果进程结束,我们没有主动释放它,则共享内存一直存在。
- 共享内存的生命周期是随内核的,只有重启系统它才会自行销毁。
- 为了更好地控制共享内存,我们要会使用命令删除它。
- 不管是指令还是代码,想对共享内存进行控制,都需要使用
shmid,shmid 是共享内存的唯一性标识!
# 查看共享内存
ipcs -m# 删除指定共享内存
ipcrm -m [shmid]
# 实机演示
$ ipcs -m------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 0 root 644 80 2
0x00000000 1 root 644 16384 2
0x00000000 2 root 644 280 2
0x6602fc97 10 ubuntu 666 4096 1$ ipcrm -m 10 # 指定shmid即可删除该共享内存资源
注意:不是必须通过手动来删除,这里只是为了演示相关指令,删除共享内存资源是进程该做的事情。
注意:共享内存没有进程同步与互斥!
6. System V 消息队列(了解)
- 消息队列提供了从一个进程向另外一个进程发送一块数据的方法。
- 每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值。
- IPC 资源必须删除,否则不会自动清除,除非重启,所以 System V IPC 资源的生命周期随内核。
7. System V 信号量(了解)
- 信号量主要用于同步和互斥。
- 由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为进程的互斥。
- 系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源。
- 在进程中涉及到互斥资源的程序段叫临界区。
相关文章:
【Linux】System V 共享内存
文章目录 1. 共享内存示意图2. 共享内存数据结构3. 共享内存函数shmgetshmatshmdtshmctl 4. 实例代码测试共享内存5. 共享内存相关命令6. System V 消息队列(了解)7. System V 信号量(了解) 共享内存区是最快的 IPC 形式。一旦这样…...
拼多多标准推广怎么玩
拼多多标准推广的玩法主要包括以下方面: 拼多多推广可以使用3an推客。3an推客(CPS模式)给商家提供的营销工具,由商家自主设置佣金比例,激励推广者去帮助商家推广商品链接,按最终有效交易金额支付佣金&…...
HFSS学习-day2-T形波导的优化设计
入门实例–T形波导的内场分析和优化设计 HFSS--此实例优化设计 优化设计要求1. 定义输出变量Power31、Power21、和Power11,表示Port3、Port2、Port1的输出功率2.参数扫描分析添加扫描变量和输出变量进行一个小设置添加输出变量进行扫描分析 3. 优化设计,…...
贪吃蛇小游戏(c语言)
1.效果展示 屏幕录制 2024-04-28 205129 2.基本功能 • 贪吃蛇地图绘制 • 蛇吃食物的功能 (上、下、左、右方键控制蛇的动作) • 蛇撞墙死亡 • 蛇撞自身死亡 • 计算得分 • 蛇身加速、减速 • 暂停游戏 3.技术要点 C语言函数、枚举、结构…...
多商户Docker Supervisor进程管理器部署
Dockerfile 根目录下没有Dockerfile的可以复制下面的命令 # 使用基础镜像 FROM leekay0218/crmeb-mer## 复制代码 ## 在本地调试注释掉,使用映射把文件映射进去 #ADD ./ /var/www# 设置工作目录 WORKDIR /var/www# 设置时区为上海 ENV TZAsia/Shanghai RUN ln -sn…...
Vue--》从零开始打造交互体验一流的电商平台(一)
今天开始使用 vue3 ts 搭建一个电商项目平台,因为文章会将项目的每处代码的书写都会讲解到,所以本项目会分成好几篇文章进行讲解,我会在最后一篇文章中会将项目代码开源到我的github上,大家可以自行去进行下载运行,希…...
uniapp 自定义相机插件(组件版、缩放、裁剪)组件 Ba-CameraView
自定义相机插件(组件版、缩放、裁剪) Ba-CameraView 简介(下载地址) Ba-CameraView 是一款自定义相机拍照组件,支持任意界面,支持裁剪 支持任意自定义界面支持手势缩放支持裁剪(手势拖动、比…...
如何在Python中生成随机密码?
你可以使用Python的random模块来生成随机密码。下面是一个生成随机密码的简单示例代码: import random import stringdef generate_random_password(length):characters string.ascii_letters string.digits string.punctuationpassword .join(random.choice(c…...
【Git管理工具】使用Docker+浪浪云服务器部署GitLab服务器
一、什么是GitLab 1.1.GitLab简介 GitLab 是一个开源的 DevOps 平台,它基于 Git 版本控制系统提供了从项目规划、源代码管理到持续集成、持续部署、监控和安全的完整生命周期管理。GitLab 是一个为开发者提供协作工作的工具,它使得团队能够高效地在同一…...
速盾:什么是cdn架构
CDN(Content Delivery Network)即内容分发网络,是一种分布式的架构,用于提高互联网上的内容传输速度和用户体验。CDN架构通过将内容分发到全球多个节点,使用户能够从最近的节点获取内容,从而减少延迟和网络…...
spring高级篇(十)
1、内嵌tomcat boot框架是默认内嵌tomcat的,不需要手动安装和配置外部的 Servlet 容器。 简单的介绍一下tomcat服务器的构成: Catalina: Catalina 是 Tomcat 的核心组件,负责处理 HTTP 请求、响应以及管理 Servlet 生命周期。它包…...
map、set底层封装模拟实现(红黑树)
文章目录 一、红黑树1.1红黑树的规则:1.2红黑树的插入操作1.2.1不需要旋转(如果叔叔存在且为红,这里的C表示孩子,P表示父亲,U表示叔叔,G表示祖父),包含四种情况,无论孩子在哪里&…...
PHP8.2-xlswriter 扩展
https://pecl.php.net/package/xlswriter ### 进入/root/ cd ~ ### 下载扩展 wget https://pecl.php.net/get/xlswriter-1.5.5.tgz ### 解压扩展 tar -zxvf xlswriter-1.5.5.tgz ### 进入扩展目录 cd xlswriter-1.5.5 ### 查找对应php版本的phpize find / -name phpi…...
imx6ull开发板设置SD卡启动,SD卡中烧写uboot,kernel,设备树,根文件系统fs
IMX6ULL ARM Linux开发板SD卡启动,SD卡的分区与分区格式化创建_sd制作分区-CSDN博客...
2024年第七届可再生能源与环境工程国际会议(REEE 2024)即将召开!
2024年第七届可再生能源与环境工程国际会议(REEE 2024)将于2024 年8月28-30日在法国南特举行。共绘绿色未来,全球同频共振!REEE 2024将汇聚全球可再生能源与环境工程领域的专家学者和业界精英,共同探讨行业发展的前沿技…...
【华为】NAT的分类和实验配置
【华为】NAT的分类和实验配置 NAT产生的技术背景IP地址分类NAT技术原理NAT分类静态NAT动态NATNAPTEasy IP(PAT)NAT Server 配置拓扑静态NAT测试抓包 动态NAT测试抓包 NAPT测试抓包 PAT测试抓包 NAT Server检测抓包 PC1PC2服务器 NAT产生的技术背景 随着…...
拉普拉斯丨独家冠名2024年度ATPV技术分论坛,助力产业科技持续创新
为了进一步促进行业技术交流,推进光伏行业发展及标准建设的进程,针对高效电池,领跑组件,新产品认证及应用等技术专题及国内外光伏标准的最新进程,由中国绿色供应链联盟光伏专委会(ECOPV)指导的2…...
LangChain入门教程 - 使用代理Agent
对于大模型,比如某些场景,需要数学计算,或者需要从某些网站获取参考资料,就必须使用专门的代理来完成任务。这里我们使用langchain提供的数学工具来实现一个最简单的例子,下一篇我们会讲如何自己实现代理。 首先创建一…...
windows驱动开发-内核编程技术汇总(五)
使用安全字符串函数 和应用层不一样的是,windows内核完全使用Unicode字符串,许多支持AsciiC的windowsAPI,是在应用层完成项Unicode的切换的。许多系统安全问题是由缓冲区处理不善和生成的缓冲区溢出引起的。 糟糕的缓冲区处理通常与字符串操…...
Java中的optional类是啥和例子
Optional 是 Java 8 引入的一个容器对象,用于表示值存在或不存在。这是一个可以为 null 的容器对象,但使用 Optional 比直接使用 null 更安全,因为 Optional 类提供了许多有用的方法,以便更优雅地处理可能存在或不存在的值。 使用…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...
DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
STM32HAL库USART源代码解析及应用
STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...
【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...
