Linux进程通信:存储映射mmap

news2024/11/25 6:51:06

1. 存储映射是什么?

        如上图,存储映射是将块设备的文件映射到进程的虚拟地址空间。之后,进程可以直接使用指针操作其地址空间中映射的文件,对这块映射区操作就相当于操作文件。


2. 存储映射函数mmap的简单使用

(1)mmap函数:

#include<sys/mman.h>

void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset);
/*
功能:
    将文件fd从开头偏移offset个字节处开始,
    映射到调用该函数的进程的虚拟地址空间的addr地址开始的往后length个字节的虚拟地址空间长度。
    映射区的保护方式为prot,特性为flags。

参数:
    addr:映射到进程地址空间的起始地址,通常为NULL,由内核指定;

    length:映射到进程地址空间的大小;

    prot:映射区的保护方式:
        a)读:PROT_READ
        b)写:PROT_WRITE
        c)读写:PROT_READ | PROT_WRITE

    flags:映射区的特性:
        a)MAP_SHARED:写入映射区的数据会复制回文件,且允许共享给其他映射该文件的进程;
        b)MAP_PRIVATE:对映射区的写入操作会产生一个映射区的复制(copy-on-write),
                        对此区域的修改不会写回原文件;

    fd:要映射的文件,即open返回的文件描述符;

    offset:从文件起始位置偏移offset开始映射,必须是4K的整数倍;
            通常为0,表示从文件起始位置开始映射;

返回值:
    成功:映射区的首地址
    失败:MAP_FAILED宏
*/

mmap使用总结:

(1)第一个参数通常为 NULL;

(2)第二个参数映射文件大小 > 0;

(3)第三个参数:PROT_READ、PROT_WRITE;

(4)第四个参数:MAP_SHARED、MAP_PRIVATE;

(5)第五个参数:要映射的文件描述符;

(6)第六个参数:4K的整数倍,通常为0;

mmap注意事项:

(1)创建映射区过程中,隐含一次对映射文件的读操作;

(2)当flags为MAP_SHARED时,要求:映射区权限 ≤ 文件打开的权限;而MAP_PRIVATE无要求;

(3)映射成功文件即可关闭;

(4)文件大小为0时不能创建映射区。使用mmap时常出错总线错误(bus error),通常是映射文件大小的问题;

(5)munmap的传入地址一定是mmap的返回值,禁止对其地址++操作;

(6)mmap调用出错几率高,一定要检查返回值。


(2)munmap函数 :

#include<sys/mman.h>

int munmap(void* addr, size_t length);
/*
功能:
    释放进程地址空间addr开始的length个字节的存储映射区
参数:
    addr:映射区的起始地址,即mmap函数的返回值;
    length:映射区大小,即mmap函数的第二个参数;
返回值:
    成功:0
    失败:-1
*/

(3)mmap使用示例:

#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/mman.h>

int main(int argc, const char* argv[]) {

    int fd = -1;
    int ret = -1;
    void* addr = NULL;

    // 1.以读写的方式打开txt文件
    fd = open("txt", O_RDWR);
    if (-1 == fd) {
        perror("open");
        return 1;
    }

    // 2.将文件映射到进程的虚拟地址空间
    addr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED) {
        perror("mmap");
        return 1;
    }

    printf("映射成功.\n");

    // 3.关闭文件
    close(fd);

    // 4.写文件
    memcpy(addr, "123456", 6);

    // 5.断开存储映射
    munmap(addr, 1024);

    return 0;
}

运行结果:


3. mmap实现父子进程通信

(1)原理:

a)父进程先创建存储映射区,得到映射区在其虚拟地址空间中的起始地址addr和映射长度length;

b)fork子进程后,子进程虚拟地址空间存在和父进程addr、length一样的存储映射区;

c)因为父子进程都映射同一个文件,因此可通过该文件进行通信。

(2)代码示例:

#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<sys/wait.h>

