【C++高并发服务器WebServer】-5:内存映射与进程通信

在这里插入图片描述

本文目录

  • 一、内存映射与进程通信
  • 二、匿名映射与进程通信

一、内存映射与进程通信

内存映射Memory-mapped I/O指的是将磁盘文件的数据映射到内存,用户通过修改内存就能够修改磁盘文件,如下图所示(进程地址空间指的是虚拟地址空间)。

在这里插入图片描述
如果两个进程都对同一块文件进行内存映射,也可以达到进程通信(进程之间有无关系都可以实现)的效果。

如果两个进程都使用了 MAP_SHARED 标志,那么它们共享同一块物理内存区域。这意味着操作系统会将文件的内容加载到物理内存中,并将两个进程的虚拟地址空间中的相应地址映射到同一块物理内存。

当一个进程修改了共享内存区域中的数据时,这些修改会反映到物理内存中,其他进程访问时也会看到这些修改。

虚拟地址空间本身并不是“加载”到物理内存中的。虚拟地址空间是一个逻辑上的地址范围,由操作系统管理。物理内存是实际存储数据的地方。当进程访问虚拟地址空间中的某个地址时,操作系统会通过页表将虚拟地址映射到物理内存地址。如果物理内存不足,操作系统会将一些不常用的页面交换到磁盘上的交换空间(Swap Space)中,以释放物理内存供其他用途。虚拟地址空间中的地址并不是直接对应物理内存的地址,而是通过页表(Page Table)映射到物理内存或其他存储设备(如磁盘)。

打个比方,假设现在有两个进程:
进程1:虚拟地址空间中的地址范围 [0x1000, 0x2000) 被映射到文件 shared_file。操作系统将这个虚拟地址范围映射到物理内存中的某个区域,例如 [0x10000, 0x11000)。
对于进程2:进程2:虚拟地址空间中的地址范围 [0x3000, 0x4000) 也被映射到文件 shared_file。操作系统将这个虚拟地址范围映射到相同的物理内存区域 [0x10000, 0x11000)。

下面是内存映射相关系统的调用。

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t
offset);
int munmap(void *addr, size_t length);

我们来使用个简单的案例demo来看看如何通过内存映射进行进程通信。

首先创建实现mmap的代码,然后在同级目录下写一个test.txt文件,里面可以写任意数据。

/*#include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);- 功能:将一个文件或者设备的数据映射到内存中(在当前调用进程的虚拟地址空间中)- 参数:- void *addr: NULL, 由内核指定- length : 要映射的数据的长度,这个值不能为0。建议使用文件的长度。获取文件的长度:stat lseek(默认取分页的大小)- prot : 对申请的内存映射区的操作权限-PROT_EXEC :可执行的权限-PROT_READ :读权限-PROT_WRITE :写权限-PROT_NONE :没有权限要操作映射内存,必须要有读的权限。PROT_READ、PROT_READ|PROT_WRITE(Read按位或Write)- flags :- MAP_SHARED : 映射区的数据会自动和磁盘文件进行同步,进程间通信,必须要设置这个选项- MAP_PRIVATE :不同步,内存映射区的数据改变了,对原来的文件不会修改,会重新创建一个新的文件。(copy on write)- fd: 需要映射的那个文件的文件描述符- 通过open得到,open的是一个磁盘文件- 注意:文件的大小不能为0,open指定的权限不能和prot参数有冲突。prot: PROT_READ                open:只读/读写 prot: PROT_READ | PROT_WRITE   open:读写- offset:偏移量,一般不用。必须指定的是4k的整数倍,0表示不偏移。- 返回值:返回创建的内存的首地址失败返回MAP_FAILED,(void *) -1int munmap(void *addr, size_t length);- 功能:释放内存映射- 参数:- addr : 要释放的内存的首地址- length : 要释放的内存的大小,要和mmap函数中的length参数的值一样。
*//*使用内存映射实现进程间通信:1.有关系的进程(父子进程)- 还没有子进程的时候- 通过唯一的父进程,先创建内存映射区- 子进程会继承父进程的虚拟内存空间,包括所有已映射的内存区域。如果先创建子进程,再在父进程中创建内存映射区,那么子进程将无法访问这个映射区,因为它是在子进程创建之后才被创建的。- 有了内存映射区以后,创建子进程- 父子进程共享创建的内存映射区2.没有关系的进程间通信- 准备一个大小不是0的磁盘文件- 进程1 通过磁盘文件创建内存映射区- 得到一个操作这块内存的指针- 进程2 通过磁盘文件创建内存映射区- 得到一个操作这块内存的指针- 使用内存映射区通信注意:内存映射区通信,是非阻塞。
*/
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <wait.h>int main() {// 1.打开一个文件int fd = open("test.txt", O_RDWR);int size = lseek(fd, 0, SEEK_END);  // 获取文件的大小// 2.创建内存映射区void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);//MAP_FAILED是一个宏,通常是一个空指针(NULL)或者一个特殊的无效地址。if(ptr == MAP_FAILED) {perror("mmap");exit(0);} // 3.创建子进程pid_t pid = fork();if(pid > 0) {wait(NULL);// 父进程char buf[64];//strcpy的原型;char *strcpy(char *dest, const char *src);//buf 是目标字符串的指针,类型为 char *。//ptr 是 void * 类型,而strcpy的第二个参数需要的是 const char *类型//所以进行了强制转换。strcpy(buf, (char *)ptr); //strncpy 会在遇到 \0 时停止拷贝printf("read data : %s\n", buf);}else if(pid == 0){// 子进程strcpy((char *)ptr, "nihao a, son!!!");}// 关闭内存映射区munmap(ptr, size);return 0;
}

