【Linux系统】systemV共享内存

system V共享内存

在Linux系统中,共享内存是一种高效的进程间通信(IPC)机制,它允许两个或者多个进程共享同一块物理内存区域,这些进程可以将这块区域映射到自己的虚拟地址空间中。

共享内存区是最快的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_nattch;    /* no. of current attaches */unsigned short        shm_unused;    /* compatibility */void                  shm_unused2;   /* ditto - used by DIPC */void                  shm_unused3;   /* unused */
};

3.共享内存函数

shmget函数

  1. 功能: 用来创建共享内存,在共享内存中起着关键的初始作用,负责在系统中分配一块共享内存区域或者获取已有的共享内存的表示符
  2. 原型
#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);
  1. 参数
    • key: 共享内存段名字(要用户传入),key是一个键值,用于唯一标识一块共享内存段。(这个键值可以用ftok函数生成),若key值为IPC_PRIVATE,则创建一个新的私有共享内存段,这个段只能通过子进程继承的方式被其他进程访问,通常用于父子进程之间的通信
    • size: 共享内存大小
    • shmflg: 由九个权限标志构成,还包括权限位(用法和创建文件时使用的mode模式标志是一样的)
      • 取值为IPC_CREAT: 共享内存不存在,创建并返回;共享内存已存在,获取并返回。
      • 取值为IPC_CREAT | IPC_EXCL: 共享内存不存在,创建并返回;共享内存已存在,出错返回一个非负整数,即该共享内存段的标识码;失败返回-1(只要成功,所创建的共享内存一定是新的)
  2. 返回值: 成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

ftok函数

ftok 函数是在Linux系统里用于生成System V IPC(Inter-Process Communication,进程间通信)键值的函数。这个函数能够把一个路径名与一个项目ID转换为一个系统V IPC键值,该键值可用于标识共享内存段、消息队列和信号量集等。

  1. 函数原型
#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);
  1. 参数说明
  • pathname:一个存在且可访问的文件或目录的路径名。该文件或目录必须真实存在,因为 ftok 会使用它的索引节点号(inode number)。
  • proj_id:一个项目ID,它是一个1 - 255之间的非零整数。这个ID会和 pathname 的索引节点号结合起来生成IPC键值。
  1. 返回值
  • 若成功,返回一个有效的IPC键值。
  • 若失败,返回 -1,并设置 errno 以指示错误类型。
  1. 示例代码
    以下是一个使用 ftok 函数的简单示例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>int main() {const char *pathname = "/tmp";int proj_id = 'A';key_t key = ftok(pathname, proj_id);if (key == -1) {perror("ftok");return 1;}printf("Generated key: %d\n", (int)key);return 0;
}

在这个示例中,使用 /tmp 作为 pathname'A' 作为 proj_id 来生成一个IPC键值。若生成成功,就会把该键值打印出来;若失败,则会输出错误信息。

  1. 注意事项
  • 要保证 pathname 对应的文件或目录在使用期间不会被删除或替换,不然生成的键值可能会改变。
  • 不同的 pathnameproj_id 组合应该能生成不同的键值,但也不能完全排除生成相同键值的可能性。
  • proj_id 的取值范围是1 - 255,若超出这个范围,可能会导致未定义行为。

shmat函数

  1. 功能: 将共享内存段连接到进程地址空间
  2. 原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
  1. 参数
    • shmid: 共享内存标识
    • shmaddr: 指定共享内存连接到进程空间的起始地址,通常设置为NULL,让系统自动选择一个合适地址自行链接
    • shmflg: 用于控制共享内存与进程地址空间之间的联系方式和访问权限等
      • SHM_RDONLY:表示一只读方式连接共享内存段,若不指定该标志,则默认是读写方式连接
      • SHM_REMAP:若设置该标志,当shmaddr部位NULL时,系统会将指定地址处已经存在的映射替换新的共享内存映射
  2. 返回值: 成功返回一个指针,指向共享内存所连接的进程地址空间的起始位置;失败返回-1

说明:

  1. shmaddrNULL,核心自动选择一个地址
  2. shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
  3. shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA(指定共享内存段在进程地址空间中的最低地址边界)的整数倍。公式: shmaddr - (shmaddr % SHMLBA)
  4. shmflg=SHM_RDONLY,表示连接操作用来只读共享内存

shmdt函数

  1. 功能: 将共享内存段与当前进程脱离,但不删除共享内存段
  2. 原型
int shmdt(const void *shmaddr);
  1. 参数
    • shmaddr: 由shmat所返回的指针
  2. 返回值: 成功返回0;失败返回-1
  3. 注意: 将共享内存段与当前进程脱离不等于删除共享内存段

shmctl函数

  1. 功能: 用于控制共享内存
  2. 原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  1. 参数
    • shmid: 由shmget返回的共享内存标识码
    • cmd: 将要采取的动作 (有三个可取值)
    • buf: 指向一个保存着共享内存的模式状态和访问权限的数据结构
  2. 返回值: 成功返回0;失败返回-1
命令说明
IPC_STATshmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值
IPC_RMID删除共享内存段

4.共享内存的生命周期

在这里插入图片描述

测试代码结构

示例1

server.c

#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>const char* PATH = ".";
const int PROJ_ID = 'a';
const int SHM_SIZE = 1024;int main() {// 生成唯一键值key_t key = ftok(PATH, PROJ_ID);if (key == -1) {perror("ftok");return 1;}// 获取共享内存int shmid = shmget(key, SHM_SIZE, 0666);if (shmid == -1) {perror("shmget");return 1;}// 将共享内存附加到进程地址空间char* shm_addr = static_cast<char*>(shmat(shmid, nullptr, 0));if (shm_addr == reinterpret_cast<char*>(-1)) {perror("shmat");return 1;}// 从共享内存读取数据std::cout << "Reader: Data read from shared memory: " << shm_addr << std::endl;// 将共享内存从进程地址空间分离if (shmdt(shm_addr) == -1) {perror("shmdt");return 1;}// 删除共享内存if (shmctl(shmid, IPC_RMID, nullptr) == -1) {perror("shmctl");return 1;}return 0;
}

client.c

#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <cstring>const char* PATH = ".";
const int PROJ_ID = 'a';
const int SHM_SIZE = 1024;int main() {// 生成唯一键值key_t key = ftok(PATH, PROJ_ID);if (key == -1) {perror("ftok");return 1;}// 创建共享内存int shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);if (shmid == -1) {perror("shmget");return 1;}// 将共享内存附加到进程地址空间char* shm_addr = static_cast<char*>(shmat(shmid, nullptr, 0));if (shm_addr == reinterpret_cast<char*>(-1)) {perror("shmat");return 1;}// 向共享内存写入数据const char* message = "Hello, shared memory!";strcpy(shm_addr, message);std::cout << "Writer: Data written to shared memory." << std::endl;// 将共享内存从进程地址空间分离if (shmdt(shm_addr) == -1) {perror("shmdt");return 1;}return 0;
}

注意: 共享内存没有进行同步与互斥! 共享内存缺乏访问控制! 会带来并发问题。

实例2.借助管道实现访问控制版的共享内存

Comm.hpp

#pragma once
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <cassert>
#include <cstdio>
#include <ctime>
#include <cstring>
#include <iostream>using namespace std;#define Debug 0
#define Notice 1
#define Warning 2
#define Error 3
const std::string msg[] = {"Debug","Notice","Warning","Error"
};
std::ostream &Log(std::string message, int level) {std::cout << " | " << (unsigned)time(nullptr) << " | " << msg[level] << " | " << message;return std::cout;
}
#define PATH_NAME "/home/hyb"
#define PROJ_ID 0x66
#define SHM_SIZE 4096 // 共享内存的大小,最好是页(PAGE: 4096)的整数倍
#define FIFO_NAME "./fifo"
class Init {
public:Init() {int n = mkfifo(FIFO_NAME, 0666);assert(n == 0);(void)n;Log("create fifo success", Notice) << "\n";}~Init() {unlink(FIFO_NAME);Log("remove fifo success", Notice) << "\n";}
};
#define READ O_RDONLY
#define WRITE O_WRONLY
int OpenFIFO(std::string pathname, int flags) {int fd = open(pathname.c_str(), flags);assert(fd >= 0);return fd;
}
void CloseFifo(int fd) {close(fd);
}
void Wait(int fd) {Log("等待中...", Notice) << "\n";uint32_t temp = 0;ssize_t s = read(fd, &temp, sizeof(uint32_t));assert(s == sizeof(uint32_t));(void)s;
}
void Signal(int fd) {uint32_t temp = 1;ssize_t s = write(fd, &temp, sizeof(uint32_t));assert(s == sizeof(uint32_t));(void)s;Log("唤醒中...", Notice) << "\n";
}
string TransToHex(key_t k) {char buffer[32];snprintf(buffer, sizeof buffer, "0x%x", k);return buffer;
}

ShmServer.cc

#include "Comm.hpp"
Init init;
int main() {// 1. 创建公共的Key值key_t k = ftok(PATH_NAME, PROJ_ID);assert(k != -1);Log("create key done", Debug) << " server key : " << TransToHex(k) << endl;// 2. 创建共享内存 -- 建议要创建一个全新的共享内存 -- 通信的发起者int shmid = shmget(k, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666);if (shmid == -1) {perror("shmget");exit(1);}Log("create shm done", Debug) << " shmid : " << shmid << endl;// 3. 将指定的共享内存,挂接到自己的地址空间char* shmaddr = (char*)shmat(shmid, nullptr, 0);Log("attach shm done", Debug) << " shmid : " << shmid << endl;// 4. 访问控制int fd = OpenFIFO(FIFO_NAME, O_RDONLY);while (true) {// 阻塞Wait(fd);// 临界区printf("%s\n", shmaddr);if (strcmp(shmaddr, "quit") == 0)break;}CloseFifo(fd);// 5. 将指定的共享内存,从自己的地址空间中去关联int n = shmdt(shmaddr);assert(n != -1);(void)n;Log("detach shm done", Debug) << " shmid : " << shmid << endl;// 6. 删除共享内存,IPC_RMID, 即便是有进程和当下的shm挂接,依旧删除共享内存n = shmctl(shmid, IPC_RMID, nullptr);assert(n != -1);(void)n;Log("delete shm done", Debug) << " shmid : " << shmid << endl;return 0;
}

ShmClient.cc

#include "Comm.hpp"
int main() {// 1. 创建公共的Key值key_t k = ftok(PATH_NAME, PROJ_ID);if (k < 0) {Log("create key failed", Error) << " client key : " << TransToHex(k) << endl;exit(1);}Log("create key done", Debug) << " client key : " << TransToHex(k) << endl;// 2. 获取共享内存int shmid = shmget(k, SHM_SIZE, 0);if (shmid < 0) {Log("create key failed", Error) << " client key : " << TransToHex(k) << endl;exit(2);}Log("create shm success", Error) << " client key : " << TransToHex(k) << endl;// 3. 挂接共享内存char* shmaddr = (char*)shmat(shmid, nullptr, 0);if (shmaddr == (char*)-1) {Log("attach shm failed", Error) << " client key : " << TransToHex(k) << endl;exit(3);}Log("attach shm success", Error) << " client key : " << TransToHex(k) << endl;// 4. 写int fd = OpenFIFO(FIFO_NAME, O_WRONLY);while (true) {ssize_t s = read(0, shmaddr, SHM_SIZE - 1);if (s > 0) {shmaddr[s - 1] = 0;Signal(fd);if (strcmp(shmaddr, "quit") == 0)break;}}CloseFifo(fd);// 5. 去关联int n = shmdt(shmaddr);assert(n != -1);Log("detach shm success", Error) << " client key : " << TransToHex(k) << endl;return 0;
}

5.共享内存系统级管理指令

1. ipcs

该指令用于显示系统中的进程间通信(IPC)资源信息,其中就包含共享内存段。

  • 显示所有共享内存段
ipcs -m
  • 显示指定用户的共享内存段
ipcs -m -u username

2. ipcrm

此指令用于删除系统中的IPC资源,能够删除指定的共享内存段。

  • 通过ID删除共享内存段
ipcrm -m shmid

其中shmid是共享内存段的ID,可以通过ipcs -m命令获取。

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

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

相关文章

(七)RestAPI 毛子(Http 缓存/乐观锁/Polly/Rate limiting/异步大文件上传)