int main(int argc, const char* argv[]) {

    int fd = -1;
    int ret = -1;
    pid_t pid = -1;
    void* addr = NULL;

    // 1.以读写的方式打开txt文件
    fd = open("txt", O_RDWR);
    if (-1 == fd) {
        perror("open");
        return 1;
    }

    // 2.将文件映射到进程的虚拟地址空间
    addr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED) {
        perror("mmap");
        return 1;
    }

    printf("映射成功.\n");

    // 3.关闭文件
    close(fd);

    // 4.创建子进程
    pid = fork();
    if (-1 == pid) {
        perror("fork");
        return 1;
    }

    if (0 == pid) { // 子进程写文件
        memcpy(addr, "ABCD", 4);
    } else { // 父进程读文件
        wait(NULL); // 等待子进程结束
        printf("子进程写的内容:%s\n", (char*)addr);
    }

    // 5.断开存储映射
    munmap(addr, 1024);

    return 0;
}

运行结果:


4. mmap实现无关系的进程通信

代码示例:

mmap_a.c文件:

#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<sys/wait.h>

int main(int argc, const char* argv[]) {

    int fd = -1;
    int ret = -1;
    pid_t pid = -1;
    void* addr = NULL;

    // 1.以读写的方式打开txt文件
    fd = open("txt", O_RDWR);
    if (-1 == fd) {
        perror("open");
        return 1;
    }

    // 2.将文件映射到进程的虚拟地址空间
    addr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED) {
        perror("mmap");
        return 1;
    }

    printf("映射成功.\n");

    // 3.关闭文件
    close(fd);

    // 4. 读存储映射区
    printf("读存储映射区:%s\n", (char*)addr);

    // 5.断开存储映射
    munmap(addr, 1024);

    return 0;
}

mmap_b.c文件:

#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<sys/wait.h>

int main(int argc, const char* argv[]) {

    int fd = -1;
    int ret = -1;
    pid_t pid = -1;
    void* addr = NULL;

    // 1.以读写的方式打开txt文件
    fd = open("txt", O_RDWR);
    if (-1 == fd) {
        perror("open");
        return 1;
    }

    // 2.将文件映射到进程的虚拟地址空间
    addr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED) {
        perror("mmap");
        return 1;
    }

    printf("映射成功.\n");

    // 3.关闭文件
    close(fd);

    // 4.写存储映射区
    memcpy(addr, "XYZ", 3);

    // 5.断开存储映射
    munmap(addr, 1024);

    return 0;
}

运行结果:


 5. 匿名映射

父子进程使用存储区映射进行通信的缺陷是需要依赖一个文件。

为了克服该缺陷,父子进程可使用匿名映射,无需依赖文件即可创建映射区,需要借助flags参数MAP_ANONYMOUS(或MAP_ANON)来指定为匿名映射,如下:

int* addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
/*
MAP_ANONYMOU需要和MAP_SHARED 一起使用
MAP_ANONYMOUS和MAP_ANON是Linux系统特有的宏,类unix系统中无该宏定义。
*/

匿名映射示例:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<sys/wait.h>

int main(int argc, const char* argv[]) {

    int ret = -1;
    pid_t pid = -1;
    void* addr = NULL;

    // 1.创建匿名映射
    addr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (MAP_FAILED == addr) {
        perror("mmap");
        return 1;
    }

    // 2.创建子进程
    pid = fork();
    if (-1 == pid) {
        perror("fork");
        munmap(addr, 4096);
        return 1;
    }

    // 3.父子进程通信
    if (0 == pid) {
        memcpy(addr, "1234", 4); // 子进程写
    } else {
        wait(NULL); // 等待子进程结束
        printf("父进程读到:%s\n", (char*)addr);
    }

    // 4.断开映射
    munmap(addr, 4096);

    return 0;
}

运行结果:

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

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

相关文章

网络安全岗位面试题大全:解析各个分支岗位的面试题目,帮助你上岸大厂