下面是一些使用内存映射的注意事项:

  1. 如果对mmap的返回值(ptr)做++操作(ptr++), munmap是否能够成功?
    可以对ptr进行++操作,但是munmap会错误,需要提前保存++前的内存地址

  2. 如果open时O_RDONLY, mmap时prot参数指定 PROT_READ | PROT_WRITE 会怎样?
    错误, 会返回 MAP_FAILED (即,void* -1)
    open() 函数中的权限建议和prot参数的权限保持一致。

  3. 如果文件偏移量为1000会怎样?
    偏移量必须是4k的整数倍, 返回MAP_FAILED (分页大小)

  4. mmap什么情况下会调用失败?

    • 第二个参数: length = 0
    • 第三个参数: prot权限有问题
      • 只指定了写权限
      • prot PROT_READ | PROT_WRITE
        第五个参数fd 通过open函数时 未指定 O_RDONLY | O_WRONLY
  5. 可以open的时候O_CREATE一个新文件来创建映射区吗?

    • 可以, 但是创建文件大小为0的话,肯定不行
    • 可以对新的文件进行扩展
      • lseek()
      • truncate()
  6. mmap后关闭文件描述符,对mmap映射有没有影响?
    int fd = open(“xxx”)
    mmap(, fd, 0);
    close(fd);
    映射区还存在, 创建映射区的fd被关闭, 没有任何影响

  7. 对ptr越界操作会怎样?
    越界操作,操作的是非法内存,-> 段错误

接下来我们实现一个使用内存映射来完成文件拷贝的功能demo:

/*基于内存映射实现文件的拷贝功能思路:1、对原始文件进行内存映射。2、创建一个新文件(拓展该文件)3、把新文件的数据映射到内存中。3、通过内存拷贝将第一个文件的内存数据拷贝到新的文件内存中。 5、释放资源。
*/#include<stdio.h>
#include<sys/mman.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>int main(){int fd = open("english.txt",O_RDWR);if (fd == -1){perror("open");exit(0);}int len = lseek(fd,0,SEEK_END);int fd1 =open("cpy.txt",O_RDWR | O_CREAT,0664);if(fd1==-1){perror("open");}truncate("cpy.txt",len);write(fd1,"",1);//分别做一个内存映射void *ptr = mmap(NULL,len,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);void *ptr1 = mmap(NULL,len,PROT_READ | PROT_WRITE,MAP_SHARED,fd1,0);if(ptr==MAP_FAILED){perror("ptr");exit(0);}if(ptr1==MAP_FAILED){perror("ptr1");exit(0);}//执行内存拷贝memcpy(ptr1,ptr,len);//释放资源+关闭munmap(ptr1,len);munmap(ptr,len);close(fd1);close(fd);return 0;
}

二、匿名映射与进程通信

匿名映射(Anonymous Mapping)是 mmap 函数的一种特殊用法,它不与任何文件关联,而是直接在进程的虚拟地址空间中分配一块内存区域。这种映射方式常用于创建匿名共享内存或分配动态内存。

匿名映射的初始内容通常被清零(即所有字节都设置为 0)匿名映射不涉及任何文件描述符(fd),因此不会从文件中加载数据。它直接在进程的虚拟地址空间中分配一块内存区域。

