【C++高并发服务器WebServer】-7:共享内存

在这里插入图片描述

本文目录

  • 一、共享内存
    • 1.1 shmget函数
    • 1.2 shmat
    • 1.3 shmdt
    • 1.4 shmctl
    • 1.5 ftok
    • 1.6 共享内存和内存映射的关联
    • 1.7 小demo
  • 二、共享内存操作命令

一、共享内存

共享内存允许两个或者多个进程共享物理内存的同一块区域(通常被称为段)。由于一个共享内存段会称为一个进程用户空间的一部分,因此这种 IPC 机制无需内核介入。所有需要做的就是让一个进程将数据复制进共享内存中,并且这部分数据会对其他所有共享同一个段的进程可用。

共享内存是一种进程间通信(IPC)机制,允许多个进程共享同一块物理内存区域。这种共享内存区域通常被称为共享内存段。共享内存段可以被映射到多个进程的用户空间中,从而实现进程间的高效通信。虚拟内存映射:每个进程的虚拟地址空间中会有一部分被映射到这块共享的物理内存上。当进程访问这块映射的虚拟内存区域时,实际上是在访问共享的物理内存。由于这块内存被映射到进程的用户空间中,进程可以直接访问它,而不需要内核介入。

在这里插入图片描述

与管道等要求发送进程将数据从用户空间的缓冲区复制进内核内存和接收进程将数据从内核内存复制进用户空间的缓冲区的做法相比,这种 IPC 技术的速度更快。内存映射相比共享内存,效率会比较低。直接操作内存效率比较高。

  • 创建或获取共享内存段

调用shmget()函数创建一个新的共享内存段,或者获取一个已存在的共享内存段的标识符(该共享内存段可能由其他进程创建)。这个调用将返回一个共享内存标识符,该标识符将在后续的调用中被使用。

  • 将共享内存段附加到进程的虚拟内存

使用shmat()函数将共享内存段附加到调用进程的虚拟内存中,使其成为进程虚拟内存的一部分。从这一刻起,程序可以像对待其他普通内存一样使用这块共享内存。为了引用这块共享内存,程序需要使用shmat()调用返回的addr值,这是一个指向进程虚拟地址空间中共享内存段起点的指针。

  • 分离共享内存段(可选)

调用shmdt()函数来分离共享内存段。分离后,进程将无法再引用这块共享内存。这一步是可选的,因为在进程终止时,系统会自动完成这一步。

  • 删除共享内存段

调用shmctl()函数来删除共享内存段。只有在所有附加到该共享内存段的进程都与之分离之后,共享内存段才会被销毁。这一步通常只需要一个进程执行即可。

int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
key_t ftok(const char *pathname, int proj_id);

1.1 shmget函数

#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);- 功能:创建一个新的共享内存段,或者获取一个既有的共享内存段的标识。新创建的内存段中的数据都会被初始化为0- 参数:- key : key_t类型是一个整形,通过这个找到或者创建一个共享内存。一般使用16进制表示,非0- size: 共享内存的大小- shmflg: 属性- 访问权限- 附加属性:创建/判断共享内存是不是存在- 创建:IPC_CREAT- 判断共享内存是否存在: IPC_EXCL , 需要和IPC_CREAT一起使用IPC_CREAT | IPC_EXCL | 0664- 返回值:失败:-1 并设置错误号成功:>0 返回共享内存的引用的ID,后面操作共享内存都是通过这个值。
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>int main() {key_t key = 0x1234; // 定义一个共享内存的键值,通常使用16进制表示size_t size = 1024; // 定义共享内存的大小(以字节为单位)int shmflg = IPC_CREAT | 0664; // 设置共享内存的标志和访问权限// 调用 shmget 创建一个新的共享内存段或获取一个已存在的共享内存段的标识符int shmid = shmget(key, size, shmflg);if (shmid == -1) {perror("shmget failed"); // 如果失败,打印错误信息exit(EXIT_FAILURE);}printf("Shared memory created/obtained successfully. ID: %d\n", shmid);return 0;
}

