内存映射文件
相关函数
mmap可以将文件映射在一段内存中,对该段内存的修改,将可以改变文件的内容。这一特性比较适合用于结构化文件的在位编辑工作。例如,以前修改配置程序,一个很low的办法是先读取文件的内容,如果是不需要修改的地方,就直接复制到另外一个文件中去,如果是需要修改的地方,就修改以后再写入到另一个文件中去,最后删除原来的文件,然后将新文件重命名为原来的文件名。
函数需要的头文件和原型定义为:
#include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
手册说明如下:
mmap() creates a new mapping in the virtual address space of the calling process.
The starting address for the new mapping is specified in addr. The length argument specifies the length of the mapping (which must be greater than 0). If addr is NULL, then the kernel chooses the address at which to create the mapping; this is the most portable method of creating a new mapping. If addr is not NULL, then the kernel takes it as a hint about where to place the mapping; on Linux, the mapping will be created at a nearby page boundary. The address of the new mapping is returned as the result of the call.
第一个参数是起始地址,通常为0(NULL),这样就由内核决定从哪里开始创建映射,这也便于程序的移植。如果不为NULL,内核就把该值作为放置映射的提示,应为这个函数是一直是按内存页面对齐的。
第二个参数是映射使用的长度,单位是字节;
第三个参数是指明映射时的内存保护方式,例如PROT_EXEC、PROT_READ、PROT_WRITE和PROT_NONE,分别表示可执行、可读、可写和不可访问,这个可以使用组合方式。还有很多其他的一些方式,可以查看man文档。
第四个是标志位,说明为: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.决定映射是否对其他进程可见等。
第五个参数是希望映射的文件描述符;
第六个参数是相对文件的偏移量,该值必须是SC_PAGESIZE的整数倍。
返回值: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.
就是说,成功了返回指向映射区域的地址,而失败了返回MAP_FAILED,并且errno会被设置。
与mmap相对于的是munmap,函数原型为:
int munmap(void *addr, size_t length);
第一个参数addr就是mmap的返回值,
第二个参数length就是映射使用的长度,单位是字节;
返回值:成功返回0,失败返回-1,并且errno会被设置。
最后一个相关的函数是msync,用于将数据同步到文件中。函数原型为:
int msync(void *addr, size_t length, int flags);
第一、二个参数和前面的munmap相同,第三个参数标志可以使用MS_ASYNC、MS_SYNC或者MS_INVALIDATE,其中前面两个比较常见,分别是异步或者同步方式。
测试
废话少说,直接上代码:
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> #include <stdlib.h> #include <string.h> int main() { int i,fd; char buff[256]; char* pc; fd=open("record.dat",O_RDWR|O_CREAT,00644); if(fd<0) { perror("open file failed"); exit(EXIT_FAILURE); } printf("PageSize=%ld\n",sysconf(_SC_PAGESIZE)); pc=(char*)mmap((void*)0x123456789abc,16,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); if(pc==MAP_FAILED) { perror("failed to map file"); exit(EXIT_FAILURE); } printf("pc=%p\n",pc); for(i=0;i<16;i++) *(pc+i)=0x33; msync((void*)pc,16,MS_ASYNC); munmap((void*)pc,16); close(fd); exit(EXIT_SUCCESS); }
运行效果如下:
虽然我们提供的地址是0x123456789abc,但是由于为了同内存对齐,返回的地址是0x123456789000,此外文件中的内容也被修改为:
最后,如果代码中将mmap函数的最后一个参数设置为非4096的整数倍的话,会报参数错误: