可靠获取Linux设备UUID和文件系统类型的方法只有两种:一是遍历/dev/disk/by-uuid/符号链接反查设备,二是调用libblkid库;后者更全面,支持热插拔、LVM、LUKS等复杂场景,需链接-blkid并正确处理probe返回值与设备路径层级。

直接读取 /proc/partitions 和 /sys/block/*/device/vendor 不行,得走 /dev/disk/by-uuid/ 和 libblkid
Linux 下没有标准系统调用能直接返回某块设备的 UUID 或文件系统类型,靠解析 /proc/partitions 只能得到设备名和大小,不含 UUID;/sys 下路径也不暴露文件系统类型。真正可靠的方式只有两个:一是遍历 /dev/disk/by-uuid/ 符号链接反查目标设备,二是调用 libblkid ——后者是 lsblk、findmnt 等工具的底层依赖,支持热插拔、LVM、LUKS、btrfs subvolume 等复杂场景。
用 libblkid 扫描所有块设备并提取 UUID 与 TYPE
需要链接 -lblkid,头文件是 <blkid></blkid>。核心流程是:调用 blkid_new_probe_from_filename() 打开设备节点(如 /dev/sda1),再用 blkid_do_safeprobe() 触发探测,最后用 blkid_lookup_tag_value() 或直接访问 probe 结构体字段获取结果。
常见错误点:
- 没检查
blkid_do_safeprobe()返回值 —— 它可能返回BLKID_PROBE_NONE(无文件系统)或BLKID_PROBE_ERROR(权限不足/设备忙) - 直接传入
/dev/sda主设备而非分区(如/dev/sda1)—— 多数文件系统只存在于分区层,整盘探测常失败 - 忽略
libblkid的缓存行为:重复调用同一设备时建议复用blkid_probe对象,避免反复 open/close
简短示例:
立即学习“C++免费学习笔记(深入)”;
#include <blkid/blkid.h>
#include <stdio.h>
<p>void probe_device(const char<em> devpath) {
blkid_probe pr = blkid_new_probe_from_filename(devpath);
if (!pr) return;
if (blkid_do_safeprobe(pr) == 0) {
const char</em> uuid = blkid_probe_lookup_value(pr, "UUID", NULL, NULL);
const char* type = blkid_probe_lookup_value(pr, "TYPE", NULL, NULL);
if (uuid && type) {
printf("%s: UUID=%s TYPE=%s\n", devpath, uuid, type);
}
}
blkid_free_probe(pr);
}不依赖第三方库?用 std::filesystem + readlink() 绕过 libblkid
C++17 的 std::filesystem 本身不提供 UUID,但可以结合系统路径操作快速建立映射:遍历 /dev/disk/by-uuid/ 下所有符号链接,用 std::filesystem::read_symlink() 解出指向的真实设备路径,再用 std::filesystem::path::filename() 提取 UUID 字符串。这是纯标准库方案,无需链接额外库,但有明显限制:
- 只能拿到已挂载或曾挂载过的设备的 UUID(因为
by-uuid是 udev 动态生成的) - 无法得知文件系统类型,除非额外 fork
lsblk -no FSTYPE /dev/sda1或解析/proc/mounts - 注意路径拼接:
/dev/disk/by-uuid/下的链接目标可能是../../sda1,需用std::filesystem::weakly_canonical()归一化
关键代码片段:
for (const auto& entry : std::filesystem::directory_iterator("/dev/disk/by-uuid")) {
try {
auto target = std::filesystem::read_symlink(entry.path());
auto real_path = std::filesystem::weakly_canonical(target);
std::string uuid = entry.path().filename().string();
printf("UUID=%s → %s\n", uuid.c_str(), real_path.string().c_str());
} catch (...) { /* 权限拒绝或损坏链接,跳过 */ }
}为什么不用 system("blkid -o export")?性能与可靠性双输
看似简单,实际问题密集:
- 每次调用都 fork 新进程,启动成本高,扫描 20 个分区就触发 20 次进程创建
-
blkid默认会缓存结果到/run/blkid/blkid.tab,多进程并发时可能因锁竞争卡住或读到陈旧数据 - 输出格式依赖 locale 和版本:
blkid -o export在某些发行版中字段顺序不固定,解析易出错;而libblkidAPI 是稳定的二进制接口 - 无法控制超时或中断 —— 如果某个设备正在被 LUKS 解密或 btrfs scrub,
blkid可能阻塞数秒甚至更久
真要 shell fallback,至少用 blkid -c /dev/null -o value -s UUID -s TYPE /dev/sda1 2>/dev/null 关闭缓存并限定字段,但依然不如原生库可控。
最麻烦的其实是设备命名动态性:/dev/sda 可能在重启后变成 /dev/nvme0n1,UUID 才是唯一稳定标识。别在配置里硬写设备名,也别以为 std::filesystem::canonical() 能帮你绕过这个问题 —— 它解决不了跨机器、跨内核版本的设备路径漂移。



