1.2 shmat

void *shmat(int shmid, const void *shmaddr, int shmflg);- 功能:和当前的进程进行关联- 参数:- shmid : 共享内存的标识(ID),由shmget返回值获取- shmaddr: 申请的共享内存的起始地址,指定NULL,内核指定- shmflg : 对共享内存的操作- 读 : SHM_RDONLY, 必须要有读权限- 读写: 0- 返回值:成功:返回共享内存的首(起始)地址。  失败(void *) -1
    // 调用 shmat 将共享内存段附加到当前进程的虚拟内存中// 参数:// - shmid: 共享内存的标识符// - shmaddr: 指定为NULL,让内核选择合适的地址// - shmflg: 0 表示读写权限void *shmaddr = shmat(shmid, NULL, 0);if (shmaddr == (void *)-1) {perror("shmat failed");exit(EXIT_FAILURE);}printf("Shared memory attached at address: %p\n", shmaddr);

1.3 shmdt

int shmdt(const void *shmaddr);- 功能:解除当前进程和共享内存的关联- 参数:shmaddr:共享内存的首地址- 返回值:成功 0, 失败 -1
    if (shmdt(shmaddr) == -1) {perror("shmdt failed");exit(EXIT_FAILURE);}printf("Shared memory detached successfully.\n");

1.4 shmctl

int shmctl(int shmid, int cmd, struct shmid_ds *buf);- 功能:对共享内存进行操作。删除共享内存,共享内存要删除才会消失,创建共享内存的进行被销毁了对共享内存是没有任何影响。- 参数:- shmid: 共享内存的ID- cmd : 要做的操作- IPC_STAT : 获取共享内存的当前的状态- IPC_SET : 设置共享内存的状态- IPC_RMID: 标记共享内存被销毁- buf:需要设置或者获取的共享内存的属性信息- IPC_STAT : buf存储数据- IPC_SET : buf中需要初始化数据,设置到内核中- IPC_RMID : 没有用,NULL
    // 删除共享内存段(可选,通常由最后一个使用它的进程完成)if (shmctl(shmid, IPC_RMID, 0) == -1) {perror("shmctl failed");exit(EXIT_FAILURE);}printf("Shared memory segment deleted.\n");

1.5 ftok

key_t ftok(const char *pathname, int proj_id);- 功能:根据指定的路径名,和int值,生成一个共享内存的key- 参数:- pathname:指定一个存在的路径/home/nowcoder/Linux/a.txt/ - proj_id: int类型的值,但是这系统调用只会使用其中的1个字节范围 : 0-255  一般指定一个字符 'a'
    // 定义一个存在的路径名const char *pathname = "/home/nowcoder/Linux/a.txt";// 定义一个项目ID,通常使用一个字符的ASCII值int proj_id = 'a';// 调用 ftok 生成共享内存的键值key_t key = ftok(pathname, proj_id);if (key == -1) {perror("ftok failed");exit(EXIT_FAILURE);}printf("Generated key: %d\n", key);// 使用生成的键值创建或获取共享内存段size_t size = 1024; // 定义共享内存的大小int shmflg = IPC_CREAT | 0664; // 设置共享内存的标志和访问权限int shmid = shmget(key, size, shmflg);if (shmid == -1) {perror("shmget failed");exit(EXIT_FAILURE);}printf("Shared memory created/obtained successfully. ID: %d\n", shmid);
  • 问题1:操作系统如何知道一块共享内存被多少个进程关联?

操作系统通过维护一个结构体struct shmid_ds来跟踪共享内存段的状态。这个结构体中有一个成员shm_nattch,它记录了当前与该共享内存段关联的进程个数。每当一个进程通过shmat函数附加到共享内存段时,shm_nattch的值会增加;而当一个进程通过shmdt函数分离共享内存段时,shm_nattch的值会减少。通过这种方式,操作系统能够实时了解每个共享内存段的使用情况。

  • 问题2:可不可以对共享内存进行多次删除shmctl?

