RocketMQ消息存储(一) - mmap零拷贝(前置篇)
从本篇开始研究Broker 对文件的存储, 这些文件分为三类 (CommitLog , ConsumeQueue , IndexFile) 。
而在分析文件存储的源码之前, 我们要先了解一个重要的前置知识: 零拷贝IO 技术.
下面是针对不同的开源MQ总结的底层使用的IO方式:
MQ | read/write |
---|---|
RocketMQ | mmap/mmap (默认,可通过修改配置,配置成FileChannel,原因是作者想避免PageCache的锁竞争,通过两层架构实现读写分离) |
Kafka | record记录基于sendfile/sendfile index基于mmap/mmap |
ActiveMQ | RandomAccessFile/RandomAccessFile |
1. 基本概念
我们需要先弄清楚, 物理内存与虚拟内存之间的关系, 以及虚拟内存下 用户空间与内核空间的关系, 弄清楚这些概念后对我们去理解 零拷贝 就非常的简单。
下面时我从 网上荡下来的一张图片:
这个图片我认为 非常清晰的能够体现出 上述我说的关系。
-
物理内存: 就是实际上计算机真实的内存大小。
-
虚拟内存:为了解决多进程出现同时操作同一物理内存地址的现象,于是就抽象虚拟内存。
-
用户空间/内核空间: 为了系统安全性考虑,用户进程不能直接操作底层内核,因此就把 虚拟内存 又划分出了 用户空间 、内核空间两部分。
-
映射:
上面这一张图是 虚拟内存 映射到 物理内存。
多进程的虚拟内存中的内核空间是共享的,都映射在物理内存的系统内核空间上。 而虚拟内存中的用户空间 则被映射到了不同的物理内存区域。
2. mmap内存映射原理分析
以上我们清楚了 物理内存与虚拟内存之间的映射关系, 它们之间实际上是通过 页表和MMU的方式来实现 地址之间的转换映射。
而本小节所说的 mmap内存映射 中的 映射 主要指的是 硬盘上文件的位置 与 **虚拟内存地址空间 **的 一 一对应,这种关系是逻辑上的概念。
注意: 在mmap的内存映射过程中, 并没有实际的数据拷贝,文件也并没有载入物理内存,只是逻辑上与虚拟内存形成了映射关系。
过程1: 实际上就是mmap内存映射, 只是建立了 文件 与 虚拟内存之间 逻辑上的映射关系。
假设 这时候 需要 读取文件上的一段数据, 因为 虚拟内存 与 文件的位置 已经建立了逻辑上的映射关系, 一次直接通过 虚拟内存来从物理内存上获取数据。
过程2:通过虚拟内存地址与物理内存地址的转换, 能够访问到 目标那块地址 上有无数据。 若没有数据,则说明文件没有加载到物理内存中,因此会产生缺页中断。
过程3:产生了缺页中断,因此就需要物理内存的内核去完成数据拷贝。 也就是将文件的数据 加载到 对应的物理内存地址中。
过程4:若在加载数据到物理内存的过程中, 物理内存的空间不够用, 因此就会产生 swap 交换, 从物理内存上腾出一部分空间给文件数据使用。