首页 > 系统相关 >使用mmap()创建内存映射

使用mmap()创建内存映射

时间:2024-10-21 22:34:01浏览次数:1  
标签:MAP 映射 文件 mmap 内存 进程

系统调用(mmap和munmap)

mmap

内存映射类型

mmap() 系统调用用于在调用进程的虚拟地址空间中 创建内存映射,主要分为两种类型:

  1. 文件映射:将文件的一部分直接映射到虚拟内存中,允许通过内存访问文件内容,映射的分页会在需要时自动加载

  2. 匿名映射:没有对应文件,分页初始化为0,可以视为一个内容总是为0的虚拟文件映射

映射内存可以被多个进程共享,具体情况包括:

  • 共享映射MAP_SHARED):修改内容对所有共享进程可见,直接影响底层文件
  • 私有映射MAP_PRIVATE):修改内容对其他进程不可见,使用 写时复制 技术确保每个进程的修改独立

映射类型总结

映射类型 变更可见性 主要用途
私有文件映射 不可见 初始化内存区域,如文本和数据段
私有匿名映射 不可见 分配零填充内存
共享文件映射 可见 内存映射 I/O,进程间共享内存 (IPC)
共享匿名映射 可见 进程间共享内存 (IPC),仅限相关进程

其他说明

  • 通过 fork() 创建的子进程会继承映射,但在执行 exec() 时映射会丢失

  • 每次调用 mmap() 创建的新映射是独立的,尤其是在匿名映射的情况下

函数原型

#include <sys/mman.h>

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:要映射的文件描述符,使用 open 系统调用获取
    • offset:文件映射的起始偏移量,必须是页面大小的倍数
  • 返回值

    • 成功时返回映射区域的指针,失败时返回 MAP_FAILED,并设置 errno

munmap

munmap() 系统调用用于从进程的虚拟地址空间中 删除一个映射

函数原型

#include <sys/mman.h>
int munmap(void *addr, size_t length);
  • 参数说明

    • addr:待解除映射的起始地址,必须与分页边界对齐
    • length:指定解除映射区域的大小,必须为非负整数,通常应为系统分页大小的倍数
  • 返回值

    • 如果指定范围内不存在映射,munmap() 将无效并返回 0(表示成功)

补充说明

  • 解除映射

    • 通常解除整个映射,可以将 addr 设置为 mmap() 返回的地址,并使 lengthmmap() 使用的值相同
    • 也可以部分解除映射,可能导致映射收缩或分割
  • 内存锁

    • 解除映射时,内核会删除在指定范围内的所有内存锁(由 mlock()mlockall() 创建)
  • 自动解除

    • 进程终止或执行 exec() 时,所有映射会自动解除
  • 注意事项

    • 在解除共享文件映射之前,应先调用 msync() 确保内容写入底层文件

文件映射

1

这张图表示由参数offsetlength决定哪些文件区域被映射到虚拟内存中

共享/私有文件映射

多个进程共享同一区域的内存映射,共享文件映射 所有的修改都是可见的,同时也会反映到底层文件,私有文件映射 的修改仅调用进程自己可见,并且不会反应到底层文件,这是使用 写时复制 的技术实现,即,当要对该内存映射做修改时,内核会复制一份相同的给进程,从而使其真正的独立出来
2

内存映射I/O

内存映射 I/O 是一种将文件的内容映射到进程的虚拟内存地址空间的技术,使得程序可以通过 直接访问内存来执行文件 I/O 操作,而无需使用传统的 read()write() 系统调用