可以对共享内存进行多次调用shmctl进行删除操作。这是因为shmctl标记共享内存为删除状态,并不是立即删除它。共享内存段的实际删除发生在与之关联的进程数为0时。当shm_nattch的值降为0,表示没有进程再使用该共享内存段,此时操作系统才会真正删除它。此外,当共享内存的key值被设置为0时,表示该共享内存段已被标记为删除。如果一个进程与共享内存取消关联,那么该进程将无法继续操作该共享内存,也不能再次进行关联。

1.6 共享内存和内存映射的关联

共享内存可以直接通过系统调用(如shmget)创建,而内存映射通常需要一个磁盘文件作为后端支持(匿名映射除外)。共享内存的创建更为直接和简单,而内存映射则需要额外的文件支持。

共享内存通常具有更高的效率。由于共享内存允许多个进程直接访问同一块物理内存,因此在进程间通信时,数据传输的开销较小。相比之下,内存映射虽然也可以实现高效的内存访问,但在某些情况下可能需要额外的文件操作,这会增加一定的开销。

共享内存允许多个进程操作同一块物理内存,这意味着所有进程看到的是同一个数据副本。而内存映射则为每个进程在自己的虚拟地址空间中提供了一个独立的内存映射。尽管这些映射可能指向同一块物理内存,但每个进程的操作是独立的,不会直接影响其他进程的内存映射。

即使一个进程突然退出,共享内存仍然存在,其他进程仍然可以继续访问和操作共享内存。但如果一个进程突然退出,该进程的内存映射区会被销毁,其他进程无法再访问该映射区。

如果运行共享内存的电脑死机或宕机,共享内存中的数据会丢失,因为共享内存依赖于系统的内存管理。如果电脑死机或宕机,内存映射区的数据仍然存在,因为这些数据是基于磁盘文件的。只要磁盘文件未被损坏,数据仍然可以恢复。

当进程退出时,该进程的内存映射区会被销毁。这意味着其他进程无法再访问该映射区。
当一个进程退出时,它会自动与共享内存取消关联,但共享内存本身仍然存在。共享内存只有在所有关联的进程都退出后才会被标记为删除。如果系统关机,共享内存中的数据也会丢失。

1.7 小demo

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>int main() {    // 1.获取一个共享内存int shmid = shmget(100, 0, IPC_CREAT);printf("shmid : %d\n", shmid);// 2.和当前进程进行关联void * ptr = shmat(shmid, NULL, 0);// 3.读数据printf("%s\n", (char *)ptr);printf("按任意键继续\n");getchar();// 4.解除关联shmdt(ptr);// 5.删除共享内存shmctl(shmid, IPC_RMID, NULL);return 0;
}
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>int main() {    // 1.创建一个共享内存int shmid = shmget(100, 4096, IPC_CREAT|0664);printf("shmid : %d\n", shmid);// 2.和当前进程进行关联void * ptr = shmat(shmid, NULL, 0);char * str = "helloworld";// 3.写数据memcpy(ptr, str, strlen(str) + 1);//设置一个等待操作,不然会进程直接结束,那么共享内存也没了。printf("按任意键继续\n");getchar();// 4.解除关联shmdt(ptr);// 5.删除共享内存shmctl(shmid, IPC_RMID, NULL);return 0;
}

二、共享内存操作命令

ipcs -a // 打印当前系统中所有的进程间通信方式的信息
ipcs -m // 打印出使用共享内存进行进程间通信的信息
ipcs -q // 打印出使用消息队列进行进程间通信的信息
ipcs -s // 打印出使用信号进行进程间通信的信息

图中的共享内存段的键是0x0000,已经被标记删除了,但是因为连接数还存在,所以没有被删除。
在这里插入图片描述

ipcrm -M shmkey // 移除用shmkey创建的共享内存段
ipcrm -m shmid // 移除用shmid标识的共享内存段
ipcrm -Q msgkey // 移除用msqkey创建的消息队列
ipcrm -q msqid // 移除用msqid标识的消息队列
ipcrm -S semkey // 移除用semkey创建的信号
ipcrm -s semid // 移除用semid标识的信号

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

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

