一、问题背景
Linux下,针对文件读写操作,一般有三个步骤:
1)把文件内容读入到内存中;调用read(系统调用),从内核态读取文件内容到虚拟内存;
2)修改内存中的内容;在用户空间修改内存中的信息;
3)把内存的数据写入到文件中;调用write(系统调用),从用户态将修改后的内容写入到文件;
过程如下图所示:(本文只讨论mmap,关于page cache,请查看:https://www.cnblogs.com/ronnieyuan/p/12377059.html)
显然一次文件的修改操作,我们对文件内容拷贝了两次;
如何优化上述的文件读写操作?如果可以直接在用户空间读写page cache,那么就不需要将文件数据拷贝到用户空间缓存区这个动作了,mmap即可完成;
使用mmap系统调用可以将用户空间的虚拟内存地址与文件进行映射绑定,对映射或的虚拟内存地址进行读写操作,就如同对文件进行读写操作一样。原理图如下所示:
通过上图可以看出,读写文件都需要经过页缓存,所以mmap映射的正是文件的页缓存,而非磁盘中的文件本身。由于mmap映射的是文件的页缓存,所以就涉及到同步的问题,即页缓存会在什么时候把数据同步到磁盘。Linux内核并不会主动把mmap映射的页缓存同步到磁盘,而是需要用户主动触发。同步mmap映射的内存到磁盘有4个时机:1)调用 msync 函数主动进行数据同步(主动);2)调用 munmap 函数对文件进行解除映射关系时(主动);3)进程退出时(被动);4)系统关机时(被动)。备注:下面示例实测不调用munmap也会写回到文件,保险起见还是主动同步数据到文件,详细的还未研究。
二、mmap示例
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>int main(void)
{int fd;fd = open("in.txt", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);if (-1 == fd){return -1;}void *result;result = mmap(0, 1024, \PROT_READ|PROT_WRITE, \MAP_SHARED, \fd, 0);if (result == (void *)-1) {printf("mmap failed: %s\n", strerror(errno));return -1;}close(fd);//映射完可以立即关闭fd,不应该映射及修改strncpy(result, "z", 1);munmap(0, 1024);return 0;
}
三、理论理解
彻底理解mmap()
Page Cache(页缓存)
mmap 内存映射详解
Linux mmap完全剖析