网络安全是一个广泛的领域&#xff0c;涵盖了许多不同的岗位和分支。我整理了网络安全各个岗位分支的面试题目&#xff1a; 安全工程师/系统管理员 您如何确保网络系统的安全性和保密性&#xff1f;您采用了哪些技术和工具&#xff1f;请描述一下您在过去工作中遇到的最具挑战…

C++ -5- 内存管理

文章目录 C语言和C内存管理的区别示例1. C/C 中程序内存区域划分2. C中动态内存管理3.operator new 与 operator delete 函数4.new 和 delete 的实现原理5.定位new表达式 C语言和C内存管理的区别示例 //C语言&#xff1a; struct SListNode {int data;struct SListNode* next; …

什么是内存?什么是内存逃逸?怎么做内存逃逸分析

内存 平时我们在电脑上听歌&#xff0c;聊天&#xff0c;或者启动某个程序&#xff0c;那么这个启动过程&#xff0c;其实就是把程序从硬盘读入到内存中去。就像安卓手机&#xff0c;内存不够了很卡&#xff0c;杀掉几个软件&#xff0c;内存就升上来了。但也不是所有的程序都…

产品经理需要了解api接口的哪些东西

一、作为产品经理&#xff0c;需要了解API接口的以下方面&#xff1a; 功能&#xff1a;API接口的功能是指它提供的业务功能&#xff0c;包括数据查询、修改、增加、删除、计算等等&#xff0c;根据产品的需求确定需要调用哪些API接口。请求方式和传参&#xff1a;API接口的请…

致力提供一站式数据可视化解决方案,支持报表、图表、大屏

一、开源项目简介 Davinci是一个DVAAS&#xff08;Data Visualization as a Service&#xff09;平台解决方案。 Davinci面向业务人员/数据工程师/数据分析师/数据科学家&#xff0c;致力于提供一站式数据可视化解决方案。既可作为公有云/私有云独立使用&#xff0c;也可作为…

Linux进程通信:信号

1. 信号的概念 Linux进程间通信的方式之一。信号也称为“软件中断”。 信号特点&#xff1a; 简单&#xff1b;携带信息有限&#xff1b;满足特定条件才发送信号&#xff1b;可进行用户空间和内核空间进程的交互&#xff1b; 2. 信号的编号 kill -l // 查看信号编号 POS…

ModelArts的使用

完整流程第一个实例&#xff1a;AI初学者&#xff1a;使用订阅算法构建模型实现花卉识别_AI开发平台ModelArts_最佳实践_模型训练&#xff08;预置算法-新版训练&#xff09;_华为云 一、支持的模型 可以在gitee上下载标准网络模型&#xff1a; models: Models of MindSpore …

Prometheus优化及高可用

Prometheus优化及高可用 概述 Prometheus几乎已成为监控领域的事实标准&#xff0c;它自带高效的时序数据库存储&#xff0c;可以让单台 Prometheus 能够高效的处理大量的数据&#xff0c;还有友好并且强大的 PromQL 语法&#xff0c;可以用来灵活的查询各种监控数据以及配置…

使用 chat_flutter 进行聊天记录展示

前言 最近需要实现一个聊天记录的页面展示&#xff0c;在网上发现没有适合自己的&#xff0c;于是自己就造了一个&#xff0c;总体感觉还不赖。 下面奉上地址、效果图和教程。 效果图 地址 github: https://github.com/xiaorui-23/chat_fluttergitee: https://gitee.com/xi…

搭建微型服务器(node express框架)

目录 一&#xff1a;打包&#xff08;npm run build&#xff09; 二&#xff1a;变成合法的包&#xff08;新建server文件夹&#xff09; 三&#xff1a;一路回车 四&#xff1a;新建服务器主文件 五&#xff1a;编辑server.js 六&#xff1a;node server启动服务器 七&a…

第十二章 享元模式

