
目录1. mmap 介绍1-1 基本说明1-2 参数介绍1-3 返回值1-4 demo 代码1-4-1 写入映射1-4-2 读取映射2. 极简模拟实现 malloc1. mmap 介绍NAME mmap, munmap - map or unmap files or devices into memory SYNOPSIS #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);1-1 基本说明允许用户空间程序将文件或设备的内容直接映射到进程的虚拟地址空间中。通过mmap程序可以高效地访问文件数据而无须通过传统的read或write系统调用进行数据复制。mmap还可以用于实现共享内存允许不同进程间共享数据。1-2 参数介绍void *addr一个提示地址表示希望映射区域开始的地址。未使用MAP_FIXED时内核可以选择其他合适的页对齐地址如果addr是NULL则由系统自动选择地址。size_t length要映射到进程地址空间中的字节数。length不必是系统页面大小的整数倍但内核按页管理映射范围。若系统页面大小为 4KB4096 字节请求映射 3500 字节时该映射在虚拟地址空间中会覆盖一个 4KB 页面。int prot指定映射区域的内存保护属性可以是以下值的组合使用按位或运算符|PROT_READ映射区域可读。PROT_WRITE映射区域可写。PROT_EXEC映射区域可执行。int flags指定映射的类型和其他选项。MAP_PRIVATE创建一个私有映射。对映射区域的修改不会反映到底层文件中。MAP_SHARED创建一个共享映射。对映射区域的修改会反映到底层文件中前提是文件以写方式打开并且文件系统支持这种操作。MAP_ANONYMOUS用于创建不与文件关联的匿名映射私有匿名映射可写作MAP_ANONYMOUS | MAP_PRIVATE共享匿名映射可写作MAP_ANONYMOUS | MAP_SHARED。部分系统也将其定义为MAP_ANON。The 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_SHARED Share 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, the kernel 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_PRIVATE Create 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 unspecified 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一起定义了映射区域在文件中的位置和大小对于普通文件映射offset必须是系统页面大小的整数倍。1-3 返回值RETURN VALUE On 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).1-4 demo 代码1-4-1 写入映射#includeiostream#includestring#includeunistd.h#includesys/types.h#includesys/stat.h#includefcntl.h#includesys/mman.h#includecstring#defineSIZE4096intmain(intargc,char*argv[]){if(argc!2){std::cerrUsage: argv[0] filenamestd::endl;return1;}std::string filenameargv[1];// 注意要成功进行写入映射这里打开文件的模式必须是 O_RDWRintfd::open(filename.c_str(),O_CREAT|O_RDWR,0666);if(fd0){std::cerropen errorstd::endl;return2;}// 默认文件大小是 0无法和 mmap 进行正确映射这里需要调整文件大小用 0 值填充::ftruncate(fd,SIZE);char*mmap_addr(char*)::mmap(nullptr,SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);if(mmap_addrMAP_FAILED){perror(mmap);return3;}// 操作文件for(inti0;iSIZE;i){mmap_addr[i]ai%26;}// memcpy(mmap_addr, hello, 5);// 取消映射::munmap(mmap_addr,SIZE);// 关闭文件::close(fd);return0;}1-4-2 读取映射#includeiostream#includestring#includeunistd.h#includesys/types.h#includesys/stat.h#includefcntl.h#includesys/mman.h#includecstringintmain(intargc,char*argv[]){if(argc!2){std::cerrUsage: argv[0] filenamestd::endl;return1;}std::string filenameargv[1];// 只读映射时以 O_RDONLY 方式打开文件intfd::open(filename.c_str(),O_RDONLY);if(fd0){std::cerropen errorstd::endl;return2;}// 获取文件真实大小structstatst;::fstat(fd,st);char*mmap_addr(char*)::mmap(nullptr,st.st_size,PROT_READ,MAP_SHARED,fd,0);if(mmap_addrMAP_FAILED){perror(mmap);return3;}std::cout.write(mmap_addr,static_caststd::streamsize(st.st_size));std::coutstd::endl;// 取消映射::munmap(mmap_addr,st.st_size);// 关闭文件::close(fd);return0;}2. 极简模拟实现 mallocMalloc.c#includestdio.h#includestring.h#includestdlib.h#includesys/mman.h#includeunistd.h// 使用 mmap 分配内存void*my_malloc(size_tsize){void*ptrmmap(NULL,size,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);if(ptrMAP_FAILED){perror(mmap);exit(EXIT_FAILURE);}returnptr;}// 使用 munmap 释放内存voidmy_free(void*ptr,size_tsize){if(munmap(ptr,size)-1){perror(munmap);exit(EXIT_FAILURE);}}intmain(){size_tsize1024;// 分配 1KB 内存char*ptr(char*)my_malloc(size);// 使用分配的内存这里只是简单地打印指针值printf(Allocated memory at address: %p\n,ptr);// ... 在这里可以使用 ptr 指向的内存 ...memset(ptr,A,size);for(inti0;isize;i){printf(%c ,ptr[i]);sleep(1);}// 释放分配的内存my_free(ptr,size);return0;}GDB 调试输出(gdb) info proc mapping process 237158 Mapped address spaces: Start Addr End Addr Size Offset objfile 0x555555554000 0x555555555000 0x1000 0x0 /home/whb/code/test/test/malloc/a.out 0x555555555000 0x555555556000 0x1000 0x1000 /home/whb/code/test/test/malloc/a.out 0x555555556000 0x555555557000 0x1000 0x2000 /home/whb/code/test/test/malloc/a.out 0x555555557000 0x555555558000 0x1000 0x2000 /home/whb/code/test/test/malloc/a.out 0x555555558000 0x555555559000 0x1000 0x3000 /home/whb/code/test/test/malloc/a.out 0x7ffff7dcd000 0x7ffff7def000 0x22000 0x0 /usr/lib/x86_64-linux-gnu/libc-2.31.so 0x7ffff7def000 0x7ffff7f67000 0x178000 0x22000 /usr/lib/x86_64-linux-gnu/libc-2.31.so 0x7ffff7f67000 0x7ffff7fb5000 0x4e000 0x19a000 /usr/lib/x86_64-linux-gnu/libc-2.31.so 0x7ffff7fb5000 0x7ffff7fb9000 0x4000 0x1e7000 /usr/lib/x86_64-linux-gnu/libc-2.31.so 0x7ffff7fb9000 0x7ffff7fbb000 0x2000 0x1eb000 /usr/lib/x86_64-linux-gnu/libc-2.31.so 0x7ffff7fbb000 0x7ffff7fc1000 0x6000 0x0 0x7ffff7fcb000 0x7ffff7fce000 0x3000 0x0 [vvar] 0x7ffff7fce000 0x7ffff7fcf000 0x1000 0x0 [vdso] 0x7ffff7fcf000 0x7ffff7fd0000 0x1000 0x0 /usr/lib/x86_64-linux-gnu/ld-2.31.so 0x7ffff7fd0000 0x7ffff7ff3000 0x23000 0x1000 /usr/lib/x86_64-linux-gnu/ld-2.31.so 0x7ffff7ff3000 0x7ffff7ffb000 0x8000 0x24000 /usr/lib/x86_64-linux-gnu/ld-2.31.so 0x7ffff7ffc000 0x7ffff7ffd000 0x1000 0x2c000 /usr/lib/x86_64-linux-gnu/ld-2.31.so 0x7ffff7ffd000 0x7ffff7ffe000 0x1000 0x2d000 /usr/lib/x86_64-linux-gnu/ld-2.31.so 0x7ffff7ffe000 0x7ffff7fff000 0x1000 0x0 0x7ffffffde000 0x7ffffffff000 0x21000 0x0 [stack] 0xffffffffff600000 0xffffffffff601000 0x1000 0x0 [vsyscall] (gdb) n 31 printf(Allocated memory at address: %p\n, ptr); (gdb) info proc mapping process 237158 Mapped address spaces: Start Addr End Addr Size Offset objfile 0x555555554000 0x555555555000 0x1000 0x0 /home/whb/code/test/test/malloc/a.out 0x555555555000 0x555555556000 0x1000 0x1000 /home/whb/code/test/test/malloc/a.out 0x555555556000 0x555555557000 0x1000 0x2000 /home/whb/code/test/test/malloc/a.out 0x555555557000 0x555555558000 0x1000 0x2000 /home/whb/code/test/test/malloc/a.out 0x555555558000 0x555555559000 0x1000 0x3000 /home/whb/code/test/test/malloc/a.out 0x7ffff7dcd000 0x7ffff7def000 0x22000 0x0 /usr/lib/x86_64-linux-gnu/libc-2.31.so 0x7ffff7def000 0x7ffff7f67000 0x178000 0x22000 /usr/lib/x86_64-linux-gnu/libc-2.31.so 0x7ffff7f67000 0x7ffff7fb5000 0x4e000 0x19a000 /usr/lib/x86_64-linux-gnu/libc-2.31.so 0x7ffff7fb5000 0x7ffff7fb9000 0x4000 0x1e7000 /usr/lib/x86_64-linux-gnu/libc-2.31.so 0x7ffff7fb9000 0x7ffff7fbb000 0x2000 0x1eb000 /usr/lib/x86_64-linux-gnu/libc-2.31.so 0x7ffff7fbb000 0x7ffff7fc1000 0x6000 0x0 0x7ffff7fcb000 0x7ffff7fce000 0x3000 0x0 [vvar] 0x7ffff7fce000 0x7ffff7fcf000 0x1000 0x0 [vdso] 0x7ffff7fcf000 0x7ffff7fd0000 0x1000 0x0 /usr/lib/x86_64-linux-gnu/ld-2.31.so 0x7ffff7fd0000 0x7ffff7ff3000 0x23000 0x1000 /usr/lib/x86_64-linux-gnu/ld-2.31.so 0x7ffff7ff3000 0x7ffff7ffb000 0x8000 0x24000 /usr/lib/x86_64-linux-gnu/ld-2.31.so 0x7ffff7ffb000 0x7ffff7ffc000 0x1000 0x0 # 映射起始地址按页对齐本例映射区占 4KB 0x7ffff7ffc000 0x7ffff7ffd000 0x1000 0x2c000 /usr/lib/x86_64-linux-gnu/ld-2.31.so 0x7ffff7ffd000 0x7ffff7ffe000 0x1000 0x2d000 /usr/lib/x86_64-linux-gnu/ld-2.31.so 0x7ffff7ffe000 0x7ffff7fff000 0x1000 0x0 0x7ffffffde000 0x7ffffffff000 0x21000 0x0 [stack] 0xffffffffff600000 0xffffffffff601000 0x1000 0x0 [vsyscall] (gdb) n Allocated memory at address: 0x7ffff7ffb000映射结果第二次查看映射时新增区间为0x7ffff7ffb000到0x7ffff7ffc000大小为0x10004KB程序打印的返回地址0x7ffff7ffb000与该区间的起始地址一致。[!NOTE] 注意MAP_ANONYMOUS是一个用于mmap系统调用的标志它指定要创建一个匿名内存映射。当使用MAP_ANONYMOUS标志时mmap会分配一段不与任何文件相关联的内存区域即这段内存没有文件作为其后端存储。这种类型的映射通常用于需要分配私有内存的场景例如进程内部的内存分配。