进程通信——内存映射

news2024/11/20 20:40:33

进程通信——内存映射

什么是内存映射

内存映射是一种将文件内容映射到进程地址空间的技术,使得进程可以直接访问文件内容,而不需要通过系统调用进行读写操作。内存映射可以提高文件访问的效率,并且可以实现进程间的通信。

内存映射的原理

我们在进程中创建一个内存映射区,但由于这个内存映射区是创建在进程的地址空间中,所以外界的进程空间无法直接访问其他进程的内存映射区。但是,我们可以通过系统调用mmap将文件内容映射到内存映射区,这样进程就可以直接访问文件内容了,像下面这样:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
如上图那样,磁盘文件数据不仅可以完全加载到进程的内存映射区,还可以部分加载到进程的内存映射区,当进程A中的内存映射区数据被修改了,数据会被自动同步到磁盘文件,同时和磁盘文件建立映射关系的其他进程内存映射区中的数据也会和磁盘文件进行数据的实时同步,基于这样的同步机制我们可以实现进程间的通信。

内存映射的创建

内存映射的创建需要使用mmap函数,该函数的原型如下:

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

参数说明:

  • addr: 指定映射区的起始地址,如果为NULL,则由系统自动选择一个地址。
  • length: 指定映射区的长度。
  • prot: 指定映射区的保护方式,可以是以下值的组合:
    • PROT_READ: 可读。如果映射区被修改,则修改的内容会被同步到文件。
    • PROT_WRITE: 可写。如果映射区被修改,则修改的内容会被同步到文件。
    • PROT_EXEC: 可执行。
    • PROT_NONE: 无权限。
  • flags: 指定映射区的标志,可以是以下值的组合:
    • MAP_SHARED: 共享映射区。多个进程可以共享同一个映射区。
    • MAP_PRIVATE: 私有映射区。每个进程都有自己的映射区,修改不会影响其他进程。
    • MAP_ANONYMOUS: 匿名映射区。不与文件关联,可以用于进程间的通信。
  • fd: 指定要映射的文件的文件描述符。
  • offset: 指定映射区的起始偏移量。

返回值:

  • 成功返回映射区的起始地址。
  • 失败返回MAP_FAILED

由于参数较多,一般我们可以像下面这样创建:

  • 第一个参数 addr 指定为 NULL 即可
  • 第二个参数 length 必须要 > 0
  • 第三个参数 prot,进程间通信需要对内存映射区有读写权限,因此需要指定为:PROT_READ | PROT_WRITE
  • 第四个参数 flags,如果要进行进程间通信, 需要指定 MAP_SHARED
  • 第五个参数 fd,打开的文件必须大于0,进程间通信需要文件操作权限和映射区操作权限相同
    • 内存映射区创建成功之后, 关闭这个文件描述符不会影响进程间通信
  • 第六个参数 offset,不偏移指定为0,如果偏移必须是4k的整数倍

内存映射的释放

既然我们创建了内存映射区,那么在不需要的时候就需要释放它,释放内存映射区需要使用munmap函数,该函数的原型如下:

int munmap(void *addr, size_t length);

参数说明:

  • addr: 指定要释放的映射区的起始地址。
  • length: 指定要释放的映射区的长度。

返回值:

  • 成功返回0。
  • 失败返回-1。

基于内存映射区实现进程间通信

内存映射区与管道通信的区别

  • 管道通信是使用文件描述符进行通信,而内存映射区通信是直接使用内存地址进行通信。这导致了管道通信是阻塞的,而内存映射区通信是非阻塞的。

  • 管道通信的数据需要经过内核缓冲区,而内存映射区通信的数据直接在用户空间和内核空间之间传递,不需要经过内核缓冲区。这导致了内存映射区通信的速度更快。

基于内存映射实现的进程通信

有血缘关系的进程通信

#include <iostream>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <cstring>
#include <unistd.h>
#include <wait.h>
#include <fcntl.h>

