mmap 文件映射

news/2025/2/10 11:42:59/

🌈 个人主页:Zfox_
🔥 系列专栏:Linux

目录

  • 一:🔥 mmap介绍
    • 🦋 基本说明
    • 🦋 参数介绍
    • 🦋 返回值
  • 二:🔥 demo代码
    • 🦋 写入映射
    • 🦋 读取映射
  • 三:🔥 极简模拟实现 malloc
  • 四:🔥 共勉

mmapfont_10">一:🔥 mmap介绍

NAMEmmap, munmap - map or unmap files or devices into memorySYNOPSIS#include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);int munmap(void *addr, size_t length);

🦋 基本说明

  • 🧑‍💻 允许用户空间程序将文件或设备的内容直接映射到进程的虚拟地址空间中。通过 mmap ,程序可以高效地访问文件数据,而无需通过传统的 read 或 write 系统调用进行数据的复制
  • 🧑‍💻 mmap 还可以用于实现共享内存,允许不同进程间共享数据
    在这里插入图片描述

🦋 参数介绍

💻

  • 🌾 🦁 void *addr 一个提示地址,表示希望映射区域开始的地址。然而,这个地址可能会被内核忽略,特别是当我们没有足够的权限来请求特定的地址时。如果 addr 是 NULL,则系统会自动选择一个合适的地址。
  • size_t length要映射到进程地址空间中的字节数。这个长度必须是系统页面大小的整数倍(通常是 4KB,但可能因系统而异)。如果指定的 length 不是页面大小的整数倍,系统可能会向上舍入到最近的页面大小边界(系统内存页大小为 4KB(即 4096 字节),而请求的内存大小为 3500 字节,则按照向上舍入的原则,应分配 4096 字节的内存)。
  • int prot指定了映射区域的内存保护属性。可以是以下值的组合(使用按位或运算符 |):
    • PROT_READ映射区域可读。
    • PROT_WRITE映射区域可写。
    • PROT_EXEC映射区域可执行。
  • int flags指定了映射的类型和其他选项:
    • MAP_PRIVATE创建一个私有映射。对映射区域的修改不会反映到底层文件中。
    • MAP_SHARED创建一个共享映射。对映射区域的修改会反映到底层文件中(前提是文件是以写方式打开的,并且文件系统支持这种操作)。
    • 其他选项(如 MAP_ANONYMOUS、MAP_ANONYMOUS_SHARED 等)可能也存在于某些系统上,用于创建不与文件关联的匿名映射。
The flags argumentThe flags argument determines whether updates to the mapping are visible to other processes mapping the same region, and whether updates are carried through to the underlying file.  This behavior is determined by including exactly one of the following values in flags:MAP_SHAREDShare this mapping.  Updates to the mapping are visible to other processes mapping the same region, and (in the case of file-backed mappings) are carried through to the underlying  file.(To precisely control when updates are carried through to the underlying file requires the use of msync(2).)MAP_SHARED_VALIDATE (since Linux 4.15)This  flag  provides  the  same behavior as MAP_SHARED except that MAP_SHARED mappings ignore unknown flags in flags.  By contrast, when creating a mapping using MAP_SHARED_VALIDATE, thekernel verifies all passed flags are known and fails the mapping with the error EOPNOTSUPP for unknown flags.  This mapping type is also required to be able to  use  some  mapping  flags(e.g., MAP_SYNC).MAP_PRIVATECreate  a  private copy-on-write mapping.  Updates to the mapping are not visible to other processes mapping the same file, and are not carried through to the underlying file.  It is un‐specified whether changes made to the file after the mmap() call are visible in the mapped region.
  • int fd : 一个有效的文件描述符,指向要映射的文件或设备。对于匿名映射,这个参数可以是 -1 (在某些系统上,也可以使用 MAP_ANONYMOUS 或 MAP_ANON 标志来指定匿名映射,此时 fd 参数会被忽略)
  • off_t offset : 文件中的起始偏移量,即映射区域的开始位置。offset 和 length 一起定义了映射区域在文件中的位置和大小。

🦋 返回值

RETURN VALUEOn success, mmap() returns a pointer to the mapped area.  On error, the value MAP_FAILED (that is, (void *) -1) is returned, and errno is set to indicate the cause of the error.On success, munmap() returns 0.  On failure, it returns -1, and errno is set to indicate the cause of the error (probably to EINVAL).

二:🔥 demo代码

🧑‍💻 引入一个手动调整文件大小的系统调用
在这里插入图片描述

🦋 写入映射