匿名映射可以用于动态分配内存,类似于 malloc,但更灵活。它可以直接在虚拟地址空间中分配内存,而不需要通过堆分配器。

/*匿名映射:不需要文件实体进程一个内存映射
*/#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>int main() {// 1.创建匿名内存映射区int len = 4096;void * ptr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);if(ptr == MAP_FAILED) {perror("mmap");exit(0);}// 父子进程间通信pid_t pid = fork();if(pid > 0) {// 父进程strcpy((char *) ptr, "hello, world");wait(NULL);}else if(pid == 0) {// 子进程sleep(1);printf("%s\n", (char *)ptr);}// 释放内存映射区int ret = munmap(ptr, len);if(ret == -1) {perror("munmap");exit(0);}return 0;
}

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

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

相关文章

使用vscode + Roo Code (prev. Roo Cline)+DeepSeek-R1使用一句话需求做了个实验

摘要 使用vscode、Roo Code和deepseek-reasoner进行了一个实验&#xff0c;尝试使用一句话需求来生成小红书封面图片。工具根据需求提供了详细的架构方案&#xff0c;包括技术栈选择、核心模块划分、目录结构建议等。然后&#xff0c;工具自动化地完成了开发和测试&#xff0c;…

C语言初阶牛客网刷题—— JZ11 旋转数组的最小数字【难度:简单】

1. 题目描述 牛客网在线OJ链接 有一个长度为 n 的非降序数组&#xff0c;比如 [1,2,3,4,5] &#xff0c;将它进行旋转&#xff0c;即把一个数组最开始的若干个元素搬到数组的末尾&#xff0c;变成一个旋转数组&#xff0c;比如变成了 [3,4,5,1,2] &#xff0c;或者 [4,5,1,2,3…

如何解压7z文件?8种方法(Win/Mac/手机/网页端)

7z 文件是一种高效的压缩文件格式&#xff0c;由 7 - Zip 软件开发者所采用。它运用独特的压缩算法&#xff0c;能显著缩小文件体积&#xff0c;便于存储与传输各类数据&#xff0c;像软件安装包、大型资料集等。但要使用其中内容&#xff0c;就必须解压&#xff0c;因为处于压…

豆包MarsCode 蛇年编程大作战 | 高效开发“蛇年运势预测系统”

&#x1f31f; 嗨&#xff0c;我是LucianaiB&#xff01; &#x1f30d; 总有人间一两风&#xff0c;填我十万八千梦。 &#x1f680; 路漫漫其修远兮&#xff0c;吾将上下而求索。 豆包MarsCode 蛇年编程大作战 | &#x1f40d; 蛇年运势预测 在线体验地址&#xff1a;蛇年…

【转帖】eclipse-24-09版本后,怎么还原原来版本的搜索功能

【1】原贴地址&#xff1a;eclipse - 怎么还原原来版本的搜索功能_eclipse打开类型搜索类功能失效-CSDN博客 https://blog.csdn.net/sinat_32238399/article/details/145113105 【2】原文如下&#xff1a; 更新eclipse-24-09版本后之后&#xff0c;新的搜索功能&#xff08;CT…

macos的图标过大,这是因为有自己的设计规范

苹果官方链接&#xff1a;App 图标 | Apple Developer Documentation 这个在官方文档里有说明&#xff0c;并且提供了sketch 和 ps 的模板。 figma还提供了模板&#xff1a; Figma

C++异步future

&#x1f30e; C11异步futrue 文章目录&#xff1a; C11异步futrue future介绍     应用场景     future操作       std::async函数模版       std::packaged_task类模版       std::promise类模版 &#x1f680;future介绍 std::future是C11标准库…

洛谷 P2846 [USACO08NOV] Light Switching G C语言

题目描述 Farmer John tries to keep the cows sharp by letting them play with intellectual toys. One of the larger toys is the lights in the barn. Each of the N(2≤N≤105) cow stalls conveniently numbered 1…N has a colorful light above it. At the beginnin…

批量创建ES索引

7.x from elasticsearch import Elasticsearch# 配置 Elasticsearch 连接 # 替换为你的 Elasticsearch 地址、端口、用户名和密码 es Elasticsearch([http://10.10.x.x:43885],basic_auth(admin, XN272G9THEAPYD5N5QORX3PB1TSQELLB) )# # 测试连接 # try: # # 尝试获取集…

大厂案例——腾讯蓝鲸DevOps类应用的设计与实践

