Linux C++ 内存映射文件及其应用

news2024/9/27 11:38:00

Linux C++ 内存映射文件及其应用

  • 一. 原理
  • 二. 应用场景
    • 1. 实现多进程通信
    • 2. 实现内存持久化
    • 3.实现读写大文件
  • 三. 问答
  • 参考链接

一. 原理

内存映射文件,是从一个文件到一块内存的映射。
内存映射文件与虚拟内存有些类似,通过内存映射文件可以将文件与内存中一个虚拟地址块关联在一起。从而在应用(包括多个进程)可以直接对内存执行读取和写入操作,从而读写文件。

优势

  • 效率高:
    • 数据映射到内存进行操作(由OS负责在恰当时机将数据同步到磁盘)故效率高
    • 直接将文件从硬盘拷贝到用户空间(没有拷贝到内核空间),只进行了一次数据拷贝
  • 易用性:可以直接对内存执行读取和写入操作,从而读写文件,不必再对文件执行I/O操作。
  • 按需映射:只将文件的部分内容映射到内存,在处理大数据量的文件时能起到相当重要的作用。
  • 多映射共享:同一个文件可被多个进程映射到各自内存,各进程看到的内容一致,即该技术自身做到了“同步”。

二. 应用场景

1. 实现多进程通信

a.cpp

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

const int SIZE = 1 << 20; // 1M
const char* szFileName = "./testshm.txt";

int main()
{
    int fd = open(szFileName, O_RDWR | O_CREAT);
    ftruncate(fd, SIZE);

    void *ptr = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (ptr == MAP_FAILED)
    {
        perror("mmap");
        return -1;
    }

    memset(ptr, ' ', sizeof(char) * SIZE);
    strcpy((char *)ptr, "hello, I am process a.");
    munmap(ptr, SIZE);
    return 0;
}

b.cpp

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

const char* szFileName = "./testshm.txt";

int main()
{
    int fd = open(szFileName, O_RDWR);
    int size = lseek(fd, 0, SEEK_END);

    void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (ptr == MAP_FAILED)
    {
        perror("mmap");
        exit(0);
    }

    printf("read data: %s\n", ptr);
    munmap(ptr, size);
    return 0;
}

编译执行

[root@QingYun ~]# g++ a.cpp -o a
[root@QingYun ~]# g++ b.cpp -o b
[root@QingYun ~]# ./a
[root@QingYun ~]# ./b
read data: hello, I am process a.
[root@QingYun ~]#

2. 实现内存持久化

共享内存持久化的实现中有重要的两点:

  • 共享内存映射的虚拟地址不是从0开始,而是从一个大地址开始(128M)
  • 共享内存映射的地址是连续的,并且指定了MAP_FIXED参数
    基于以上两点,可以实现
  • 可以增量的扩展共享内存文件
  • 映射出来连续的地址空间,从而实现可以通过记录号查找记录数据

示例代码如下:

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

const int CNT = 8;                      // 
const int SIZE = 1 << 20;               // 1M
const int BASE = 1 << 27;               // 基址=128M,注意,如果不指定一个较大的地址作为起始地址,会导致mmap映射失败!!
const char *szData = "hello, mmap.\n";  // 

int main(int argc, char **argv) {
    // 1. 打开共享内存
    int fd = shm_open("testshm.dat", O_CREAT | O_RDWR, 0777);
    if (fd < 0) {
        printf("error open\n");
        return 0;
    }

    // 2.增量映射共享内存
    for (int i = 0; i < CNT; i++) {
        // 2.1 扩增共享内存文件
        ftruncate(fd, (i + 1) * SIZE);

        // 2.2 映射扩增共享内存文件
        void *lpShm = mmap((void *)(BASE + i * SIZE), SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, SIZE * i);

        if (lpShm == MAP_FAILED) {
            printf("mmap error %p, errno=%d\n", lpShm, errno);
            return -1;
        }
        else {
            memset(lpShm, '0', SIZE * sizeof(char));
            memcpy(lpShm, szData, sizeof(char) * strlen(szData));
            printf("mmap success: i=%d, lpShm=%p\n", i, lpShm);
        }

        sleep(1);
    }

    szData = "hello, 12345678\n";
    // 注意:只有映射地址连续,才能使用(BASE + SIZE*2 + 1000)的基址变址寻址操作,否则程序会崩溃!!!!!
    memcpy((void*)(BASE + SIZE*2 + 1000), szData, sizeof(char) * strlen(szData));
    // munmap(lpData, sizeof(char) * CNT * SIZE);
    close(fd);
}

3.实现读写大文件

c.cpp

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

const int SIZE = 1 << 20; // 1M
const char* szFileName = "./testshm.txt";