文章目录 项目地址一、Http Cache1.1 服务注册1.2 Validation with ETag1. 添加ETagMiddleware中间件2. 创建内存ETag存储器3. 服务注册4. 测试二、使用ETag实现乐观锁2.1 添加乐观锁方法2.2 修改Controller2.3 测试乐观锁三、Rate Limiting3.1 添加速率控制服务1. 在Program里…

2025.4.26_STM32_SPI

1.SPI简介 2.硬件电路 所有SPI设备的SCK(时钟)、MOSI(主机输出从机输入)、MISO(主机输入从机输出)分别连在一起。SCK线只能被主机控制&#xff0c;和I2C相同。 主机另外引出多条SS控制线&#xff0c;分别接到各从机的SS引脚 (SS不用的时候为高电平&#xff0c;当主机需要选中某…

JAVA:单例模式

单例模式是设计模式之一 设计模式&#xff0c;就像古代打仗&#xff0c;我们都听过孙子兵法&#xff0c;把计谋概括下来弄成一种模式&#xff0c;形成一种套路。 软件开发中也有很多场景&#xff0c;多数类似的问题场景&#xff0c;解决方案就形成固定的模式&#xff0c;单例…

脑机接口:重塑人类未来的神经增强革命

引言 人类对大脑的探索从未停止&#xff0c;而脑机接口&#xff08;Brain-Computer Interface, BCI&#xff09;的崛起&#xff0c;正在将科幻电影中的“意念操控”变为现实。 这项技术通过解码脑电信号&#xff0c;实现人脑与外部设备的直接交互&#xff0c;不仅为医疗康复带来…

从SOA到微服务:架构演进之路与实践示例

一、架构演进背景 在软件开发领域&#xff0c;架构风格随着业务需求和技术发展不断演进。从早期的单体架构&#xff0c;到面向服务架构(SOA)&#xff0c;再到如今的微服务架构&#xff0c;每一次变革都是为了解决当时面临的核心问题。 二、SOA架构解析 2.1 SOA核心概念 SOA&…

可灵AI 2.0上线:重新定义AI创作?好莱坞级特效触手可及

2025年4月15日&#xff0c;快手正式发布可灵AI 2.0&#xff0c;这款被誉为“让好莱坞特效师颤抖”的AI工具&#xff0c;以物理引擎级动态生成和电影语言自由操控两大核心技术&#xff0c;彻底颠覆了内容创作的想象边界。上线24小时内&#xff0c;全球用户已用它生成超过100万条…

Mysql存储引擎、锁机制

Mysql存储引擎 InnoDB​&#xff08;MySQL 5.5 及以后版本中的默认存储引擎&#xff09; ​​事务支持​​&#xff1a;支持 ​​ACID 事务​​&#xff0c;适合需要高可靠性的场景&#xff08;如支付、订单&#xff09;。 ​​锁机制​​&#xff1a;默认使用 ​​行级锁​​…

飞蛾扑火算法优化+Transformer四模型回归打包(内含MFO-Transformer-LSTM及单独模型)

飞蛾扑火算法优化Transformer四模型回归打包&#xff08;内含MFO-Transformer-LSTM及单独模型&#xff09; 目录 飞蛾扑火算法优化Transformer四模型回归打包&#xff08;内含MFO-Transformer-LSTM及单独模型&#xff09;预测效果基本介绍程序设计参考资料 预测效果 基本介绍 …

音视频开发---视频编码基础

一、视频编码的必要性 1. 存储与传输成本高 未经编码压缩的原始视频的数据量极大,例如:一般电影的亮度信号采样频率为13.5MHz;色度信号的频带通常为亮度信号的一半或更少,为6.75MHz或3.375MHz。以4:2:2的采样频率为例,Y信号采用13.5MHz,色度信号U和V采用6.75MHz采样,…

手动安装OpenSSL1.1.1

根据报错信息 Could not build the ssl module! Python requires a OpenSSL 1.1.1 or newer&#xff0c;说明当前系统中的 OpenSSL 版本低于 1.1.1&#xff0c;或者 Python 编译时未找到符合要求的 OpenSSL 库。以下是分步解决方案&#xff1a; 1. 检查当前 OpenSSL 版本 首先…

[原创](现代Delphi 12指南):[macOS 64bit App开发]: 跨平台开发同样支持retain()引用计数器处理.