相关文章

【C语言指针】数组指针和指针数组

一、数组指针 1.1 含义 数组指针本质是一个指针&#xff0c;它指向一个数组也就是说它指向数组在内存中的起始地址。数组指针可以用来处理多维数组&#xff0c;尤其是二维数组。 1.2 数组指针的一般形式 首先数组的一般形式是&#xff1a; int a[10] {1,2,3,4,5};这里a代…

关于av_get_channel_layout_nb_channels函数

问题&#xff1a;ffmpeg5.1 使用av_get_channel_layout_nb_channels函数时报错。 过程&#xff1a;经过检查&#xff0c;发现对应头文件内已经不包含该函数。遂查找资料&#xff0c;发现在ffmpeg5.1之后该函数被废弃&#xff0c;具体而言&#xff0c;新增了AVChannelLayout。 …

CrypTen——基于pytorch的隐私保护机器学习框架

目录 一、CrypTen概述 二、应用场景 三、CrypTen优势 四、CrypTen技术解析 1.基于pytorch的构建基础 2.核心密码学原语 3.加密模型训练流程 五、传统隐私保护技术与CrypTen的对比 1.传统隐私保护技术介绍 2.CrypTen与传统隐私保护技术的区别 六、CrypTen的环境配置…

ES6 简单练习笔记--变量申明

一、ES5 变量定义 1.在全局作用域中 this 其实就是window对象 <script>console.log(window this) </script>输出结果: true 2.在全局作用域中用var定义一个变量其实就相当于在window上定义了一个属性 例如: var name "孙悟空" 其实就相当于执行了 win…

Arduino大师练成手册 -- 控制 PN532 NFC 模块

要在 Arduino 上控制 PN532 NFC 模块&#xff0c;你可以按照以下步骤进行&#xff1a; 硬件连接 VCC&#xff1a;连接到 Arduino 的 3.3V 引脚。 GND&#xff1a;连接到 Arduino 的 GND 引脚。 SDA&#xff1a;连接到 Arduino 的 SDA 引脚&#xff08;通常是 A4&#xff09…

.NET Core跨域

CORS 跨域通讯的问题。解决方案&#xff1a;JSONP、前端代理后端请求、CORS等。CORS原理&#xff1a;在服务器的响应报文头中通过access-control-allow-origin告诉浏览器允许跨域访问的域名。在Program.cs的“var appbuilder.Build()”这句代码之前注册 string[] urls new[] …

python——Django 框架

Django 框架 1、简介 Django 是用python语言写的开源web开发框架&#xff0c;并遵循MVC设计。 Django的**主要目的是简便、快速的开发数据库驱动的网站。**它强调代码复用&#xff0c;多个组件可以很方便的以"插件"形式服务于整个框架&#xff0c;Django有许多功能…

大模型正确调用方式

1、ollama 安装 curl -fsSL https://ollama.com/install.sh | sh 如果是AutoDl服务器&#xff0c;可以开启学术加速。 source /etc/network_turbo 本次使用腾讯云Cloud Studio&#xff0c;所以已经安装好了 Ollama 2、启动 ollama run 模型的名字 ollama serve # 开启服务 olla…

CE-PBFT:大规模联盟区块链的高可用一致性算法

摘要 区块链已广泛应用于农产品溯源、供应链管理、物流运输等各个领域。作为联盟区块链不可缺少的组成部分&#xff0c;共识算法保证了网络中每个节点的一致性和可信度。然而&#xff0c;由于通信过程的复杂性&#xff0c;现有的大规模联盟区块链场景中的共识算法存在低系统吞…

2025年新开局!谁在引领汽车AI风潮?

汽车AI革命已来。 在2025年伊始开幕的CES展上&#xff0c;AI汽车、AI座舱无疑成为了今年汽车行业的最大热点。其中不少车企在2025年CES上展示了其新一代AI座舱&#xff0c;为下一代智能汽车的人机交互、场景创新率先打样。 其中&#xff0c;东软集团也携带AI驱动、大数据支撑…