#include <iostream>
#include <string>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>#define PAGE_SIZE 4096// write_map filename
int main(int argc, char *argv[])
{if(argc != 2){std::cout << "Usage: " << argv[0] << " filename" << std::endl;return 1;}// 1. 打开目标文件,mmap 需要你自己先打开文件int fd = ::open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0666);if(fd < 0){std::cout << "Failed to open file: " << argv[1] << std::endl;return 2;}// 2. 我们要手动调整一下文件的大小,方便进行合法映射,用0填充if(ftruncate(fd, PAGE_SIZE) == -1){std::cout << "Failed to ftruncate file: " << argv[1] << std::endl;return 3;}// 3. 文件映射char *shmaddr = (char*)::mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if(shmaddr == MAP_FAILED){std::cout << "Failed to mmap file: " << argv[1] << std::endl;return 4;}// 4. 进行文件操作for(char c = 'a'; c <= 'z'; c++){shmaddr[c - 'a'] = c;sleep(1);}// 5. 关闭映射if(::munmap(shmaddr, PAGE_SIZE) == -1){std::cout << "Failed to munmap file: " << argv[1] << std::endl;return 5;}::close(fd);return 0;
}

💻 运行:

root@# ./a.out log.txt
^C
root@# ll
total 44
drwxr-xr-x 2 root root  4096 Feb  8 23:35 ./
drwxr-xr-x 7 root root  4096 Feb  8 20:58 ../
-rwxr-xr-x 1 root root 16832 Feb  8 23:35 a.out*
-rw-r--r-- 1 root root  4096 Feb  8 23:35 log.txt
-rw-r--r-- 1 root root  1409 Feb  8 21:38 WriteMmap.cc

🧑‍💻 这里我们可以看到 log.txt 文件的大小正是 4096,剩余的用 null 填充,文件内容也是我们写入的 a - z
在这里插入图片描述

🦋 读取映射

#include <iostream>
#include <string>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>// write_map filename
int main(int argc, char *argv[])
{if(argc != 2){std::cout << "Usage: " << argv[0] << " filename" << std::endl;return 1;}// 1. 打开目标文件,mmap 需要你自己先打开文件int fd = ::open(argv[1], O_RDONLY);if(fd < 0){std::cout << "Failed to open file: " << argv[1] << std::endl;return 2;}// 2. 获取文件大小struct stat st;::fstat(fd, &st);// 3. 文件映射char *shmaddr = (char*)::mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);if(shmaddr == MAP_FAILED){std::cout << "Failed to mmap file: " << argv[1] << std::endl;return 4;}// 4. 进行文件操作std::cout << "File content: " << shmaddr << std::endl;// 5. 关闭映射if(::munmap(shmaddr, st.st_size) == -1){std::cout << "Failed to munmap file: " << argv[1] << std::endl;return 5;}::close(fd);return 0;
}

💻 运行:
此时我们读取刚才写入文件的内容 a-z:

root@# ./a.out log.txt
File content: abcdefghijklmnopqrstuvwxyz

三:🔥 极简模拟实现 malloc

🧑‍💻 在内存映射中,MAP_PRIVATE 和 MAP_SHARED 是两种不同的映射类型,它们定义了对映射区域的修改是如何反映到底层文件的。

  • MAP_PRIVATE(私有映射)

    • ⚠️ 当使用 MAP_PRIVATE 标志创建映射时,会生成一个写时拷贝(copy-on-write,COW)的私有映射,这些修改实际上是在内存中进行的,并且是私有的,不会影响其他进程的映射或文件在磁盘上的实际内容。
    • ⚠️ 对这个映射区域的任何修改都不会影响底层文件,也不会影响其他进程的映射。
  • MAP_SHARED(共享映射)

    • ⚠️ 使用 MAP_SHARED 标志创建的映射是可写的,并且对映射区域的修改会反映到底层文件中。
    • ⚠️ 这种映射类型允许多个进程共享对同一文件的访问,并且所有进程看到的是文件的最新状态。
    • ⚠️ 如果一个进程修改了映射区域,其他进程也会看到这些更改。
    • ⚠️ 共享映射适用于多个进程需要协作修改文件内容的情况。
  • MAP_ANONYMOUS (匿名映射)

    • mmap 系统调用中的一个标志,用于创建匿名映射。匿名映射区是指创建的映射区域不与任何文件关联,而是由操作系统分配的匿名内存。在调用 mmap 进行匿名映射的时候,是将进程虚拟内存空间中的某一段虚拟内存区域与物理内存中的匿名内存页进行映射。
#include <iostream>
#include <string>
#include <cstring>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>void *my_malloc(size_t size)
{if (size > 0){void *ptr = ::mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);if (ptr == MAP_FAILED){return nullptr;}return ptr;}return nullptr;
}void my_free(void *start, size_t size)
{if (start != nullptr && size > 0){int ret = ::munmap(start, size);if (ret != 0){perror("munmap");}}
}int main()
{char *ptr = (char*)my_malloc(1024);if (ptr == nullptr){std::cerr << "malloc failed" << std::endl;return 1;}// 使用分配的内存 (这里只是简单地打印指针值)printf("Allocated memory at address: %p\n", ptr);//...在这里可以使用ptr指向的内存...memset(ptr, 'A', 1024);for (int i = 0; i < 1024; i++){printf("%c ", ptr[i]);fflush(stdout);sleep(1);}my_free(ptr, 1024);return 0;
}

💻 运行:

root@# ./a.out 
Allocated memory at address: 0x7f1810db7000
A A A A A A A A A A A