关键特点

  1. 共享文件映射:映射的内存内容源自文件,并且对映射内容的任何更改都会自动反映到文件中。这意味着可以通过简单的内存访问操作来进行文件 I/O

  2. 结构化数据类型:通常,程序会定义一个结构化数据类型,与磁盘文件的内容对应,以便于访问和处理映射的内存内容

    举个例子,假设磁盘文件中存储的是员工信息,那我可以先定义一个员工结构体

    struct Employee {
        int id;          // 员工ID
        char name[50];  // 员工姓名
        float salary;    // 员工薪资
    };
    

    一旦文件被映射到内存,程序可以直接通过访问结构体的字段来读取和修改员工信息,而不需要手动处理字节的偏移和数据格式

    struct Employee *employees = mmap(NULL, fileSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    printf("Employee ID: %d\n", employees[0].id); // 直接访问第一个员工的ID
    

优势

  1. 简化应用逻辑:使用内存访问替代传统的 read()write() 调用,可以使一些应用程序的逻辑更为简洁和直观。

  2. 提高性能

    • 减少数据传输:传统的 read()write() 操作需要两次数据传输:一次在文件和内核缓冲区之间,另一次在内核缓冲区和用户空间之间。而使用 mmap() 可以省去第二次传输,用户进程可以直接访问内存中的数据。
    • 共享内存:当使用 mmap() 时,内核空间和用户空间共享同一个缓冲区,避免了在用户空间和内核空间之间复制数据的开销。如果多个进程在同一文件上进行 I/O,它们可以共享同一内核缓冲区,从而节省内存使用。

边界情况

  1. 文件内容更大,超过映射区域
    3

  2. 文件内容小于映射区域
    4

同步映射区域:msync()

msync() 系统调用用于 显式控制共享内存映射与底层文件之间的同步。虽然内核会自动将 MAP_SHARED 映射内容的更改写入文件,但默认情况下并 不保证同步的时间

作用

  • 数据完整性:在数据库等应用中,调用 msync() 可以强制将数据写入磁盘,以确保数据完整性
  • 可见性:确保在可写映射上进行的更新对执行 read() 的其他进程可见

函数原型

#include <sys/mman.h>

int msync(void *addr, size_t length, int flags);
  • 参数

    • addr:需要同步的内存区域的起始地址,必须分页对齐
    • length:同步区域的大小,向上舍入到系统分页大小的下一个整数倍
    • flags:指定同步行为,可以是以下值:
      • MS_SYNC:执行同步写入,调用会阻塞直到所有修改的数据页写入磁盘
      • MS_ASYNC:执行异步写入,修改的数据页将在将来的某个时间写入磁盘,立即对其他进程可见
      • MS_INVALIDATE:使映射数据的缓存副本失效,确保下一次访问时从文件读取更新的内容

匿名映射

匿名映射

匿名映射是一种没有对应文件的内存映射。可以通过以下两种方式在Linux中创建匿名映射:

  1. 使用 MAP_ANONYMOUS

    • mmap()flags 中指定 MAP_ANONYMOUS,同时将 fd 设置为 -1。这个值会被忽略,但为可移植性,建议遵循这个约定
    • 需要在代码中定义 _BSD_SOURCE_SVID_SOURCE 来使用 MAP_ANONYMOUS
  2. 使用 /dev/zero

    • 打开 /dev/zero 设备文件并将其文件描述符传递给 mmap()。该设备始终返回0,写入的数据会被丢弃

无论使用哪种方法,得到的映射都会被初始化为0,且 offset 参数会被忽略

匿名映射类型

  • MAP_PRIVATE 匿名映射

    • 用于分配进程私有的内存块,并将其初始化为0。
    • glibcmalloc() 函数在分配大于 MMAP_THRESHOLD(默认128 KB,可调整)的内存时使用此映射,以提高内存管理效率并减少内存碎片
  • MAP_SHARED 匿名映射

    • 允许相关进程(如父子进程)共享一块内存区域而无需对应的映射文件
    • 如果在创建共享映射后调用 fork(),子进程会继承该映射,从而实现进程间的内存共享

代码示例

#ifdef USE_MAP_ANON
#define _BSD_SOURCE   // 获取 USE_MAP_ANON 定义
#endif

int *addr;   // 假设为int

#ifdef USE_MAP_ANON
  addr = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, 
              MAP_SHARED | MAP_ANONYMOUS, -1, 0);
#else
  int fd = open("/dev/zero", O_RDWR);
  addr = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE,
              MAP_SHARED, fd, 0);
#endif

标签:MAP,映射,文件,mmap,内存,进程
From: https://www.cnblogs.com/dylaris/p/18491457

相关文章

  • 记录一次内存泄漏排查
    事件描述order服务出现频繁GC告警,app卡顿事件回顾【2024-10-2108:20:04】order出现频繁GC告警【2024-10-2109:24:04】通过命令jmap-histo:live[pid]查看存活对象发现sentinel统计对象占用大量内存【2024-10-2110:33:04】dump下堆内存信息【2024-10-2110:35:04】版本......
  • [20241021]使用gdb查看修改内存地址以及相关值.txt
    [20241021]使用gdb查看修改内存地址以及相关值.txt--//执行oradebugpoke报错,感觉oracle已经禁止这类hack操作。1.环境:SYS@book>@ver2==============================PORT_STRING                  :x86_64/Linux2.4.xxVERSION              ......
  • podman 无根用户分配系统CPU、内存等系统资源,提示cgroup相关权限不足
    问题:在使用Podman以无根用户(rootless)模式创建容器时,如果遇到分配系统CPU等资源时提示cgroup权限不足,这是因为无根用户没有直接访问cgroup相关资源的权限。以下是一些解决方法(目前采用的办法3临时解决,,主要是更改系统目录权限sudochown-R$USER:$USER/sys/fs/cgro......
  • 揭秘PostgreSQL的隐藏奥秘:物理、内存与进程模型的深度解析与高效优化策略
    引言PostgreSQL作为一款强大的开源关系型数据库管理系统,以其灵活性、高性能和丰富的功能特性在全球范围内受到广泛欢迎。其底层架构的精心设计,使其在处理复杂查询、支持多种数据类型和高并发用户访问时表现出色。理解PostgreSQL的底层架构不仅有助于提升系统性能,还能帮助开......
  • C++基础与实用技巧第三课:内存管理与性能优化
    第二章:C++基础与实用技巧第三课:内存管理与性能优化1.动态内存的管理策略与技巧动态内存管理是C++编程的核心部分之一,合理管理内存可以极大提高程序的性能和稳定性。在C++中,动态内存的分配和释放通常使用new和delete运算符,但由于手动管理内存容易引入错误,因此建议使用现代C+......
  • vue3 ref 或者reactive被赋值其他对象数据,用的是同一块内存,而不是深拷贝
    <template><divclass='box'>{{abcDemo?.a?.b?.c}}<button@click="changeAbc">ChangeABC</button><div>{{abdDemo?.a?.b?.c}}</div></div></template><sc......
  • 七,JVM内存划分与参数传递
    Java编程基础:JVM内存划分与参数传递在Java编程中,了解Java虚拟机(JVM)的内存划分对于优化程序性能和资源管理至关重要。本文将详细探讨JVM内存的划分以及参数传递的机制,并提供图示以帮助理解。JVM内存划分JVM内存主要划分为以下几个区域:栈(Stack)局部变量:存储方法内部定义的局部......
  • Java的内存模型
    硬件效率的一致性======================================================================随着硬件技术的发展,处理器的处理能力越来越强大,但是与处理器交互的内存的处理能力并没有提升多少,读取运算,存储运算这些IO操作的瓶颈并没有得以消除,处理器的处理效率比内存的处理......
  • C系统编程通信方式——共享内存
        共享内存,标准IPC之一,也是进程间通信最快的一种方式。1.概念    所有的标准IPC都有一个内部ID作为唯一标识。内部ID的获取通过外部key,key的类型是key_t。key的获取方法有在头文件中定义所有key和通过ftok函数获取一个key。key_tftok(constchar*pathna......
  • pbootcms网站占用服务器内存很高的解决办法
    PBootCMSV3.2.5之前的版本确实存在缓存机制不够友好的问题,导致在数据量较大时占用大量内存,影响服务器性能和网站访问速度。以下是解决这一问题的步骤和注意事项:解决方法1.升级到官方最新版备份网站:在进行任何升级操作之前,务必备份整个网站,包括数据库和文件。下载最......