一文带你搞懂 mmap 技术

Posted by 陈树义 on 2023-03-06

大家好,我是树哥。

相信大家在面试的时候会被问到:Kafka 为啥会这么快?其中我们都会说是因为 zero-copy 的缘故,但 zero-copy 中其实有很多种实现方式,例如:mmap + write、sendfile 等等。这其中的 mmap 到底是咋回事呢?今天我们就来讲讲 mmap 技术。

mmap 是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。

那么 mmap 技术有啥用呢?

答案是:减少数据文件复制,提高效率。

想一下我们读取文件,然后将其通过网卡发送出去的流程,其整体流程如下所示:

图片来自知乎博主艾小仙

上述数据读取和发送过程发生了 4 次用户态和内核态的上下文切换和 4 次拷贝,具体流程如下:

  1. 用户进程通过 read() 方法向操作系统发起调用,此时进程从用户态转向内核态。
  2. DMA 控制器把数据从硬盘中拷贝到读缓冲区。
  3. CPU 把读缓冲区数据拷贝到应用缓冲区,上下文从内核态转为用户态,read() 返回
  4. 用户进程通过 write() 方法发起调用,上下文从用户态转为内核态
  5. CPU 将应用缓冲区中数据拷贝到 socket 缓冲区
  6. DMA 控制器把数据从 socket 缓冲区拷贝到网卡,上下文从内核态切换回用户态,write() 返回

可以看到数据要从内核空间的读缓冲区读取到用户空间的用户缓冲区,再拷贝到内核空间的 socket 缓冲区,这个过程其实是很浪费时间的。而 mmap 技术的出现,就是为了提高这个效率。 使用 mmap 技术进行文件读写操作的过程如下图所示:

图片来自知乎博主艾小仙

整个过程发生了 4 次用户态和内核态的上下文切换和 3 次拷贝,具体流程如下:

  1. 用户进程通过 mmap() 方法向操作系统发起调用,上下文从用户态转向内核态
  2. DMA 控制器把数据从硬盘中拷贝到读缓冲区
  3. 上下文从内核态转为用户态,mmap 调用返回
  4. 用户进程通过 write() 方法发起调用,上下文从用户态转为内核态
  5. CPU 将读缓冲区中数据拷贝到 socket 缓冲区
  6. DMA 控制器把数据从 socket 缓冲区拷贝到网卡,上下文从内核态切换回用户态,write() 返回

使用了 mmap 方式的文件读写节省了一次 CPU 拷贝,我们不需要再次从内核空间拷贝到用户空间,然后再从用户空间拷贝到内核空间。

此时我们会想:那这到底是怎么实现的呢?

其实这一切的背后都是操作系统的功劳。操作系统在这背后为我们做好了所有的映射和回写工作。当我们对内存特定区域进行读写时,操作系统便会检测到这一操作,然后根据不同场景去做读磁盘或者写磁盘的操作。

看到这里,对于 mmap 的理解基本上足够我们应用开发人员使用了。

在学习这篇文章的时候,参考了几篇不错的文章,如果你感兴趣,可以在公众号后台回复「mmap文章」获取具体链接。

参考资料