Linux 进程通信之:内存映射(Memory Map)_linux apue memorymap范例-程序员宅基地

技术标签: C/C++  Linux  内存映射  mmap  

一、简介

正如其名(Memory Map),mmap 可以将某个设备或者文件映射到应用进程的内存空间中。通过直接的内存操作即可完成对设备或文件的读写。.

通过映射同一块物理内存,来实现共享内存,完成进程间的通信。由于减少了数据复制的次数,一定程度上提高了进程间通信的效率。

二、API 说明

1. 头文件

#include <sys/mman.h>

2. 创建内存映射

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
  • addr : 将文件映射到进程空间指定地址,可以为 NULL,此时系统将自动分配地址
  • length : 被映射到进程空间的内存块大小
  • prot : 被映射内存的访问权限
    • PROT_EXEC : 内存页可执行
    • PROT_READ : 内存页可读
    • PROT_WRITE : 内存页可写
    • PROT_NONE : 内存页不可访问
  • flags : 指定程序对内存块所做的改变将造成的影响,通常有:
    • MAP_SHARED : 共享的形式,对内存块所做的修改将保存到文件中
    • MAP_PRIVATE : 私有的,对内存块的修改只在局部范围内有效
    • MAP_FIXED : 使用指定的映射起始地址
    • MAP_ANONYMOUS/MAP_ANON : 匿名映射,即不和任何文件关联,同时将 fd 设置为 -1。通常需要进程间有一定关系才能使用这种映射方式
  • fd : 文件描述符,即 open() 函数返回的值
  • offset : 指定从文件的哪一部分开始映射,必须是内存页的整数倍,通常为 0
  • 返回值 : 成功返回指向映射内存的指针,失败返回 -1,并设置合适的 errno 值

3. 解除内存映射

int munmap(void *addr, size_t length);
  • addr : 映射内存的起始指针,必须是 mmap 方法返回的那个值
  • length : 映射到进程空间的内存块大小
  • 返回值 : 成功返回 0,失败返回 -1,并设置合适的 errno 值

三、示例

1. 无血缘关系进程通信

写端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
 
typedef struct _data {
    
    int a;
    char b[64];
} Data;
 
 
int main() {
    
    Data *addr;
    Data data = {
     10, "Hello World\n" };
    int fd;
 
    fd = open("mmap_temp_file", O_RDWR|O_CREAT|O_TRUNC, 0644);
    if (fd == -1) {
    
        perror("open failed\n");
        exit(EXIT_FAILURE);
    }
    ftruncate(fd, sizeof(data));
 
    // 使用fd创建内存映射区
    addr = (Data *)mmap(NULL, sizeof(data), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED) {
    
        perror("mmap failed!\n");
        exit(EXIT_FAILURE);
    }
    close(fd); // 映射完后文件就可以关闭了
 
    memcpy(addr, &data, sizeof(data)); // 往映射区写数据
    munmap(addr, sizeof(data)); // 释放映射区
    return 0;
}
读端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
 
typedef struct _data {
    
    int a;
    char b[64];
} Data;
 
 
int main() {
    
    Data *addr;
    int fd;
 
    fd = open("mmap_temp_file", O_RDONLY);
    if (fd == -1) {
    
        perror("open failed\n");
        exit(EXIT_FAILURE);
    }
 
    // 使用fd创建内存映射区
    addr = (Data *)mmap(NULL, sizeof(Data), PROT_READ, MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED) {
    
        perror("mmap failed!\n");
        exit(EXIT_FAILURE);
    }
    close(fd); // 映射完后文件就可以关闭了
 
    printf("read form mmap: a = %d, b = %s\n", addr->a, addr->b); // 往映射区写数据
    munmap(addr, sizeof(Data)); // 释放映射区
    return 0;
}
执行结果:

在这里插入图片描述

2. 血缘关系进程通信

存在血缘关系的话,可以使用 匿名 的方式创建映射区,这样就不需要那个临时文件了。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
 
