关于RocketMQ的一些linux基础知识

关于零拷贝和文件预热的linux基础知识

Posted by Byolio on May 10, 2025

本文主要介绍linux的零拷贝和文件预热的基础知识

零拷贝

将一个文件发送到网络上时, 一般会经历以下步骤:

  1. 进程切换为内核态
  2. 从磁盘读取文件内容到内核缓冲区
  3. 将内核缓冲区的内容复制到用户缓冲区
  4. 将用户缓冲区的内容复制到网络缓冲区
  5. 将网络缓冲区的内容发送到网络上
  6. 进程切换为用户态, 释放资源

总体来看, 这个过程是非常消耗时间和系统资源的, 因为需要进行多次的复制, 也就是大量的缓存I/O操作, 在有些场景下,拷贝的次数确实多了,就出现了零拷贝。

sendFile实现零拷贝

步骤为:

  1. 进程切换为内核态
  2. 从磁盘读取文件内容到内核缓冲区
  3. 将内核缓冲区的内容复制到网络缓冲区
  4. 将网络缓冲区的内容发送到网络上
  5. 进程切换为用户态, 释放资源 可以看到, 这个过程中, 并没有进行用户缓冲区的复制, 而是直接将内核缓冲区的内容复制到网络缓冲区, 然后将网络缓冲区的内容发送到网络上, 这样就减少了来回两次复制, 提高了效率。

sendFile + gather实现零拷贝

步骤为:

  1. 进程切换为内核态
  2. 从磁盘读取文件内容到内核缓冲区
  3. 将内核缓冲区的内容发送到网络上
  4. 进程切换为用户态, 释放资源 可以看到, 这个过程中, 数据不需要再拷贝到scoket buffer(网络缓冲区), 而是直接将内核缓冲区的内容发送到网络上, 这样就减少了三次复制, 提高了效率。

零拷贝适合的场景

不需要对磁盘文件内容做解析, 修改, 备份操作的场景适合使用零拷贝, 因为都不拷贝到用户空间,无法解析其内容。

文件预热

文件预热是指通过提前将文件加载到内存中,避免每次访问文件时都需要从磁盘读取。这样可以显著提高文件的读取性能,尤其是在频繁访问的文件或高并发访问的场景中,能够有效地减少I/O延迟。

mmap + 文件预热

RocketMQ在文件预热时, mmap将磁盘文件对应的内核读缓冲区和用户的缓存映射成一个地址, 用户空间操作这块缓存的数据能直接作用到内核的读缓存区, 本质上是一块物理内存, 减少了内核空间到用户空间的拷贝,用户空间可以直接操作这块数据。
但物理内存中实际并没有分配资源, 只有当进程访问到,发现内存中没数据才会进行缺页中断,分配资源, 而这个缺页中断是系统调用,涉及上下文切换,比较耗费时间,对 RocketMQ 消息的写入动作来说,会产生性能波动。
因此RocketMQ采用了文件预热,即预先将当前映射的文件,每一页遍历过去, 写入一个0字节, 触发缺页中断的系统调用,预发分配好内存, 并将进程使用的部分或者全部的地址空间锁定在物理内存中,防止其被交换到swap空间。

总结

零拷贝和文件预热是提高文件读取性能的重要手段, 可以减少I/O延迟, 提高文件读取效率。