4. PRP与SGL数据传递机制

各位同学,今天我们来聊聊NVMe协议里一个非常核心的话题——数据怎么在主机和SSD之间传递。说白了,就是PRP和SGL这两种机制。

我记得刚接触NVMe时,看到PRP和SGL这两个缩写,第一反应是「又来了,又是两种协议打架」。但实际用下来发现,它们各有各的用武之地。今天我就把这两兄弟掰开揉碎了讲清楚。

4.1 PRP原理与构造

PRP,全称Physical Region Page,物理区域页。名字听着挺唬人,其实原理很简单。

PRP的核心思想:把物理内存划分成固定大小的页(通常是4KB),然后用一个链表把这些页串起来。NVMe控制器要读写数据时,就顺着这个链表找到每一页的物理地址。

来看一个PRP条目的结构:

PRP Entry (64位):
+-------------------------------+
| 物理地址 (63:2) | 保留位 (1:0) |
+-------------------------------+

每个PRP条目指向一个物理页的起始地址。注意,地址的低2位是保留的,因为页大小至少是4字节对齐的。

PRP列表的构造方式:

  • 如果数据量小于等于一个页(4KB),只需要一个PRP条目
  • 如果数据量大于一个页但小于等于两个页,需要两个PRP条目
  • 如果数据量更大,就需要PRP列表了——第一个PRP条目指向数据起始页,第二个PRP条目指向一个PRP列表,这个列表里存放着后续所有页的地址

关键点:PRP列表本身也必须在物理内存中连续存放。这一点我在项目中踩过坑,后面会讲。

我画了一张图,帮大家理解PRP的链式结构:

PRP链式结构示意图 SQ Entry PRP1指针 | PRP2指针 数据页1 (4KB) 物理地址 A PRP列表 条目1 → 数据页2 条目2 → 数据页3 ... 数据页2 (4KB) 物理地址 B 数据页3 (4KB) 物理地址 C PRP1指向第一个数据页,PRP2指向PRP列表 PRP列表中的每个条目指向后续的数据页 ⚠ 注意 PRP列表本身必须在物理内存中连续 数据页可以是不连续的物理页

我的经验:PRP适合小数据量的场景。比如4KB的随机读写,一个PRP条目就搞定了,开销很小。但如果你要传1MB的数据,PRP列表就会变得很长,管理起来有点麻烦。

4.2 SGL原理与构造

SGL,Scatter Gather List,分散聚合列表。这玩意儿比PRP灵活多了。

SGL的核心思想:用描述符(Descriptor)来描述每一段数据的位置和长度。每个描述符可以指向任意大小的内存块,不限于4KB页。

SGL描述符的结构:

SGL Descriptor (16字节):
+-------------------------------+-------------------------------+
| 物理地址 (63:0)               | 长度 (31:0) | 类型 (15:0)   |
+-------------------------------+-------------------------------+

每个描述符16字节,比PRP条目大一倍。但它能描述任意长度的数据段,灵活性大大提升。

SGL的几种类型:

  • Data Block描述符:指向一块连续的数据缓冲区
  • Segment描述符:指向另一个SGL段(用于链式扩展)
  • Last Segment描述符:指向最后一个SGL段
  • Keyed Data Block描述符:带密钥的数据块(用于加密场景)

我画了SGL的结构图:

SGL链式结构示意图 SQ Entry SGL指针 SGL段1 Desc1: 数据块A (2KB) Desc2: 数据块B (1KB) 数据块A (2KB) 数据块B (1KB) Desc3: Segment → SGL段2 SGL段2 Desc4: 数据块C (3KB) Desc5: Last Segment 数据块C (3KB) SGL通过描述符链式扩展,每个描述符可以指向不同大小的数据块 Last Segment描述符标记SGL的结束 优势:数据块大小灵活,不需要物理连续,适合大数据量传输

我个人的习惯:在做大数据量传输时,我倾向于用SGL。比如视频流写入,数据块大小不一,用SGL一个描述符就能搞定,不用像PRP那样拆成很多个4KB页。

4.3 PRP vs SGL的选择策略

好了,两个机制都讲完了。那问题来了——什么时候用PRP,什么时候用SGL?

我直接给结论:

对比维度 PRP SGL
条目大小 8字节 16字节
数据块大小 固定4KB页 任意大小
列表连续性 PRP列表必须物理连续 SGL段可以分散
适合场景 小数据量、固定大小传输 大数据量、变长传输
硬件复杂度 低 高
NVMe协议支持 所有版本都支持 NVMe 1.2+ 支持

我的选择策略:

  • 4KB及以下的小IO:用PRP。一个条目搞定,简单高效。
  • 4KB到1MB的中等IO:看情况。如果数据块大小是4KB的整数倍,PRP也还行。否则用SGL。
  • 1MB以上的大IO:无脑选SGL。PRP列表会变得很长,管理起来很痛苦。
  • 变长数据块:必须用SGL。PRP搞不定。

避坑指南:我曾经在一个项目中,用PRP传一个2.5MB的数据块。结果PRP列表有640个条目,光构造这个列表就消耗了大量CPU时间。后来换成SGL,一个段就搞定了,性能提升明显。

另外要注意,NVMe 1.2之前的设备不支持SGL。如果你要兼容老设备,只能用PRP。但现在的SSD基本都支持SGL了,所以新项目我建议直接用SGL。

嗯,PRP和SGL就讲到这里。记住一句话:小数据用PRP,大数据用SGL,变长数据必须SGL。这个原则在项目中屡试不爽。

总结:PRP和SGL都是NVMe协议中主机与SSD之间传递数据的手段。PRP简单高效但灵活性差,SGL灵活强大但开销稍大。选择哪个,取决于你的数据特征和硬件支持情况。

可可精品资料库