【Linux】关于 mmap 文件映射

张开发
2026/6/9 18:58:26 15 分钟阅读
【Linux】关于 mmap 文件映射
目录一、mmap 文件映射1.1 基本说明1.2 参数介绍1.3 写入映射1.4 读取映射1.5 简单实现 malloc个人主页矢望个人专栏C、Linux、C语言、数据结构、Coze-AI、MySQL一、mmap 文件映射1.1 基本说明mmapmemory map内存映射是操作系统提供的一个系统调用它将文件或设备的内容映射到进程的虚拟地址空间中使进程可以像访问内存一样直接读写文件而无需使用传统的read / write系统调用。核心让内核将文件数据页直接映射到进程的页表中当进程访问该内存区域时内核自动完成文件数据与物理内存之间的同步。mmap可以用于实现共享内存允许不同进程间共享数据可以用于动态内存分配等。mmap函数1.2 参数介绍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等可能也存在于某些系统上用于创建不与文件关联的匿名映射。int fd:一个有效的文件描述符指向要映射的文件或设备。对于匿名映射这个参数可以是-1在某些系统上也可以使用MAP_ANONYMOUS或MAP_ANON标志来指定匿名映射此时fd参数会被忽略。off_t offset:文件中的起始偏移量即映射区域的开始位置。offset和length一起定义了映射区域在文件中的位置和大小。返回值mmap成功时返回映射区域的起始地址void*类型失败时返回MAP_FAILED通常定义为(void*)-1。1.3 写入映射这里我们要写的demo代码是使用mmap将文件映射到内存然后直接通过内存写入来修改文件内容。首先要做的工作就是打开目标文件。#includeiostream#includesys/types.h#includesys/stat.h#includefcntl.h#includecstdlibintmain(intargc,char*argv[]){if(argc!2){std::coutUsage: argv[0] filenamestd::endl;exit(1);}// 1、打开目标文件intfd::open(argv[1],O_CREAT|O_RDWR|O_TRUNC,0666);if(fd0){std::cerropen file failstd::endl;exit(2);}return0;}这里有一个细节我们使用mmap将文件映射到内存时是想要求文件可读可写的所以我们需要以读写方式O_RDWR的方式打开文件。打开文件之后第二步就是要手动调整这个文件的大小刚创建出来的文件大小是0而mmap映射时文件大小必须≥映射长度否则会失败。这里调整大小用到了ftruncate函数。ftruncate是一个系统调用用于将文件截断或扩展到指定长度。新增的区域用空字节\0填充。成功时返回0失败返回-1。#defineSIZE1024intmain(intargc,char*argv[]){// 2、手动调整文件大小if(::ftruncate(fd,SIZE)-1){std::cerrFailed to truncate file: argv[1]std::endl;exit(3);}return0;}紧接着就是进行文件映射的工作。// 3、进行文件映射char*shmaddr(char*)::mmap(nullptr,SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);if(shmaddrMAP_FAILED){std::cerrFailed to mmap fileargv[1]std::endl;exit(4);}参数值含义addrnullptr让操作系统自动选择映射地址最常用lengthSIZE映射的字节数建议是页大小的倍数如4096、8192protPROT_READ | PROT_WRITE可读可写权限flagsMAP_SHARED共享映射修改会写回文件其他进程可见fd文件描述符指向文件普通文件或shm_open对象offset0从文件开头开始映射文件映射完成之后就是进行文件操作我们进行写入文件。// 4、进行文件操作for(charca;cz;c){shmaddr[c-a]c;sleep(1);}完成文件操作之后就是取消文件映射和关闭文件。munmapMemory Unmap是mmap的逆操作用于解除内存映射。// 5、取消映射::munmap(shmaddr,SIZE);// 6、关闭文件::close(fd);运行测试如上写入成功并且文件的大小是1024和我们设置的一样。1.4 读取映射读取映射和写入映射的结构一模一样这里就在上面的demo代码上修改了。fstat是一个系统调用用于获取已打开文件的状态信息元数据如文件大小、权限、修改时间等。#includeiostream#includesys/types.h#includesys/stat.h#includefcntl.h#includecstdlib#includeunistd.h#includesys/mman.hintmain(intargc,char*argv[]){if(argc!2){std::coutUsage: argv[0] filenamestd::endl;exit(1);}// 1、打开目标文件intfd::open(argv[1],O_RDONLY);if(fd0){std::cerropen file failstd::endl;exit(2);}// 2、获取文件大小structstatst;::fstat(fd,st);// 3、进行文件映射char*shmaddr(char*)::mmap(nullptr,st.st_size,PROT_READ,MAP_SHARED,fd,0);if(shmaddrMAP_FAILED){std::cerrFailed to mmap fileargv[1]std::endl;exit(4);}// 4、进行文件操作std::coutFile content: shmaddrstd::endl;// 5、取消映射::munmap(shmaddr,st.st_size);// 6、关闭文件::close(fd);return0;}修改了打开文件和映射文件时的权限以及修改了第二步代码的核心结构没有改动。运行测试1.5 简单实现 malloc上图中这个参数的选项MAP_SHARED的修改会写回文件并对其他进程可见多进程共享同一份物理内存而MAP_PRIVATE的修改不写回文件且对其他进程不可见写时复制机制每个进程拥有独立副本。MAP_ANONYMOUS是mmap的一个标志用于创建不与任何文件关联的匿名内存映射。MAP_SHARED用于进程间通信、共享缓存、数据库、内存映射文件编辑器等需要多进程共享数据或持久化存储的场景MAP_PRIVATE用于加载动态库/可执行文件、只读分析大文件、临时修改配置等不需要保存修改或希望各进程保持独立的场景。MAP_PRIVATE | MAP_ANONYMOUS的作用是映射出一块匿名空间这种场景和malloc的场景很相像事实上大多数情境下malloc的底层就是使用mmap实现的。当使用这个选项时文件描述符就可以设为-1了。demo代码#includeiostream#includecstdio#includecstring#includecstdlib#includesys/mman.h#includeunistd.hvoid*my_malloc(size_t size){void*ptr::mmap(nullptr,size,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);if(ptrMAP_FAILED){returnnullptr;}returnptr;}voidmy_free(void*ptr,intsize){intret::munmap(ptr,size);if(ret!0){perror(munmap);}}intmain(){size_t size1024;char*str(char*)my_malloc(size);if(strnullptr){perror(my_malloc);exit(2);}printf(Allocated memory at address: %p\n,str);// 使用 str 指向的内存memset(str,A,size);for(inti0;isize;i){std::coutstr[i];}my_free(str,size);return0;}运行测试编译时加上-g选项使用gdb进行调试时执行info proc mapping就可以看到我们申请的空间。info proc mappings是一个GDB命令用于查看目标进程的内存映射布局显示进程的虚拟地址空间中各个区域的起始地址、权限、大小以及映射的文件等信息。上图中大小是0x1000就是4096字节即便我们使用的是1024字节mmap的length参数也会向上取整到4096字节。上面我们也可以看到我们的objfile一栏是空的因为是匿名映射。总结以上就是本期博客分享的全部内容啦如果觉得文章还不错的话可以三连支持一下你的支持就是我前进最大的动力技术的探索永无止境! 道阻且长行则将至后续我会给大家带来更多优质博客内容欢迎关注我的CSDN账号我们一同成长(▽)

更多文章