[作者] 常用网名: 猪头三 出生日期: 1981.XX.XX 企鹅交流: 643439947 个人网站: 80x86汇编小站 编程生涯: 2001年~至今[共24年] 职业生涯: 22年 开发语言: C/C++、80x86ASM、Object Pascal、Objective-C、C#、R、Python、PHP、Perl、 开发工具: Visual Studio、Delphi、XCode、…

从 “制造” 到 “品牌”:官网建设助力中国企业突围东南亚

在全球产业链重构与区域经济一体化的浪潮下&#xff0c;中国企业出海已从“战略选项”升级为“生存刚需”。东南亚市场因其地理邻近性、人口红利及政策开放性&#xff0c;成为许多中企出海的“首站试验田”。然而&#xff0c;如何在这一文化多元、竞争激烈的市场中建立品牌认知…

iPhone闹钟无法识别调休致用户迟到,苹果客服称会记录反馈

iPhone闹钟无法识别调休致用户迟到&#xff0c;苹果客服称会记录反馈 基于 6 个来源 因“五一”劳动节调休&#xff0c;4月27日&#xff08;周日&#xff09;本应上班&#xff0c;不少iPhone用户却因闹钟未响迟到&#xff0c;“调休”“当苹果闹钟遇到调休”话题登上热搜。苹…

Ubuntu 磁盘空间占用清理(宝塔)

目录 前言1. 基本知识2. 实战 前言 &#x1f91f; 找工作&#xff0c;来万码优才&#xff1a;&#x1f449; #小程序://万码优才/r6rqmzDaXpYkJZF 爬虫神器&#xff0c;无代码爬取&#xff0c;就来&#xff1a;bright.cn 本身自搭建了一个宝塔&#xff0c;突然一下子多了好些空…

杰理-安卓通过map获取时间的时候,部分手机切换sbc和aac时候单耳无声音

杰理-安卓通过map获取时间的时候&#xff0c;部分手机切换sbc和aac时候单耳无声音 #if USER_SUPPORT_PROFILE_MAPif(tws_api_get_role()0){ //主机才获取&#xff0c;否则切换sbc 和 aac 的时候影响单耳无声音user_send_cmd_prepare(USER_CTRL_MAP_READ_TIME,0,NULL);} #endif…

Android 进阶开发:深入掌握 ProgressBar 的使用与高级技巧

一、前言 在 Android 开发中,ProgressBar 是一个非常常见且功能强大的控件,用于向用户反馈操作的进度。虽然它的基础用法简单,但对于进阶开发者来说,如何通过自定义动画、插值器、样式和逻辑控制来提升用户体验,是一个值得深入研究的方向。 本文将带你从 基本使用入手,…

AGILE:开启LLM Agent强化学习的创新框架

在大语言模型&#xff08;LLMs&#xff09;蓬勃发展的今天&#xff0c;基于LLMs构建的智能体成为研究热点。但如何将各组件整合优化仍是难题。本文提出的AGILE框架给出了创新解法&#xff0c;它不仅统一多组件&#xff0c;还让智能体性能超越GPT-4。想知道它是如何做到的吗&…

java使用websocket推送消息到页面

文章目录 一、项目背景二、使用方式1.vue2javaspringpom.xmlRealtimeMonitor.vueMonitorTaskExe.javaWSTopicEnum.javaWServerHelper.java 2.vue3javaspringbootpom.xmlTopologyView.vueAlarmDataInquiryController.javaPushService.javaPushWebSocketHandler.javaWebSocketCon…

小市值策略复现(A股选股框架回测系统)

相关config配置 https://quantkt.com/forumDetail?id201043 很早就知道了小市值模型&#xff0c;正好量化选股回测框架出来了&#xff0c;把最裸的小市值复现下&#xff0c;顺便验证下框架逻辑。 科普: 小市值策略基于 “小市值效应”&#xff0c;即从历史数据来看&#xf…

解决 Flutter 在 iOS 真机上构建失败的问题

在开发 Flutter 应用时&#xff0c;有时会在尝试将应用部署到 iOS 真机时遇到构建失败的问题。错误信息通常类似于以下内容&#xff1a; Could not build the precompiled application for the device. Uncategorized (Xcode): Timed out waiting for all destinations matchi…