关于mmap的理解和使用
本篇文章介绍的是mmap的经典用法——文件映射mmap允许用户将文件或设备的内容文件内核缓冲区直接映射到用户的虚拟地址空间中用户通过虚拟地址直接访问文件缓冲区避免了read/write等系统调用带来的拷贝和性能开销mmap接口介绍#include sys/mman.h void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);addr我们可以指定一个地址去映射设备或文件内容但是这个参数可能会被OS忽略如果不合理的话。当它为NULL的时候表示由OS自己决定映射到什么地方length表示要映射到进程地址空间的字节数这个长度必须是页的整数倍内存以页为单位管理一般为4KB如果不足OS自动向上对齐多分配一点内存prot用于设置这个内存块的访问权限PROT_READ,PROT_WRITE,PROT_EXEC学习了虚拟地址空间我们知道权限是页表上的概念。flags指定了映射的类型。MAP_PRIVATE表示创建的是一个私有映射对映射区域的修改不会反映到磁盘上而且会有写时拷贝机制即进程间不能共享MAP_SHARED表示创建的是一个共享映射对映射区域的修改会反映到磁盘上进程间共享。MAP_ANONYMOUS表示创建的是匿名映射不会与磁盘上的实际文件相关联。fd表示要把那个文件映射到地址空间中。如果是匿名映射则为-1。offset表示要从文件的什么位置开始映射。返回值成功返回映射到的虚拟地址失败返回-1。#include sys/mman.h int munmap(void *addr, size_t length);这个函数用于解除虚拟地址addr与其指向的length字节的空间的映射关系。使用mmap共享映射一个文件并向文件写入内容#includesys/mman.h #include sys/types.h #include sys/stat.h #include fcntl.h #include unistd.h int main() { //首先创建并打开一个文件 int fd open(text.txt,O_RDWR | O_CREAT,0666); //调整这个文件的大小以便填充mmap的length参数从而正确映射(不能说文件大小只有1KB你映射了10KB) truncate(text.txt,4096); //调用mmap把文件映射到虚拟地址空间 char* ptr (char*)mmap(NULL,4096,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0); //直接使用地址对文件进行写入 for(char i a;iz;i) { ptr[i-a] i; } //取消映射 munmap((void*)ptr,4096); //关闭文件 close(fd); }私有映射一个文件并从文件读取内容#includesys/mman.h #include sys/types.h #include sys/stat.h #include fcntl.h #include unistd.h #includeiostream using namespace std; int main() { //首先创建并打开一个文件 int fd open(text.txt,O_RDWR); //获取这个文件的大小以便填充mmap的length参数从而正确映射(不能说文件大小只有1KB你映射了10KB) struct stat x; fstat(fd,x); int size x.st_size; //调用mmap把文件映射到虚拟地址空间 char* ptr (char*)mmap(NULL,size,PROT_READ | PROT_WRITE,MAP_PRIVATE,fd,0); //直接使用地址对文件进行读取 coutptrendl; //取消映射 munmap((void*)ptr,size); //关闭文件 close(fd); }关于flags取值易混淆的点虽然说设置了PROT_PRIVATE就不与磁盘交互并且进程私有这个控件但并不代表他就是MAP_ANONYMOUS匿名映射它还是与一个实际文件相关联的只不过不会修改这个文件因此flags为PROT_PRIVATE不能把fd设置为-1会报错。私有映射一个文件并写入内容#includesys/mman.h #include sys/types.h #include sys/stat.h #include fcntl.h #include unistd.h #includeiostream using namespace std; int main() { //首先创建并打开一个文件 int fd open(text.txt,O_RDWR); //获取这个文件的大小以便填充mmap的length参数从而正确映射(不能说文件大小只有1KB你映射了10KB) struct stat x; fstat(fd,x); int size x.st_size; //调用mmap把文件映射到虚拟地址空间 char* ptr (char*)mmap(NULL,size,PROT_READ | PROT_WRITE,MAP_PRIVATE,fd,0); //直接使用地址对文件进行修改 *ptr M; //取消映射 munmap((void*)ptr,size); //关闭文件 close(fd); }匿名映射#includesys/mman.h #include sys/types.h #include sys/stat.h #include fcntl.h #include unistd.h #includeiostream using namespace std; int main() { //调用mmap把文件映射到虚拟地址空间 char* ptr (char*)mmap(NULL,4096,PROT_READ | PROT_WRITE,MAP_PRIVATE | MAP_ANONYMOUS,-1,0); //在这就可以把这个空间当作malloc出来的动态空间使用啦 //取消映射 munmap((void*)ptr,4096); }匿名映射不需要关联文件因此比较简单你可以像使用malloc出来的动态空间一样使用它共享内存和动态库的加载我们使用的共享内存其实就是调用mmap以MAP_SHARED方式匿名映射出了一块空间这样两个进程可以看到同一份空间。而动态库的加载则是通过mmap把动态库文件的内容以MAP_SHARED方式映射到虚拟地址空间这样我们访问虚拟地址就相当于访问动态库。实际上vm_area_struct中包含一个struct_file指针当我们读取共享库中的内容而其又没有加载的时候就可以通过这个文件结构体指针找到文件内容并加载实现懒加载。同时这个结构体指针也记录着当前文件指针的偏移量。