int m_var = 100;
 
int main() {
    
    int *addr;
    pid_t child_pid;
 
    // 以匿名的方式创建内存映射区,适用于存在血缘关系的进程间
    addr = (int *)mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0);
    if (addr == MAP_FAILED) {
    
        perror("mmap failed!\n");
        exit(EXIT_FAILURE);
    }
 
    child_pid = fork(); // 创建子进程
    if (child_pid == 0) {
    
        *addr = 666; // 往内存映射区写数据
        m_var = 200;
        printf("child process: *addr = %d, m_var = %d\n", *addr, m_var);
    } else {
    
        sleep(1);
        printf("parent process: *addr = %d, m_var = %d\n", *addr, m_var); // 读内存映射区的数据
        wait(NULL);
 
        int ret = munmap(addr, sizeof(int)); // 释放内存映射区
        if (ret == -1) {
    
            perror("munmap failed\n");
            exit(EXIT_FAILURE);
        }
    }
    return 0;
}
执行结果:

在这里插入图片描述
addr 的值,父子进程都成功改变了。全局变量 m_var 的值,父子进程遵从 读时共享,写时复制 原则。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/afei__/article/details/84257303

智能推荐

HMC使用手册_台达hmc07-n511h52使用手册-程序员宅基地

文章浏览阅读1k次。今天偶然间想起了之前发生的一次运行事件,在这次故障中,物理服务器宕机,远程终端无法救急。于是就需要用到HMC(硬件管理控制台)进行底层的操作,之前对于这个工具的使用确实不多,所以今天就特意在网上找了一篇关于HMC使用的手册,这里给大家分享一下,如果大家想要看网页原版,下面是网址:HMC使用手册..._台达hmc07-n511h52使用手册

设置定时任务的几种方式_制定定时任务的方法-程序员宅基地

文章浏览阅读471次。◦ 前言 ◦ 解决方案 普通线程sleep的方式实现定时任务 Timer实现定时任务 ScheduledExecutorService实现定时任务 Handler实现定时任务 AlarmManager实现精确定时操作前言项目中总是会因为各种需求添加各种定时任务,所以就打..._制定定时任务的方法

mysql支持的平台和操作系统_MySQL支持的操作系统列表_MySQL-程序员宅基地

文章浏览阅读461次。我们使用GNU Autoconf,因此将MySQL移植到所有使用Posix线程和C++编译器的现代系统是可能的。(要求服务器支持线程。如果只是编译客户端代码,则只需要C++编译器)。我们主要在Linux(SuSE和Red Hat)、FreeBSD和Sun Solaris(版本8和9)上使用并开发本软件。已经报告MySQL可以在下列操作系统/线程包的组合上成功地进行编译。注意,对于很多操作系统,原生..._mysql可以运行在哪些平台

MyEclipse下安装FatJar打包工具-程序员宅基地

文章浏览阅读112次。方法一:help > software updates > Find and install > add > add remote sitename:FatJarurl:http://kurucz-grafika.de/fatjar (这个是FatJarUpdateSite)填写name和url,之后要把fatjar那项打钩,接下来就一路Next吧。方法二:..._myeclipse2020 安装不了 fatjar 插件

STM32入门-GPIO端口的八种工作模式_gpio的8种工作模式-程序员宅基地

文章浏览阅读5.7w次,点赞428次,收藏2.2k次。新手必看,超讲解详细GPIO端口的八种工作模式_gpio的8种工作模式

QGIS基本功| 8 初识数据源_qgis qml-程序员宅基地

文章浏览阅读3k次,点赞2次,收藏10次。初次接触GIS的朋友,常常弄不清数据源(Data Source)和图层(Layer)的区别与联系,导致在数据处理和分发时出现一些问题,例如:辛辛苦苦做出了一幅漂亮的地图,为什么再次打开数据时,地图又变回丑陋的样子?怎么才能把工作成果分发给同事,让他打开数据时效果跟我本机一模一样?回答这些问题,我们需要从数据源的基本概念说起。本文从数据源和图层的区别与联系、打开数据源管理器、数据源管理器界面介绍和QGIS支持打开的数据源类型等方面,整体上初步介绍数据源相关知识。后续将陆续推出文章,详细介绍.._qgis qml

