标准方案是Linux用statvfs()配合/proc/mounts解析,Windows用GetDiskFreeSpaceEx();需过滤伪文件系统,验证路径可访问性,并用realpath()标准化路径以确保准确性。

Linux 下 C++ 获取硬盘分区挂载点与剩余空间,标准方案是调用 statvfs()(非 statfs()),配合 /proc/mounts 解析;Windows 下必须用 GetDiskFreeSpaceEx() 或 WMI,不能直接读取 /proc。
怎么从 /proc/mounts 提取有效挂载点(Linux)
直接读取 /proc/mounts 是最轻量、无需 root 的方式,但要注意过滤掉伪文件系统(如 sysfs、proc、devtmpfs)和网络挂载(如 nfs、cifs),否则会得到无效或不可访问的路径。
实操建议:
- 逐行读取
/proc/mounts,用空格分割,第 2 列是挂载点(mount_point),第 3 列是文件系统类型(fstype) - 跳过以
#开头的注释行 - 只保留
fstype为ext4、xfs、btrfs、vfat、ntfs等实际存储类型的条目 - 对每个挂载点调用
statvfs()前,先用access(mount_point, F_OK)验证可访问性,避免因卸载中/权限问题导致statvfs()返回 -1
statvfs() 与 statfs() 的关键区别和选型依据
statvfs() 是 POSIX 标准接口,返回字段单位统一为字节(f_frsize * f_blocks 算总大小),且跨平台兼容性好;statfs() 是 Linux 特有,字段含义与内核版本强相关(如 f_type 值在不同 kernel 中不一致),不推荐新项目使用。
立即学习“C++免费学习笔记(深入)”;
常见错误现象:
- 误用
statfs()后发现f_bavail在某些 ext4 分区上为 0 —— 实际是字段语义差异(statfs的f_bavail是非 root 可用,而statvfs的f_bavail是所有用户可用) - 未乘
f_frsize直接用f_blocks计算容量,结果偏差巨大(例如f_frsize=4096,f_blocks=1000000,真实大小是 4GB 而非 1MB)
正确用法示例:
struct statvfs buf;
if (statvfs("/home", &buf) == 0) {
uint64_t total = buf.f_frsize * buf.f_blocks;
uint64_t free = buf.f_frsize * buf.f_bfree;
uint64_t avail = buf.f_frsize * buf.f_bavail; // 用户可用
}
Windows 下必须绕过 GetDriveType(),改用 GetDiskFreeSpaceEx()
GetDriveType() 只能判断盘符类型(如是否是固定磁盘),无法获取剩余空间;真正可用的是 GetDiskFreeSpaceEx(),它返回三个 ULARGE_INTEGER:总空间、空闲空间、用户可用空间(考虑配额)。
使用场景与注意事项:
- 传入路径必须以
"C:\\"形式结尾(双反斜杠转义),不能是"C:"或"C:\dir",否则可能返回错误值 - 需链接
kernel32.lib,且在 C++ 中注意ULARGE_INTEGER的QuadPart成员才是 64 位整数 - 若目标路径是网络驱动器(
GetDriveType()返回DRIVE_REMOTE),GetDiskFreeSpaceEx()仍可工作,但延迟较高,建议加超时逻辑或跳过
简短示例:
ULARGE_INTEGER free, total, usable;
if (GetDiskFreeSpaceEx(L"C:\\", &free, &total, &usable)) {
uint64_t total_bytes = total.QuadPart;
uint64_t free_bytes = free.QuadPart;
}
跨平台封装时最容易被忽略的细节
多数人会封装一个 get_disk_usage(const std::string& path),但漏掉两个关键点:一是路径标准化(Linux 下 /mnt/data/ 和 /mnt/data 视为不同挂载点;Windows 下 C: 和 C:\ 行为不一致),二是符号链接处理(statvfs() 作用于链接目标,不是链接本身)。
实操建议:
- Linux:对输入
path先调用realpath(),再提取其所在挂载点(可通过反复statvfs()父目录直到f_fsid改变,或解析/proc/self/mountinfo) - Windows:用
PathStripToRoot()或正则提取盘符根路径(^[a-zA-Z]:\\$),避免传入子目录导致失败 - 不要缓存
/proc/mounts结果超过 1 秒——热插拔 U 盘、mount --bind等操作会实时更新该文件
真正难的不是调用某个函数,而是让一次查询既快又稳:Linux 下要避开 /proc/mounts 解析开销与 statvfs() 权限失败,Windows 下要处理 UNC 路径和配额干扰。这些边界情况,往往在压测或用户现场才暴露。


















