nvme_get_log_page 调用失败主因是权限不足、log_id错误、缓冲区长度不匹配及平台限制;需以O_RDWR打开设备,校验log_id(如SMART为0x02),严格按规范设len与lsp,并优先用nvme-cli验证。

nvme_get_log_page 函数调用失败:权限与设备打开方式不对
直接用 open("/dev/nvme0", O_RDONLY) 打开设备后调用 nvme_get_log_page,大概率返回 -1 且 errno = EPERM。Linux 内核从 5.10+ 开始默认禁止非特权进程通过 ioctl 访问 NVMe 日志页(尤其关键页如 SMART/Health、Error Log),除非显式启用 CONFIG_NVME_MULTIPATH 或绕过安全策略。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 改用
open("/dev/nvme0", O_RDWR | O_CLOEXEC)—— 即使只读日志也需写权限,因为底层 ioctl(如NVME_IOCTL_ADMIN_CMD)要求设备 fd 可写 - 确保进程有
CAP_SYS_ADMIN能力(开发调试可临时用sudo ./a.out;生产环境应 setcap 或用 systemd capabilityBoundingSet) - 检查内核是否禁用了用户空间日志访问:
cat /sys/module/nvme/parameters/ignore_dev_subnqn若为 Y,某些日志页会被静默拒绝(罕见但存在)
log_id 参数填错导致返回 EINVAL 或空数据
传给 nvme_get_log_page 的 log_id 必须是标准 NVMe 规范定义的值,比如 SMART/Health 是 0x02,错误日志是 0x01,而 0x0f(Firmware Slot Info)在部分老固件上不支持。填错或固件不响应时,函数可能返回 0(成功假象)但缓冲区全零,或直接 errno = EINVAL。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 优先查 NVMe 1.4c 规范文档 Table 279 确认 log_id —— 别凭记忆写
2,要写0x02 - 先用
nvme smart-log /dev/nvme0命令验证设备是否响应对应日志页,再写代码 - 对
log_id = 0x0e(Device Self-test Log)等可选页,必须检查nvme_id_ctrl.lpa & NVME_CTRL_LPA_DEV_SELF_TEST位,否则盲目调用会失败
缓冲区大小和 LSP 字段设置不当引发截断或崩溃
nvme_get_log_page 的 len 参数不是“最多读多少”,而是“必须精确匹配该日志页的预期长度”。例如 SMART 日志固定 512 字节,若传 1024,部分驱动会拒绝;若传 256,则只读前半截,critical_warning 字段可能被切掉。更隐蔽的是 lsp(Log Specific Parameter)字段——它控制子页索引,比如获取第 2 个 firmware slot 要设 lsp = 1,但多数日志页应设 lsp = 0,设错会导致返回乱码。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- SMART/Health 日志:固定用
len = 512,lsp = 0 - Error Log:长度由
nvme_id_ctrl.elpe字段决定(最大条目数 × 64),需先读 Identify Controller 数据,再分配缓冲区 - 务必检查函数返回值是否等于
len,不等即说明读取异常(如固件中途断连或 DMA 失败)
跨平台兼容性:Linux ioctl vs Windows WMI vs macOS IOKit
Linux 下靠 nvme_get_log_page(本质是 ioctl(fd, NVME_IOCTL_ADMIN_CMD, &args));Windows 没有等价的公开 API,得走 WMI 类 MSFT_SMARTData 或 NVMe vendor 提供的 DLL;macOS 从 Ventura 开始才通过 IORegistryEntryCreateCFProperty 暴露有限 SMART 字段,不支持原始日志页读取。这意味着同一份 C++ 代码无法跨平台直接复用。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- Linux 项目直接依赖 libnvme(
libnvme-dev包)或手写 ioctl 封装,别自己解析 /proc/diskstats - Windows 上放弃 ioctl 思路,改用
WmiGetSingleInstance查询MSFT_PhysicalDisk,再结合 vendor 工具(如 Samsung Magician SDK)补全 - 所有平台都应 fallback 到
smartctl -a /dev/nvme0n1的文本解析——虽然慢,但最稳,尤其当内核模块被禁用或权限收紧时
真正麻烦的不是调用函数,而是每次换一块盘都要确认它的固件是否把 log page 实现全了。有些国产控制器甚至把 SMART 日志放在自定义 log_id=0x80 下,连 nvme-cli 都不认——这时候只能抓 PCIe trace 或硬啃 vendor datasheet。



