随便推点

Esp8266 进阶之路34 【外设篇③】乐鑫esp8266 NONOS SDK 3.0编程使用 SPI 驱动基于Max7219芯片的八位数码管,显示日期信息。(附带Demo)_8266时钟代码 数码管-程序员宅基地

文章浏览阅读7k次,点赞5次,收藏28次。本系列博客学习由非官方人员 半颗心脏 潜心所力所写,仅仅做个人技术交流分享,不做任何商业用途。如有不对之处,请留言,本人及时更改。 1、 Esp8266之 搭建开发环境,开始一个“hellow world”串口打印。 2、 Esp8266之 利用GPIO开始使用按钮点亮你的“第一盏灯”。 3、 Esp8266之 利用 "软件定时器 " 定时0.5秒闪烁点亮一盏LED。4 、Es..._8266时钟代码 数码管

2024年阿里云服务器地域选择方法_地域城市分布表-程序员宅基地

文章浏览阅读639次,点赞22次,收藏20次。阿里云服务器地域选择方法,如何选择速度更快、网络延迟更低的地域节点,地域指云服务器所在的地理位置区域,地域以城市划分,如北京、杭州、深圳及上海等,如何选择地域?建议根据用户所在地区就近选择地域,用户距离地域所在城市越近,网络延迟越低,速度越快。阿里云服务器网aliyunfuwuqi.com从速度延迟、备案限制、多产品内网互通、不同地域价格等四点因素来考虑地域的选择因素,可以在阿里云CLUB中心查看当前最新的云服务器优惠券和配置报价表。

【云驻共创】云原生应用架构之企业核心业务未来架构演进路线及华为云方案_基于云原生架构构建核心业务支撑系统应用试点研究-程序员宅基地

文章浏览阅读2.6w次,点赞4次,收藏9次。文章目录前言前言本文整理自华为云社区【内容共创】活动第14期。查看活动详情:https://bbs.huaweicloud.com/blogs/336904相关任务详情:任务16.企业核心业务未来架构演进路线及华为云方案_基于云原生架构构建核心业务支撑系统应用试点研究

实验十九、利用运算电路解方程_利用运算电路求解方程的方法研究-程序员宅基地

文章浏览阅读882次。运算电路求解一元二次方程_利用运算电路求解方程的方法研究

Vue3+ElementPlus 根据路由 自动创建二级菜单_vue 项目 element新增菜单下级页面-程序员宅基地

文章浏览阅读334次,点赞5次,收藏4次。本人分享一下这次字节跳动、美团、头条等大厂的面试真题涉及到的知识点,以及我个人的学习方法、学习路线等,当然也整理了一些学习文档资料出来是附赠给大家的。知识点涉及比较全面,包括但不限于前端基础,HTML,CSS,JavaScript,Vue,ES6,HTTP,浏览器,算法等等详细大厂面试题答案、学习笔记、学习视频等资料领取,点击资料领取直通车》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**_vue 项目 element新增菜单下级页面

linux下mysql安装完后是默认:区分表名的大小写,不区分列名的大小写;_linux按照mysql数据库配置表名不区分大小写-程序员宅基地

文章浏览阅读1.2k次。1、linux下mysql安装完后是默认:区分表名的大小写,不区分列名的大小写; 2、用root帐号登录后,在/etc/my.cnf 中的[mysqld]后添加添加lower_case_table_names=1,重启MYSQL服务,这时已设置成功:不区分表名的大小写; lower_case_table_names参数详解: lower_case_table_names = 0 其_linux按照mysql数据库配置表名不区分大小写