通义灵码插件保姆级教学-IDEA(安装及使用)

一、JetBrains IDEA 中安装指南 官方下载指南&#xff1a;通义灵码安装教程-阿里云 步骤 1&#xff1a;准备工作 操作系统&#xff1a;Windows 7 及以上、macOS、Linux&#xff1b; 下载并安装兼容的 JetBrains IDEs 2020.3 及以上版本&#xff0c;通义灵码与以下 IDE 兼容&…

Kafka常见问题之 `javax.management.InstanceAlreadyExistsException`

文章目录 Kafka常见问题之 javax.management.InstanceAlreadyExistsException1. 概述2. 常见原因3. 具体异常示例4. 解决方案4.1 确保单一 Kafka Producer 实例4.2 配置 Kafka Broker 和 Producer 使用唯一的 JMX 名称&#xff08;对于Producer重点检查 client.id&#xff09;4…

跟我学C++中级篇——64位的处理

一、计算机的发展 计算机从二进制为基础开始描述整个世界&#xff0c;但正如现实世界一样&#xff0c;十进制为主的世界也会有万千百概念。所以在实际的应用中&#xff0c;会出现32位和64位的计算机系统。当然&#xff0c;前面还有过16位、8位和4位等&#xff0c;以后还可以会…

文献阅读 250125-Accurate predictions on small data with a tabular foundation model

Accurate predictions on small data with a tabular foundation model Accurate predictions on small data with a tabular foundation model | Nature 使用一种基于表格的模型来对小型数据实现准确预测 ## Abstract: 基于其他列来填充标签列中缺失值的基本预测任务对于各种应…

dup2 + fgets + printf 实现文件拷贝

思路 将源文件的内容读取到内存中&#xff0c;然后将这些内容写入到目标文件。 1: 打开源文件、目标文件 fopen() 以读模式打开源文件。 open ()以写模式打开目标文件。 2: 读取源文件、写入目标文件 fgets ()从源文件中读取内容。 printf ()将内容写入目标文件。 printf…

通过配置核查,CentOS操作系统当前无多余的、过期的账户;但CentOS操作系统存在共享账户r***t

通过配置核查,CentOS操作系统当前无多余的、过期的账户;但CentOS操作系统存在共享 核查CentOS操作系统中的用户账户&#xff0c;可以使用以下命令&#xff1a; 查看当前活跃用户&#xff1a; awk -F: /\$1\$/{print $1} /etc/shadow 查看多余账户&#xff08;非活跃账户&…

Leetcode40: 组合总和 II

题目描述&#xff1a; 给定一个候选人编号的集合 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用 一次 。 注意&#xff1a;解集不能包含重复的组合。 代码思路&#xff…

C++——list的了解和使用

目录 引言 forward_list与list 标准库中的list 一、list的常用接口 1.list的迭代器 2.list的初始化 3.list的容量操作 4.list的访问操作 5.list的修改操作 6.list的其他操作 二、list与vector的对比 结束语 引言 本篇博客要介绍的是STL中的list。 求点赞收藏评论…

Charles 4.6.7 浏览器网络调试指南:HTTPS抓包(三)

概述 在现代互联网应用中&#xff0c;网络请求和响应是服务交互的核心。对于开发者和测试人员来说&#xff0c;能够准确捕获并分析这些请求&#xff0c;是保证系统稳定性和性能的关键。Charles作为一个强大的网络调试工具&#xff0c;不仅可以捕获普通的HTTP请求&#xff0c;还…

js手撕 | 使用css画一个三角形 使用js修改元素样式 驼峰格式与“-”格式相互转化

1.使用css画一个三角形 借助 border 实现&#xff0c;在 width 和 height 都为 0 时&#xff0c;设置 border&#xff0c;便会呈现三角形。想要哪个方向的三角形&#xff0c;设置其他三边为 透明即可。同时&#xff0c;可以通过调整不同边的宽度&#xff0c;来调整三角形的高度…