using namespace std;

int main()
{
    int fd = open("test.txt", O_RDWR | O_CREAT, 0666);
    if (fd == -1)
    {
        perror("open");
        return -1;
    }
    cout<<111<<endl;
    void* ptr = mmap(NULL, 4000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (ptr == MAP_FAILED)
    {
        perror("mmap");
        return -1;
    }
    int pid = fork();
    if (pid > 0) // 父进程
    {
        const char* str = "test";
        cout<<222<<endl;
        // 确保字符串正确终止
        memcpy(ptr, str, strlen(str) + 1);
        // 等待一段时间,确保子进程有机会读取数据
        sleep(5); // 使用 sleep(1) 替代 usleep(1),确保子进程有足够时间读取
    }
    else if (pid == 0) // 子进程
    {
        // 读取数据
        cout << "从内存映射区读取出来的数据: " << static_cast<char*>(ptr) << endl;
    }
    
    // 确保父进程等待子进程结束
    wait(NULL);
    
    // 解除映射并关闭文件
    munmap(ptr, 4000);
    close(fd);
    
    return 0;
}

备注: mmap不能去扩展一个内容为空的新文件,因为大小为0,所有本没有与之对应的合法的物理页,不能扩展。我们需要在新创建的空文件中先写入一些数据,否则会报错bus error(总线错误)

无血缘关系的进程通信

//
// 写端
#include <iostream>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <cstring>
#include <unistd.h>
#include <wait.h>
#include <fcntl.h>

using namespace std;

int main()
{
    int fd=open("test.txt",O_RDWR);
    if(fd==-1)
    {
        perror("open");
        return -1;
    }
    void* ptr=mmap(NULL,4096,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(ptr==MAP_FAILED)
    {
        perror("mmap");
        return -1;
    }
    const char* str="test";
    memcpy(ptr,str,strlen(str)+1);
    munmap(ptr,4096);
    return 0;
}
//读端

#include <iostream>
#include <cstring>
#include <fcntl.h>  
#include <unistd.h>
#include <sys/mman.h>

using namespace std;

int main()
{
    int ret=open("test.txt",O_RDWR);
    if(ret==-1)
    {
        perror("open");
        return -1;
    }
    void* ptr=mmap(NULL,4096,PROT_READ|PROT_WRITE,MAP_SHARED,ret,0);
    if(ptr==MAP_FAILED)
    {
        perror("mmap");
        return -1;
    }
    cout<<"ptr:"<<(char*)ptr<<endl;
    munmap(ptr,4096);
    return 0;
}

基于内存映射实现文件拷贝

我们除了使用文件拷贝函数,也可以使用内存映射区实现文件拷贝,下面就是我们如何基于内存映射实现文件拷贝

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>

using namespace std;

int main()
{
    //打开待复制的文件
    int fd1=open("test.txt", O_RDONLY);
    if(fd1==-1)
    {
        perror("open");
        return 0;
    }
    //获取待复制文件的大小
    int size=lseek(fd1, 0, SEEK_END);
    //创建源文件的内存映射区
    void* ptr1=mmap(NULL, size, PROT_READ, MAP_SHARED, fd1, 0);
    if(ptr1==MAP_FAILED)
    {
        perror("mmap");
        return 0;
    }

    //打开目标文件
    int fd2=open("test2.txt", O_RDWR|O_CREAT|O_TRUNC, 0666);
    if(fd2==-1)
    {
        perror("open");
        return 0;
    }
    //创建目标文件的内存映射区
    void* ptr2=mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);
    if(ptr2==MAP_FAILED)
    {
        perror("mmap");
        return 0;
    }

    //拓展文件大小,避免出现总线错误
    ftruncate(fd2, size);

    //拷贝文件
    memcpy(ptr2, ptr1, size);

    //关闭文件
    close(fd1);
    close(fd2);

    //解除内存映射区
    munmap(ptr1, 4096);
    munmap(ptr2, size);


    return 0;
}

结语

有关进程的教学部分就以此告一段落,大家如果多其他的部分感兴趣,可以参考我的博客网站

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

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

相关文章

【HarmonyOS】时间处理Dayjs

背景 在项目中经常会使用要时间的格式转换&#xff0c;比如数据库返回一个Date数据&#xff0c;你需要转成2024-10-2的格式&#xff0c;鸿蒙的原生SDK中是没有办法实现的&#xff0c;因此&#xff0c;在这里介绍第三方封装好并且成熟使用的库Dayjs。 安装 切换到Entry文件夹下…

C++初学者指南-5.标准库(第二部分)–特殊迭代器

C初学者指南-5.标准库(第二部分)–特殊迭代器 文章目录 C初学者指南-5.标准库(第二部分)–特殊迭代器容器操纵器std::insert_iterator\<Container>std::back_insert_iterator\<Container>std::front_insert_iterator\<Container> I/O 流迭代器std::istream_i…

2024大二上js高级+ES6学习9.29(深/浅拷贝,正则表达式,let/const,解构赋值,箭头函数,剩余参数)

9.29.2024 1.浅拷贝和深拷贝 Es6的语法糖&#xff1a;用assign将obj对象浅拷贝给o对象。 把数组写在前面是因为数组也是对象 2.正则表达式 创建和检测正则表达式 正则表达式的使用直接跳过&#xff0c;等要用时现查现用 3.ES6 4.let关键字 块级作用域是指在一个{}l里 变量提…

Python | Leetcode Python题解之第441题排列硬币

题目&#xff1a; 题解&#xff1a; class Solution:def arrangeCoins(self, n: int) -> int:left, right 1, nwhile left < right:mid (left right 1) // 2if mid * (mid 1) < 2 * n:left midelse:right mid - 1return left

四、Java 基础语法

一、Java 的类、对象、方法和实例变量 一个 Java 程序可以认为是一系列对象的集合&#xff0c;而这些对象通过调用彼此的方法来协同工作。下面简要介绍下类、对象、方法和实例变量的概念。对象&#xff1a;对象是类的一个实例&#xff0c;有状态&#xff08;实例变量&#xff…

MySQL基础练习题49-低质量的问题

目录 题目 准备数据 分析数据 总结 题目 找出 低质量 问题的 ID 集合。如果一个力扣问题的喜欢率&#xff08;喜欢数除以总投票数&#xff09;严格低于 60% &#xff0c;则该问题为低质量问题。 按 problem_id 升序排列返回结果表。 准备数据 Create table If Not Exis…

深度学习基础—卷积神经网络示例

1.卷积神经网络的结构 在之前的博客《深度学习—简单的卷积神经网络》&#xff0c;仅由卷积层构成网络的全部&#xff0c;这还不是标准的网络结构&#xff0c;本文将继续介绍标准的卷积神经网络结构有哪些&#xff1f; 深度学习基础—简单的卷积神经网络https://blog.csdn.net…

STM32-按键控制LED 光敏传感器控制蜂鸣器(江协笔记)

1、按键 2、常见传感器模块 R1 定值电阻 N1 传感器电阻。对于光敏传感器来说&#xff0c;相当于光敏电阻&#xff1b;...... C(滤波电容) 给中间的电压输出进行滤波&#xff0c;用于滤除一些干扰&#xff0c;保证输出电压波形的平滑&#xff08;保持电路稳定&#xff09; …

Pikachu-暴力破解-验证码绕过(on client)

访问页面&#xff0c; 从burpsuite 上看到返回的源代码&#xff1b; 验证码生成时通过 createCode 方法生成&#xff0c;在前端页面生成&#xff1b; 同时也是在前端做的校验&#xff1b; 直接验证&#xff1b;F12 -- 网络&#xff0c;随便输入个账号、密码、验证码&#xff0…

多维度柱状图绘制

图形结果 绘制过程 数据如下 调整柱子宽度 Z轴设置 、 配色表

开源链动2+1模式AI智能名片S2B2C商城小程序源码:流量运营中的价值创造与用户影响

摘要&#xff1a;本文深入探讨在开源链动21模式AI智能名片S2B2C商城小程序源码的背景下&#xff0c;流量的激活、信任建立、圈层沉淀以及裂变等流量运营现象。分析流量运营成本与用户消费意识的关系&#xff0c;强调内容在赋予流量价值以影响用户感知和消费判断方面的重要性。 …

基于yolov8深度学习的120种犬类检测与识别系统python源码+onnx模型+评估指标曲线+精美GUI界面目标检测狗类检测犬类识别系统

【算法介绍】 基于YOLOv8深度学习的120种犬类检测与识别系统是一款功能强大的工具&#xff0c;该系统利用YOLOv8深度学习框架&#xff0c;通过21583张图片的训练&#xff0c;实现了对120种犬类的精准检测与识别。 该系统基于Python与PyQt5开发&#xff0c;具有简洁的UI界面&a…

当AI成为作家,人工智能在写作领域的崛起

AI写作技术的应用正在多个领域展现出其强大的潜力和价值&#xff0c;它不仅极大地提升了内容创作的效率&#xff0c;还为创作者提供了一个全新的创作伙伴。 随着技术的进步&#xff0c;AI写作工具越来越能够理解复杂的语境和用户需求&#xff0c;帮助创作者生成高质量的内容。…

DpCas 镜头场景分割 Scene Segmentation

开源项目 - DpCas 镜头场景分割 Scene Segmentation 开源项目地址&#xff1a;https://gitcode.net/EricLee/dpcas 示例&#xff1a;

写出第一个php程序

一、打开vscode&#xff0c;下载chinese插件、php debug、phpintelephense 二、下载完上方图片插件后&#xff0c;创建一个PHP文件&#xff0c;1.php 三、执行命令&#xff0c;成功输出

pytorch搭建神经网络(手搓方法)

假如我们有一个数据集形状为(348,14)。即有348个记录&#xff0c;每个记录有14个特征值。 我们想要搭建一个如下的神经网络&#xff1a; import torch import numpy as np# 创建数据集: 每个样本有14个特征 x_train np.array([[0.5, -1.2, 0.3, 0.8, 1.0, -0.5, 2.3, 1.2, -0…

在Ubuntu 20.04中安装CARLA

0. 引言 CARLA (Car Learning to Act) 是一款开源自动驾驶模拟器&#xff0c;其支持自动驾驶系统全管线的开发、训练和验证&#xff08;Development, Training, and Validation of autonomous driving systems&#xff09;。Carla提供了丰富的数字资产&#xff0c;例如城市布局…

前端编程艺术(2)----CSS

目录 1.CSS 2.CSS引入 3.选择器 1.标签选择器 2.类选择器 3.id选择器 4.属性选择器 5.后代选择器 5.直接子元素选择器 6.伪类选择器 链接相关 动态伪类 结构化伪类 否定伪类 其他伪类 UI元素状态伪类 4.字体 1.font-family 2.font-size 3.font-style 4.fo…

Linux查找隐藏病毒进程

工具连接 下载工具不要分&#xff0c;随便下 下载后修改工具名&#xff1a;如修改为lsof、ps、top等并为工具加入执行权限 2、 直接执行即可&#xff0c;与正常命令用法一致&#xff08;截图如下&#xff09;

足球预测推荐软件:百万数据阐述百年足球历史-大数据模型量化球员成就值

我开始创建这个模型是从梅西22世界杯夺冠第二天开始准备的&#xff0c;当时互联网上充斥了太多了个人情感的输出&#xff0c;有的人借题对C罗冷嘲热讽&#xff0c;有的人质疑梅西的阿根廷被安排夺冠不配超越马拉多纳做GOAT。作为一个从2002年开始看球的球迷&#xff0c;说实话有…