Linux下应优先读取/sys/class/dmi/id/product_uuid获取稳定硬件UUID,需检查可读性、去除换行符、转大写并校验格式(36位含4短横线、非全零),虚拟机或精简内核可能不可用。

Linux系统下用/sys/class/dmi/id/product_uuid读取UUID
大多数现代Linux发行版(如Ubuntu、CentOS 7+)在内核启用DMI支持时,会将主板/系统UUID暴露在/sys/class/dmi/id/product_uuid路径下。这个值通常对应SMBIOS Type 1中的UUID字段,稳定且无需root权限读取(只要文件可读)。
注意:该文件内容末尾带换行符,且字母全大写、含短横线(如XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX),直接std::ifstream读取后需std::string::erase掉'
',否则后续校验或拼接可能出错。
- 检查文件是否存在且可读:
access("/sys/class/dmi/id/product_uuid", R_OK) == 0 - 避免用
system("cat ...")——启动shell开销大,且输出不可控 - 某些虚拟机(如QEMU默认配置)或精简内核可能禁用DMI,此时该路径不存在或返回空
Windows平台调用CoCreateGuid不是真主机UUID
很多资料误推CoCreateGuid作为“获取UUID”,但它生成的是随机GUID,和硬件无关。真正对应主机唯一标识的是Windows的Win32_ComputerSystemProduct WMI类中UUID字段,或注册表路径HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\MachineGuid(但后者是机器安装时生成的随机ID,不随硬件变化)。
实际工程中更可靠的做法是调用WMI:
立即学习“C++免费学习笔记(深入)”;
- 需链接
comctl32.lib和wbemuuid.lib - 必须初始化COM:
CoInitializeEx(nullptr, COINIT_MULTITHREADED) - 查询时指定命名空间
"root\cimv2",类名"Win32_ComputerSystemProduct",属性名"UUID" - 返回值可能是全大写带短横线,也可能是小写无短横线(取决于WMI实现),建议统一
std::transform标准化
跨平台方案别硬造,优先用libuuid(Linux)或CFUUID(macOS)
macOS没有/sys伪文件系统,但提供IOPlatformUUID属性,可通过I/O Kit获取;Linux上libuuid库的uuid_generate仍是随机生成,不解决“获取已有UUID”需求。
真正跨平台的务实做法是分条件编译,而不是试图用同一段代码覆盖所有系统:
- Linux:走
/sys/class/dmi/id/product_uuid(fallback到/sys/devices/virtual/dmi/id/product_uuid) - macOS:用
IORegistryEntryCreateCFProperty查kIOPlatformUUIDKey,返回CFStringRef再转std::string - Windows:走WMI(如前),不依赖注册表
MachineGuid——它不反映物理设备变更 - 所有路径读取失败时,明确返回空字符串或抛
std::runtime_error,不要fallback到uuid_generate
UUID格式校验比想象中容易翻车
拿到字符串后别直接当有效UUID用。常见错误包括:文件读取失败返回空串、WMI查询超时返回"NULL"、虚拟机返回"00000000-0000-0000-0000-000000000000"占位符。
基础校验逻辑应包含:
- 长度是否为36(含4个短横线)
- 第9/14/19/24位是否为
'-' - 其余32位是否全为十六进制字符(
0-9a-fA-F) - 排除全零UUID:
if (s == "00000000-0000-0000-0000-000000000000")
硬件UUID本身不保证全局唯一(尤其在克隆虚拟机场景),应用层若需强唯一性,得叠加其他因子(如MAC地址哈希、启动时间戳等),但这就超出“获取”范畴了。


