文章目录 前言一、享元模式基本介绍二、享元模式解决网站展现项目完整代码WebSite 抽象网站类User 外部状态用户内部状态网站 ConcreteWebSite网站工厂产生网站和负责共享&#xff08;池&#xff09; WebSiteFactoryClint 测试 三、享元模式在JDK-Interger的应用源码分析四、享…

NFS部署

共享/webdata/目录&#xff1b; ~ 用于存储 AppSrv 主机的 WEB 数据&#xff1b; ~ 仅允许 AppSrv 主机访问该共享&#xff1b; ~ 考虑安全&#xff0c;不论登入 NFS 的使用者身份为何&#xff0c;都将其设置为匿名用 户访问 StorageSrv和AppSrv nfs共享 1.安装nfs(App…

pdf怎么删除其中一页?

pdf怎么删除其中一页&#xff1f;大家都应该知道&#xff0c;PDF是一种实用性非常强且非常便携文件格式&#xff0c;许多用户对其非常熟悉。不管是工作还是学习中&#xff0c;都会下载或者使用到pdf文件。pdf文件具有非常好的兼容性&#xff0c;F可以将各种图片、文字内容整合在…

根据cadence设计图学习硬件知识day05 了解一些芯片

1.NXS0102DC &#xff08;2位双电源转换收发器&#xff09; 1.NXS0102DC 介绍 NXS0102是一款2位双电源转换收发器&#xff0c;具有自动方向感测功能&#xff0c;可实现双向电压电平转换。它具有两个2位输入输出端口&#xff08;An和Bn&#xff09;、一个输出使能输入&#xf…

Transformer 原理及代码详细解析

Transformer 原理及代码详细解析 文章目录 Transformer 原理及代码详细解析一、Transformer 背景介绍1.1 Transformer 的诞生1.2 Transformer 的优势1.3 Transformer 的市场 二、Transformer架构解析2.1 认识 Transformer 架构2.1.1 Transformer模型的作用2.1.2 Transformer 总…

k8s优雅终止pod

k8s优雅终止pod 概述 Pod 销毁时&#xff0c;会停止容器内的进程&#xff0c;通常在停止的过程中我们需要执行一些善后逻辑&#xff0c;比如等待存量请求处理完以避免连接中断&#xff0c;或通知相关依赖进行清理等&#xff0c;从而实现优雅终止目的。本文介绍在 Kubernetes …

深度学习—入门

深度学习与机器学习的区别 深度学习由机器学习中的神经网络发展而来&#xff0c;机器学习多用于处理数值数据&#xff0c;而深度学习还可处理图片、音频等数据。 特征提取方面 机器学习的特征工程步骤需要靠手动完成&#xff0c;需要大量专业领域知识。深度学习通常由多个层组…

Mysql8.0 包学包会!一篇文章解决Mysql

基于尚硅谷的Mysql8.0视频&#xff0c;修修改改。提取了一些精炼的内容。 首先需要在数据库内引入一张表。链接地址如下。 链接&#xff1a;https://pan.baidu.com/s/1DD83on3J1a2INI7vrqPe4A 提取码&#xff1a;68jy 会进行持续更新。。 1. Mysql目录结构 Mysql的目录结构…

传感器-红外接收,NodeMCU,arduino实现红外接收,4.x版本的irRemote库的使用

一&#xff0c;常见视频和搜索到接收红外的代码 发送参考&#xff0c;接收参考 这里只是看arduino中的接收程序&#xff0c; #include <IRremote.h> int RECV_PIN 5; /红外接收模块的s引脚 IRrecv irrecv(RECV_PIN); decode_results results; void setup() {Serial.b…

复现Apache HTTPD 换行解析漏洞(CVE-2017-15715)

影响版本 2.4.0~2.4.29都有可能存在该漏洞 原理分析 进入容器 docker exec -it 容器ID /bin/bash 该程序是采用黑名单的形式&#xff0c;如果文件后缀名不在名单内即可上传&#xff0c;所以 a.php\x0A不在黑名单列表中&#xff0c;可以上传。但是x0A是换行符&#xff0c;所…