第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 |
保留 / 无效 | 不可用于实际命名空间 |
我曾经在调试一台多控制器 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)
下面这张图展示了命名空间从出生到销毁的完整流程。记住它,你就能把管理命令串起来了。
流程其实不复杂:创建 → Attach → I/O → Detach → 删除。中间如果有多个控制器共享,就做多次 Attach/Detach。记住顺序,尤其是 Detach 和 Delete 不能颠倒。
命名空间就是 NVMe 的逻辑盘,NSID 就是盘符,Management 命令管生管死,Attachment 命令管用管停。
嗯,我记得刚学 NVMe 那会儿,我总把命名空间和分区搞混。实际上分区是文件系统层的概念,命名空间是介质层的逻辑抽象。你可以在一个命名空间里再分区,也可以一个命名空间直接格式化成一个文件系统。这俩不冲突。