Linux平台使用C++如何正确检测系统块设备?
问题原因
靠文件名前缀匹配/dev下的块设备是非常不可靠的做法:块设备的命名完全由对应内核驱动决定,除了你提到的会漏掉loop、nbd设备之外,常见的mmcblk(EMMC/SD卡)、zram(压缩内存盘)、dm-(LVM、LUKS等Device Mapper设备)、md(软RAID)、sr*(光驱)、ram*(内存盘)等块设备全都会漏,而且以后内核加了新的块设备驱动,你还得不停补前缀,根本维护不过来。
正确实现方法
Linux本身有标准接口判断文件类型,根本不需要看文件名猜是不是块设备:
- C++17以上直接用标准库
std::filesystem的is_block_file()方法就能判断 - 要兼容老版本C++就用POSIX标准的
stat()接口,配合S_ISBLK()宏判断 - 如果要拿当前系统真正在线的整块磁盘(不含分区),直接遍历内核导出的
/sys/block目录最准,这个目录是内核实时生成的,不会有/dev下可能残留的无效设备节点。
参考代码
遍历/dev获取所有块设备(含分区)
#include <filesystem> #include <iostream> namespace fs = std::filesystem; int main() { for (const auto& entry : fs::directory_iterator("/dev/")) { // 核心判断:直接识别块设备文件,和文件名没有任何关系 if (fs::is_block_file(entry.status())) { std::cout << "Found block device: " << entry.path() << '\n'; // 要过滤分区只留整盘的话,在这里加判断就行:可以查设备号的分区位,或者去/sys/block下查对应条目是否存在 } } return 0; }
遍历/sys/block仅获取在线整块块设备
这个方案不会返回分区,也不会返回已经拔掉但/dev下还留着节点的无效设备,适合做磁盘枚举的场景:
#include <filesystem> #include <iostream> namespace fs = std::filesystem; int main() { for (const auto& entry : fs::directory_iterator("/sys/block/")) { auto dev_name = entry.path().filename().string(); std::cout << "Found online whole disk: /dev/" << dev_name << '\n'; // 要枚举这个盘的分区也很简单,遍历entry.path()下的子目录,名字是盘名加数字的就是分区,对应/dev下的同名分区设备 } return 0; }
兼容无C++17环境的判断函数
如果编译环境不支持C++17,用POSIX接口实现块设备判断即可:
#include <sys/stat.h> #include <cerrno> bool is_block_device(const char* path) { struct stat st{}; if (stat(path, &st) != 0) { // 可以根据errno拿错误原因,比如权限不足、文件不存在 return false; } return S_ISBLK(st.st_mode); }
注意事项
- 永远不要硬编码设备名前缀,内核里的块设备驱动有几十上百种,你枚举不完的
- 要拿块设备的容量、扇区大小、是否可移动等属性,直接读
/sys/block/<设备名>/目录下的对应文件就行,不用调用lsblk、fdisk之类的外部命令,解析效率高还没有兼容性问题
内容的提问来源于stack exchange,提问作者hanshenrik
相关产品推荐
相关产品推荐

