第3章 命名空间概念

聊 NVMe 的命名空间(Namespace)之前,我先问你一个问题:
你有一块 2TB 的 SSD,想把它切成两块逻辑盘,一块装系统,一块存数据,怎么办?
传统硬盘靠分区。NVMe 呢?靠命名空间。说白了,它就是 NVMe 协议里对存储空间做逻辑划分的基本单元。

3.1 命名空间到底是个啥

命名空间是 NVM 子系统中一块可独立寻址的存储空间。你可以把它理解成一张白纸,控制器拿着 NSID(命名空间 ID)这张标签,就能找到对应的那张纸,读它、写它。

每个命名空间有自己的属性:

  • 大小:以 LB (Logical Block) 为单位,通常是 512B、4KB 等
  • 块大小:LBA Data Size (LBADS)
  • 保护信息:End-to-End Data Protection (E2E) 开关
  • 原子性边界:Atomic Write Unit Normal
个人习惯
我一般在项目初期就把命名空间规划好,比如系统盘 256GB,日志盘 128GB,数据盘 1TB。后头再改虽然也能改,但涉及数据迁移,挺折腾的。

3.2 命名空间 ID(NSID)

NSID 是一个 4 字节的标识符(uint32),范围从 1 到 0xFFFFFFFD。还有两个特殊值:

NSID 值 含义 说明
0x00000001 ~ 0xFFFFFFFD 有效命名空间 每个分配了存储空间的命名空间
0xFFFFFFFE 广播 NSID 用于 Identify 等命令查询所有命名空间
0xFFFFFFFF 全局命名空间 用于部分管理命令,表示作用于整个子系统
0x00000000 保留 / 无效 不可用于实际命名空间
避坑:NSID 冲突
我曾经在调试一台多控制器 NVMe 设备时,发现两个控制器同时创建了同一个 NSID 的命名空间。结果读数据时直接返回全零。检查了半天,原来是管理软件没做 NSID 分配互斥。所以多控制器场景下,NSID 冲突是很容易踩的坑。

3.3 命名空间管理命令

命名空间不是焊死在硬件里的,它是动态可管理的。NVMe 提供了两条管理命令:

  • Namespace Management:创建 / 删除命名空间
  • Namespace Attachment:将命名空间附加到某个控制器,或从控制器分离

这两条命令都走 Admin Submission Queue,由主机驱动发起。

创建命名空间(Create)

你需要告诉设备三个关键参数:

  • NSZE(Namespace Size):命名空间包含的 LBA 总数
  • NCAP(Namespace Capacity):最大可用 LBA 数(通常等于 NSZE)
  • LBADS(LBA Data Size):每个逻辑块的大小,如 0x0009 表示 512B

命令格式大概是这样(DWord 布局):

DWord 0:  CID=0x0D (Namespace Management) | OPC=0x0D | FUSE=0 | PSDT=0
DWord 1:  NSID = 0x00000000 (由设备分配)
DWord 10: NSZE = 0x00400000 (256 GB, 假设 4KB 块)
DWord 11: NCAP = 0x00400000
DWord 12: LBADS = 0x0009 (512B 格式)
...
Completion Entry 返回分配的 NSID

注意,创建命名空间不会自动把它附加到控制器上。创建完只是“有了这块空间”,但还不能用。

核心要点
创建命名空间只是分配逻辑地址空间,它不是格式化,也不是分区。数据在物理介质上仍然是未初始化状态。

删除命名空间(Delete)

删除命令正好反过来:传入 NSID,设备回收该命名空间占用的存储资源。删除后该 NSID 不再有效,原来对应的数据不可访问。

注意两点:

  • 删除前必须确保该命名空间已从所有控制器分离(Detach),否则命令会失败
  • 如果命名空间正在被 I/O 操作,删除会被拒绝。你需要先停掉 I/O 再 Delete
我曾经踩过的坑
在热插拔 NVMe 设备的测试中,我直接发 Delete 命令,忘了先 Detach。结果系统报错 Namespace Not Ready。从那以后,我每次都会先发 Detach,确认完成之后才发 Delete。

附加命名空间(Attach / Detach)

附加(Attach)是把命名空间“绑定”到某个控制器上,绑定之后该控制器才能对它做 I/O。分离(Detach)就是解绑。

命令参数很简单:

  • SEL:选择操作类型,0 为 Attach,1 为 Detach
  • NSID:命名的空间 ID
  • CTRLIST:目标控制器 ID 列表(最多支持 4 个控制器同时附加)

一个命名空间可以同时附加到多个控制器,这叫做「共享命名空间」(Shared Namespace)。但前提是控制器之间要做 I/O 协同,否则数据一致性会出问题。

我的建议
共享命名空间虽然灵活,但生产环境中能不用就别用。多控制器同时写同一个 LBA,如果没做分布式锁或原子写协议,数据很容易乱。我一般在单控场景下才用命名空间,多控场景更倾向于每个控制器独占一组命名空间。

3.4 命名空间生命周期(SVG)

下面这张图展示了命名空间从出生到销毁的完整流程。记住它,你就能把管理命令串起来了。

命名空间生命周期管理流程 命名空间生命周期管理 创建命名空间 Namespace Management 指定大小 附加到控制器 Namespace Attachment Attach I/O 操作 Read / Write 使用完毕后 分离命名空间 Namespace Attachment Detach 分离完成后 删除命名空间 Namespace Management 说明 创建 ≠ 可用 必须 Attach 删除前必须 Detach

流程其实不复杂:创建 → Attach → I/O → Detach → 删除。中间如果有多个控制器共享,就做多次 Attach/Detach。记住顺序,尤其是 Detach 和 Delete 不能颠倒。

一句话总结
命名空间就是 NVMe 的逻辑盘,NSID 就是盘符,Management 命令管生管死,Attachment 命令管用管停。

嗯,我记得刚学 NVMe 那会儿,我总把命名空间和分区搞混。实际上分区是文件系统层的概念,命名空间是介质层的逻辑抽象。你可以在一个命名空间里再分区,也可以一个命名空间直接格式化成一个文件系统。这俩不冲突。