当前位置: 首页 > 科技观察

原来mmap这么简单

时间:2023-03-19 02:15:28 科技观察

本文转载自微信公众号《Linux内核那些事儿》,作者songsong001。转载本文请联系Linux内核那些事儿公众号。1、传统的文件读写一般来说,修改一个文件的内容需要以下三个步骤:将文件的内容读入内存。修改内存内容。将内存中的数据写入文件。流程如图1所示:如果用代码实现上述流程,代码如下:read(fd,buf,1024);//读取文件内容到buf...//修改contentofbufwrite(fd,buf,1024);//将buf的内容写入文件从图1可以看出,页面缓存(pagecache)是读写文件时的中间层,内核使用页面缓存与文件的数据块相关联。所以当一个应用程序读写文件时,它实际上是在页面缓存上操作的。2.使用mmap读写文件从传统的文件读写流程中,我们可以发现有一个地方可以优化:如果pagecache可以直接在用户空间读写,那么数据在pagecache中可以复制到用户空间而不需要buffer过程。那么,是否有这样一种技术可以实现上述方法呢?答案是肯定的,就是mmap。mmap系统调用可以将用户空间的虚拟内存地址映射(绑定)到文件中,对映射后的虚拟内存地址的读写操作与对文件的读写操作相同。原理如图2所示:前面我们提到,读写文件需要经过pagecache,所以mmap映射的是文件的pagecache,而不是磁盘上的文件本身。由于mmap映射的是文件的pagecache,所以涉及到同步的问题,即pagecache什么时候会把数据同步到磁盘。Linux内核不会主动将mmap映射的pagecache同步到磁盘,而是需要用户主动触发。将mmap映射内存同步到磁盘有4种机会:调用msync函数自动同步数据(主动)。调用munmap函数取消映射文件时(活动)。当进程退出时(被动)。当系统关闭时(被动)。3.mmap的使用下面介绍一下mmap的使用方法。mmap函数的原型如下:void*mmap(void*addr,size_tlength,intprot,intflags,intfd,off_toffset);下面介绍mmap函数各参数的作用:addr:指定映射的虚拟内存地址,可以设置为NULL让Linux内核自动选择合适的虚拟内存地址。length:地图的长度。prot:映射内存的保护模式,可选值如下:PROT_EXEC:可以执行。PROT_READ:可读。PROT_WRITE:可写。PROT_NONE:不可访问。flags:指定映射的类型。常用的可选值如下:MAP_FIXED:使用指定的起始虚拟内存地址进行映射。MAP_SHARED:与映射到该文件的所有其他进程共享映射空间(可以实现共享内存)。MAP_PRIVATE:创建一个写时复制(CopyonWrite)私有映射空间。MAP_LOCKED:锁定映射区域中的页面,防止页面被换出内存。...fd:要映射的文件句柄。offset:文件偏移量(从文件开始映射的位置)。介绍完mmap函数的原型,我们现在通过一个简单的例子介绍如何使用mmap:intfd=open(filepath,O_RDWR,0644);//打开文件void*addr=mmap(NULL,8192,PROT_WRITE,MAP_SHARED,fd,4096);//映射文件在上面的例子中,我们首先通过open函数以可读可写的方式打开文件,然后通过mmap函数映射文件。映射方法如下:addr参数设置为NULL,意思是让操作系统自动选择合适的虚拟内存地址进行映射。length参数设置为8192表示映射区域为2个内存页的大小(一个内存页的大小为4KB)。prot参数设置为PROT_WRITE表示映射的内存区域可读可写。flags参数设置为MAP_SHARED以指示共享映射区域。fd参数设置打开的文件句柄。offset参数设置为4096,从文件的4096开始映射。mmap函数返回映射后的内存地址,通过它我们可以读写文件。我们通过图3展示了上面例子在内核中的结构:4.总结本文主要介绍mmap的原理和使用方法。通过这篇文章,我们可以知道使用mmap读写文件可以减少内存拷贝的次数,并且可以减少系统调用的次数,从而提高读写文件的效率。由于内核不会主动同步mmap映射的内存区中的数据,所以在一些特殊场景下(比如掉电)可能会出现数据丢失的情况。为了避免数据丢失,在使用mmap时,可以在适当的时候主动调用msync函数同步映射内存区域中的数据。