👀 为了更好的观察我们所创建的内存空间 我们使用 gdb 调试工具

root@# g++ MallocMmap.cc -g -std=c++11
root@# gdb ./a.out 

在这里插入图片描述

我们在第36行打一个断点,然后运行程序

在这里插入图片描述

可以看到此时 ptr 的地址是 0

在这里插入图片描述

此时我们输入:

(gdb) info proc mapping 
  • 在 GDB(GNU调试器)中,info proc mapping 命令用于显示当前进程的内存映射信息,包括可执行文件的代码段、数据段、堆和栈等信息,以及共享库的地址空间等信息。通过这些信息,我们可以更好地了解程序的内存使用情况,方便我们进行调试和优化。

  • 具体来说,该命令会列出每个内存区域的起始地址、结束地址、大小、偏移量和访问权限等详细信息。
    在这里插入图片描述

  • 可以看到 ptr 对应的地址是进行了内存映射的(匿名映射)MAP_ANONYMOUS

📚 我们知道实际上虚拟地址空间都会有一个 struct vm_area_struct 然后链入自己的 pcb(task_struct)

在这里插入图片描述

当前我们做文件映射就会有文件描述符,而文件描述符是作为当前进程文件描述符表的索引去找对应的文件的,但是真正在 linux 内核里找到一个文件用的是 struct_file 结构体

那么进程地址空间是如何跟文件关联起来的呢?

在这里插入图片描述

🎯 我们发现在 linux 内核源代码 struct vm_area_struct 中包含了一个 struct_file * vm_file 直接指向打开的文件,文件都找到了,文件里又有什么内容找不到呢

四:🔥 共勉

😋 以上就是我对 mmap 文件映射 的理解, 觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~ 😉
在这里插入图片描述


http://www.ppmy.cn/news/1570851.html

相关文章

SOME/IP--协议英文原文讲解5

前言 SOME/IP协议越来越多的用于汽车电子行业中&#xff0c;关于协议详细完全的中文资料却没有&#xff0c;所以我将结合工作经验并对照英文原版协议做一系列的文章。基本分三大块&#xff1a; 1. SOME/IP协议讲解 2. SOME/IP-SD协议讲解 3. python/C举例调试讲解 这一章节…

ORB-SLAM2源码学习:Tracking.cc:GrabImageStereo、GrabImageRGBD、GrabImageMonocular处理图像

前言 该部分函数在Tracking.cc源文件中定义&#xff0c;用于处理图像。 1.函数作用&#xff1a; 1.GrabImageStereo 函数的主要作用是处理输入的双目图像&#xff08;左视图和右视图&#xff09;&#xff0c;进行必要的预处理&#xff08;颜色转换&#xff09;&#xff0c;创…

react使用if判断

1、第一种 function Dade(req:any){console.log(req)if(req.data.id 1){return <span>66666</span>}return <span style{{color:"red"}}>8888</span>}2、使用 {win.map((req,index) > ( <> <Dade data{req}/>{req.id 1 ?…

【基于SprintBoot+Mybatis+Mysql】电脑商城项目之上传头像和新增收货地址

&#x1f9f8;安清h&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;【Spring篇】【计算机网络】【Mybatis篇】 &#x1f6a6;作者简介&#xff1a;一个有趣爱睡觉的intp&#xff0c;期待和更多人分享自己所学知识的真诚大学生。 目录 &#x1f680;1.上传头像 -持久…

Leetcode 3449. Maximize the Minimum Game Score

Leetcode 3449. Maximize the Minimum Game Score 1. 解题思路2. 代码实现 题目链接&#xff1a;3449. Maximize the Minimum Game Score 1. 解题思路 这一题思路上就是一个二分法&#xff0c;尝试各个score&#xff0c;看看是否可以满足在给定的m次操作限制下&#xff0c;使…

2025年终总结

文章目录 前言2024年回顾1月2月4月5月6月7月8月10月12月 2025年 回顾1月2月 2025年期望 前言 最近因为一些原因&#xff0c;暂停了博客的书写。但是毕竟到年底了&#xff0c;还是简单作个总结好了。 2024年回顾 1月 回家之后。我堂哥找我聊天&#xff0c;说要一起开网店&am…

如何使用deepseek开发一个翻译API

什么是deepseek Deepseek 是一个基于人工智能技术的自然语言处理平台&#xff0c;提供了多种语言处理能力&#xff0c;包括文本翻译、语义分析、情感分析等。它通过深度学习模型和大规模语料库训练&#xff0c;能够实现高质量的文本翻译和多语言理解。Deepseek 的核心优势在于…

Ranger 2.1.0 Admin安装

个人博客地址&#xff1a;Ranger 2.1.0 Admin安装 | 一张假钞的真实世界 Creating MySQL user ranger2 failed 2021-07-01 18:44:53,480 [E] Creating MySQL user ranger2 failed.. 2021-07-01 18:44:53,496 [E] DB schema setup failed! Please contact Administrator. 原…