蓝鲸体系架构图 蓝鲸CICD应用功能架构 降低DEVOPS门槛—开发者中心 CICD应用需要的后台服务 系列阅读 12306亿级流量架构分析&#xff08;史上最全&#xff09;实现电商平台从业务到架构的治理体系基于主数据驱动的数据治理什么时候需要分表分库&#xff1f;-CSDN博客

React和Vue有什么区别,如何选择?

React和Vue有什么区别&#xff0c;如何选择&#xff1f; React 和 Vue 是当前最受欢迎的前端框架之一&#xff0c;两者在开发者中都有极高的声誉。它们都旨在帮助开发人员构建用户界面&#xff0c;但在实现方式和适用场景上有所不同。如果你正考虑在项目中选择 React 或 Vue&a…

uart、iic、spi通信总线

一、uart uart一种异步串行通信协议&#xff0c;用于在两个设备之间传输数据。它将数据按位发送&#xff0c;不需要时钟信号进行同步。在uart通信中&#xff0c;数据通过两根线路传输&#xff1a;发送线&#xff08;TX&#xff09;和接收线&#xff08;RX&#xff09;。它主要用…

LMI Gocator GO_SDK VS2019引用配置

LMI SDK在VS2019中的引用是真的坑爹,总结一下经验,希望后来的人能少走弯路.大致内容如下: &#xff08;1&#xff09; 环境变量 &#xff08;2&#xff09;C/C 附加包含目录 E:\GWQ\Gocator\GO_SDK\Gocator\GoSdk E:\GWQ\Gocator\GO_SDK\Platform\kApi &#xff08;3&#…

QT QTableWidget控件 全面详解

本系列文章全面的介绍了QT中的57种控件的使用方法以及示例,包括 Button(PushButton、toolButton、radioButton、checkBox、commandLinkButton、buttonBox)、Layouts(verticalLayout、horizontalLayout、gridLayout、formLayout)、Spacers(verticalSpacer、horizontalSpacer)、…

C# OpenCV机器视觉:红外体温检测

在一个骄阳似火的夏日&#xff0c;全球却被一场突如其来的疫情阴霾笼罩。阿强所在的小镇&#xff0c;平日里熙熙攘攘的街道变得冷冷清清&#xff0c;人们戴着口罩&#xff0c;行色匆匆&#xff0c;眼神中满是对病毒的恐惧。阿强作为镇上小有名气的科技达人&#xff0c;看着这一…

立创开发板入门ESP32C3第八课 修改AI大模型接口为deepseek3接口

#原代码用的AI模型是minimax的API接口&#xff0c;现在试着改成最热门的deepseek3接口。# 首先按理解所得&#xff0c;在main文件夹下&#xff0c;有minimax.c和minimax.h, 它们是这个API接口的头文件和实现文件&#xff0c;然后在main.c中被调用。所以我们一步步更改。 申请…

2025.1.21——六、BUU XSS COURSE 1 XSS漏洞|XSS平台搭建

题目来源&#xff1a;buuctf BUU XSS COURSE 1 目录 一、打开靶机&#xff0c;整理信息 二、解题思路 step 1&#xff1a;输入框尝试一下 step 2&#xff1a;开始xss注入 step 3&#xff1a;搭建平台 step 4&#xff1a;利用管理员cookie访问地址 三、小结 二编&#…

操作无法完成,因为文件已经在Electronic Team Virtual Serial Port Driver Service中打开

报错 操作无法完成,因为文件已经在Electronic Team Virtual Serial Port Driver Service中打开 现象 这个exe文件无法删除 解决办法 按下WinR, 找到Electronic Team Virtual Serial Port Driver Service,右击停止. 再次尝试删除,发现这个exe文件成功删除!

单值二叉树(C语言详解版)

一、摘要 今天要讲的是leetcode单值二叉树&#xff0c;这里用到的C语言&#xff0c;主要提供的是思路&#xff0c;大家看了我的思路之后可以点击链接自己试一下。 二、题目简介 如果二叉树每个节点都具有相同的值&#xff0c;那么该二叉树就是单值二叉树。 只有给定的树是单…

【多表查询】

目录 一. 一对多二. 一对一 and 多对多三. 多表设计案例四. 多表查询4.1 概述4.2 内连接与外连接4.3 子查询4.4 案例 \quad 一. 一对多 \quad 删除外键 \quad 二. 一对一 and 多对多 \quad \quad 三. 多表设计案例 \quad 一个员工对应多个工作经历 \quad 四. 多表查询 \quad \q…