int main()
{
    int fd = open(szFileName, O_RDWR | O_CREAT);
    ftruncate(fd, SIZE);

    void *ptr = mmap(NULL, 0, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (ptr == MAP_FAILED)
    {
        perror("mmap");
        return -1;
    }

    memset(ptr, 0, sizeof(char) * SIZE);
    // 此处修改成写到 ptr + 4096
    strcpy((char *)ptr + 4096, "hello, I am process a.");
    munmap(ptr, SIZE);
    return 0;
}

d.cpp

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

const char *szFileName = "./testshm.txt";

int main()
{
    int fd = open(szFileName, O_RDWR);
    int size = lseek(fd, 0, SEEK_END);

    // 此处修改成,指定从4096位置开始,读取16字节
    void *ptr = mmap(NULL, 16, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 4096);
    if (ptr == MAP_FAILED)
    {
        perror("mmap");
        exit(0);
    }

    char buf[1024] = {0};
    snprintf(buf, 16, "%s", ptr);
    printf("read data: %s\n", buf);
    munmap(ptr, 16);
    return 0;
}

三. 问答

1.如果对mmap的返回值(ptr)做++操作(ptr++),munmap是否能成功?

void* ptr=mmap(…); ptr++; 可以对齐进行++操作 munmap(ptr,len); //错误,要保持地址

2.如果open时O_RDONLY,mmap时prot参数指定PORT_READ | PORT_WRITE会怎样?

错误,返回MAP_FAILED open()函数中的权限建议和prot参数的权限保持一致。

3.如果文件偏移量为1000会怎样?

偏移量必须是页大小(通常为4k)的整数倍,返回MAP_FAILED

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

第二个参数:length=0,报错:Invalid argument
第三个参数:prot指定的权限比如:PROT_WRITE,在第五个参数fd中不满足(open()使用O_RDONLY),报错:Permission denied
第六个参数:设置文件的偏移量不是页大小的整数倍,报错:Invalid argument

5.可以open的时候O_CREAT一个新文件来创建映射区吗?

可以的,但是创建文件的大小如果为0的话,肯定不行
可以对新的文件进行扩展

  • lseek()
  • truncate()

6.mmap后关闭文件描述符,对mmap映射有没有影响?

映射区还是存在,创建映射区的fd被关闭,没有任何影响。

7.对ptr越界操作会怎样?

void* ptr=mmap(NULL,100,…);
越界操作操作的时非法内存 -> 段错误

参考链接

  • 操作系统 | 内存文件映射 —— 文件到内存的映射
  • 共享内存之——mmap内存映射
  • Linux的mmap 源码分析
  • Linux C/C++内存映射

在这里插入图片描述

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

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

相关文章

【ESP32+freeRTOS学习笔记-(十)任务通知机制】

目录1、任务通知&#xff08;Task Notifications&#xff09;概念1.1 概念1.2 使用任务通知的优势1.3 无法使用任务通知的场景2、使用任务通知2.1 xTaskNotifyGive()2.2 ulTaskNotifyTake()2.3 使用任务通知代替信号量&#xff0c;方法12.4 使用任务通知代替信号量&#xff0c;…

mtd和嵌入式系统分区

一. MTD介绍 嵌入式系统的磁盘分区情况&#xff0c;一般由mtd来表示。 MTD&#xff08;Memory Technology Device)&#xff1a;内存技术设备是Linux的存储设备中的一个子系统。其设计此系统的目的是&#xff0c;对于内存类的设备&#xff0c;提供一个抽象层&#xff0c;一个接口…

【计算机三级网络技术】 第五篇 局域网技术基础及应用

文章目录一、局域网组网基础知识1.局域网的主要特征2.局域网标准3.交换式局域网4.虚拟局域网技术二、综合布线基本概念1.综合布线系统的组成2.综合布线的优点3.综合布线的设计等级4综合布线系统标准三、局域网互联设备1.中继器2.集线器3.网桥&#xff08;Bridge&#xff09;4.交…

eKuiper 1.8.0 发布:零代码实现图像/视频流的实时 AI 推理

LF Edge eKuiper 是 Golang 实现的轻量级物联网边缘分析、流式处理开源软件&#xff0c;可以运行在各类资源受限的边缘设备上。eKuiper 的主要目标是在边缘端提供一个流媒体软件框架&#xff08;类似于 Apache Flink &#xff09;。eKuiper 的规则引擎允许用户提供基于 SQL 或基…

10_创建和管理表

表操作基础1.1 一条数据存储的过程存储数据是处理数据的第一步 。只有正确地把数据存储起来&#xff0c;我们才能进行有效的处理和分析。否则&#xff0c;只能是一团乱麻&#xff0c;无从下手。那么&#xff0c;怎样才能把用户各种经营相关的、纷繁复杂的数据&#xff0c;有序、…

CIMCAI intelligent railway logistics world leading container AI

港航人工智能/集装箱人工智能领军企业CIMCAI&#xff0c;多式联运智能化铁路货运智能化方案&#xff0c;全自动化集装箱识别数字化海运铁路运输集装箱管理。CIMCAI中集飞瞳是世界应用落地最广&#xff0c;规模最大&#xff0c;最先进的的集装箱人工智能高科技企业。世界集装箱A…

一文读懂PaddleSpeech中英混合语音识别技术

语音识别技术能够让计算机理解人类的语音&#xff0c;从而支持多种语音交互的场景&#xff0c;如手机应用、人车协同、机器人对话、语音转写等。然而&#xff0c;在这些场景中&#xff0c;语音识别的输入并不总是单一的语言&#xff0c;有时会出现多语言混合的情况。例如&#…

PMP怎么对项目工程师的职业生涯产生影响?

职业生涯的影响无非两个&#xff1a; 工作能力增强升职加薪 考取证书你能获得&#xff1a; a、全面的、科学的、专业的项目管理架构理论知识; b、经过模拟实践题的联系&#xff0c;项目经验可以媲美拥有三年以上的项目管理经验的项目管理人士; c、所学的知识基础与实践已经获得…

flutter 优化检测工具

本篇内容主要结合我的另一篇文章flutter 开发App优化之旅同步补充的一篇文章&#xff0c;以供各位攻城狮借鉴共勉。 1、Flutter Inspector (debug模式下) Flutter Inspector有很多功能&#xff0c;其中有两个功能更值得我们去关注&#xff0c;例如&#xff1a;“Select Widget…

Python+Yolov5跌倒检测 摔倒检测 人物目标行为 人体特征识别

PythonYolov5跌倒检测 摔倒检测 人物目标行为 人体特征识别如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01;前言这篇博客针对<<PythonYolov5跌倒摔倒人体特征识别>>编写代码&#xff0c;代码整洁&…

Torch中常见插值方式及各自的优缺点

Pytorch常见插值方式及优缺点1 插值算法2 Pytorch中能看到的插值方式3 Nearest插值法3.1 方法介绍3.2 优缺点4 Linear插值法4.1 方法接受4.2 优缺点5 Bilinear插值法5.1 方法介绍5.2 优缺点6 Bicubic插值法6.1 方法介绍6.2 优缺点7 Trlinear插值法7.1 方法介绍7.2 优缺点8 图片…

C#窗体应用程序可能会遇到的一些奇怪问题

最近在上程序实训课&#xff0c;写一个管理程序&#xff0c;主要是用了C#&#xff0c;在VS2017平台&#xff0c;在开发过程中自然是少不了很多奇怪的问题&#xff0c;做个记录。 有下面几个问题: 问题1&#xff1a;.Conversion failed when converting from a character stri…

小红书“复刻”微信,微信“内造”小红书

配图来自Canva可画 随着互联网增长红利逐渐见顶&#xff0c;各大互联网平台对流量的争夺变得愈发激烈。而为了寻找新的业务可能性&#xff0c;各家都在不遗余力地拓宽自身边界。在此背景下&#xff0c;目前最为“吸睛”和“吸金”的社交、电商、种草、短视频等领域&#xff0c…

linux创建文件软连接和硬链接详解

前言linux系统中链接文件仔细区分可以分为软连接&#xff08;符号链接&#xff09;和硬链接。软链接比硬链接应用更广泛&#xff0c;所以也可以认为linux链接文件就是指软链接文件。本文将会在第2部分介绍创建软链接和硬链接的基本命令&#xff0c;在第3部分从linux文件系统的角…

Gated Activations门控激活单元

门控激活 在架构图的方框部分&#xff0c;您会注意到扩张卷积输出分成两个分支&#xff0c;随后通过逐元素乘法重新组合。这描绘了一个门控激活单元&#xff0c;其中我们将tanh激活分支解释为一个学习过滤器&#xff0c;将sigmoid激活分支解释为一个学习门&#xff0c;用于调节…

(五十五)大白话更新数据的时候,自动维护的聚簇索引到底是什么?

上一次我们给大家讲了一下基于主键如何组织一个索引&#xff0c;然后建立索引之后&#xff0c;如何基于主键在索引中快速定位到那行数据所在的数据页&#xff0c;再如何进入数据页快速到定位那行数据&#xff0c;大家看下面的图。 我们今天就先基于上面的图&#xff0c;把按照主…

·神经网络

目录11神经网络demo112神经网络demo213神经网络demo320tensorflow2.0 安装教程,所有安装工具&#xff08;神经网络&#xff09;21神经网络-线性回归- demo122神经网络-线性回归- demo228神经网络-多层感知- demo1目录11神经网络demo1 package com.example.xxx; import java.ut…

玩转qsort——“C”

各位CSDN的uu们你们好呀&#xff0c;今天小雅兰的内容还是我们的深度剖析指针呀&#xff0c;上篇博客我们学习了回调函数这个知识点&#xff0c;但是没有写完&#xff0c;因为&#xff1a;小雅兰觉得qsort值得单独写出来&#xff01;&#xff01;&#xff01;好啦&#xff0c;就…

Ae:合成设置

Ae菜单&#xff1a;合成/合成设置Composition Settings快捷键&#xff1a;Ctrl K合成名称Composition Name为合成定义一个恰当的名称以便于查找和识别。◆ ◆ ◆基本Basic有关合成的一些常规设置。预设Preset给出了适合各种平台的常用预设。也可以创建并保存自己的自定义预设…

项目请求地址自动加上了本地ip的解决方式

一般情况下来说都是一些粗心大意的问题导致的 场景一&#xff1a;少加了/ 场景二&#xff1a;前后多加了空格 场景三&#xff1a;拼接地址错误![