最可靠方式是Linux用getmntent()解析/proc/mounts,Windows用GetVolumeInformation();二者语义不同,应分平台实现而非强行统一。

Linux 下用 /proc/mounts 或 getmntent() 读取挂载信息最可靠
直接解析 /proc/mounts 是最快、最轻量的方式,它由内核实时维护,无需额外权限或外部命令依赖。C++ 程序只需以只读方式打开该文件,逐行解析即可。注意:该文件字段顺序固定(设备、挂载点、文件系统类型、选项、dump、pass),但卷标(LABEL)不在其中 —— 它属于设备元数据,需额外查证。
更标准的做法是调用 setmntent() + getmntent()(定义在 <mntent.h></mntent.h>),它会自动处理 /etc/fstab、/proc/mounts 和 /etc/mtab 的优先级逻辑,兼容性更好:
#include <mntent.h>
#include <cstdio>
#include <string>
<p>void list_mounts() {
FILE* fp = setmntent("/proc/mounts", "r");
if (!fp) return;</p><pre class="brush:php;toolbar:false;">struct mntent* ent;
while ((ent = getmntent(fp)) != nullptr) {
std::string dev = ent->mnt_fsname;
std::string path = ent->mnt_dir;
std::string type = ent->mnt_type;
// 注意:mnt_opts 是逗号分隔字符串,不含 LABEL
printf("dev=%s, path=%s, type=%s
", dev.c_str(), path.c_str(), type.c_str());
}
endmntent(fp);}
常见坑:mnt_fsname 可能是设备路径(如 /dev/sda1)、UUID(如 UUID=xxxx)或 LABEL(如 LABEL=Home)。若值以 LABEL= 开头,说明该挂载项本身已通过卷标识别 —— 这就是你要的“卷标信息”,可直接提取。
从设备路径反查 LABEL 需调用 udevadm 或读取 /dev/disk/by-label/
如果 mnt_fsname 是 /dev/sdb2 这类路径,而你又需要它的卷标,不能靠 stat() 或 ioctl() 直接获取 —— 文件系统类型不同,获取方式也不同(ext4 用 e2label,xfs 用 xfs_admin -l)。最通用且免 root 的办法是查 /dev/disk/by-label/ 符号链接:
立即学习“C++免费学习笔记(深入)”;
- 该目录下每个文件名即为卷标,指向实际设备节点(如
/dev/disk/by-label/MYDATA → ../../sdc1) - 用
readlink()读符号链接目标,再与mnt_fsname比对,就能建立 “卷标 ↔ 设备” 映射 - 注意:
/dev/disk/by-label/下的文件名是经过 shell 转义的(空格变),需用printf "%b"类逻辑还原,但 C++ 中更稳妥的是直接遍历目录并用realpath()解析目标后比较
示例片段(省略错误检查):
#include <dirent.h>
#include <libgen.h>
#include <unistd.h>
<p>std::string dev_to_label(const std::string& dev_path) {
DIR<em> dir = opendir("/dev/disk/by-label/");
if (!dir) return "";
struct dirent</em> ent;
char target[PATH_MAX];
while ((ent = readdir(dir)) != nullptr) {
if (ent->d_type != DT_LNK) continue;
std::string linkpath = "/dev/disk/by-label/" + std::string(ent->d_name);
if (readlink(linkpath.c_str(), target, sizeof(target)-1) <= 0) continue;
target[sizeof(target)-1] = ' ';
char* resolved = realpath(target, nullptr);
if (resolved && std::string(resolved) == dev_path) {
free(resolved);
closedir(dir);
return std::string(ent->d_name); // 原始卷标名(含转义)
}
if (resolved) free(resolved);
}
closedir(dir);
return "";
}
Windows 下必须用 GetVolumeInformation() 配合 GetLogicalDrives()
Windows 没有统一挂载点概念,所有驱动器字母(如 C:、D:)都是根级卷。先用 GetLogicalDrives() 获取位图掩码,再对每位调用 GetVolumeInformation():
- 传入驱动器根路径(如
"C:\\"),注意双反斜杠转义 -
lpVolumeNameBuffer参数即为卷标;lpFileSystemNameBuffer是文件系统名(NTFS/FAT32) - 若卷无标签,该 buffer 为空字符串 —— 不代表失败,只是未设置
- 需要
#include <windows.h></windows.h>,链接kernel32.lib
关键点:某些网络驱动器或 RAMDisk 可能返回 ERROR_NOT_READY,应跳过而非报错;U 盘热插拔时可能短暂不可读,建议加简单重试逻辑。
跨平台统一获取不现实,别硬套抽象接口
Linux 的 “挂载点” 和 Windows 的 “驱动器字母” 本质不同:前者可嵌套挂载(如 /mnt/data 挂载到 /dev/sdb1),后者强制单层。试图用一套 C++ 类封装两者,往往导致接口失真或漏掉关键细节(比如 Linux 的 bind mount、overlayfs,Windows 的卷挂载点 mountvol)。
真正实用的做法是:按目标平台写两套逻辑,在构建时用 #ifdef _WIN32 分支;或者用 CMake 判断平台后链接对应实现文件。别为了“统一”把 GetVolumeInformation() 的返回值硬塞进 struct mntent —— 字段语义不匹配,后续扩展和调试都会变麻烦。
最容易被忽略的是:卷标可能含 Unicode 字符(尤其 Windows),Linux 下 ext4 卷标默认是 UTF-8,但 FAT32 是 ASCII 或 OEM 编码;GetVolumeInformation() 返回的是宽字符(WCHAR*),必须正确转成 UTF-8 才能与其他模块交互。


















