最可靠方式是直接调用 statvfs() 获取挂载点剩余空间,用 f_bavail * f_frsize 计算非 root 用户可用字节数,注意溢出和路径有效性;枚举挂载点应解析 /proc/mounts 并过滤虚拟文件系统;单位换算须用 1024 进制整数除法,避免浮点误差。

Linux 下用 statvfs() 读取挂载点剩余空间最可靠
直接调用 statvfs() 是 C++ 获取单个路径所在文件系统剩余空间的底层标准方式,比解析 df 命令输出更稳定、无 shell 依赖、线程安全。它返回的是 struct statvfs,关键字段包括 f_bavail(非 root 用户可用块数)、f_frsize(文件系统基本块大小),二者相乘才是真实可用字节数。
常见错误是误用 f_bfree(所有用户空闲块)或忽略 f_frsize 直接乘 f_bavail —— 这会导致数值小好几个数量级。
-
statvfs("/home", &buf)中路径只需是挂载点内任一有效路径,不强制要求是挂载根目录(如/home/user也能得到 /home 分区信息) - 若返回 -1,检查
errno:常见为ENOENT(路径不存在)、ENOTDIR(路径是文件非目录)、EBADF(路径为空字符串) - 注意
f_bavail * f_frsize可能溢出uint64_t,建议用static_cast<uint64_t>(buf.f_bavail) * buf.f_frsize</uint64_t>显式转换
如何枚举所有挂载点:优先读 /proc/mounts 而非 getmntent()
/proc/mounts 是 Linux 内核实时维护的挂载表,格式稳定(空格分隔,第 1 列设备、第 2 列挂载点、第 3 列文件系统类型),C++ 可用 std::ifstream 逐行解析。相比 getmntent(),它不依赖 libc 的静态缓冲区,无线程安全问题,且能正确识别 overlayfs、tmpfs 等现代挂载类型。
容易踩的坑是未跳过注释行(以 # 开头)或未处理带空格的挂载点(如 /mnt/My Passport)—— 实际上 /proc/mounts 中空格会被转义为 0,需做反向解码。
- 过滤条件建议保留
statvfs()可成功调用的挂载点:跳过proc、sysfs、debugfs等虚拟文件系统(它们statvfs()返回 0 或失败) - 对每个挂载点调用
statvfs()前,先用access(mount_point.c_str(), F_OK)确认路径可访问,避免因卸载竞态导致崩溃 - 若需区分“硬盘分区”(如
/dev/sda1)和“网络/内存挂载”,可检查/proc/mounts第 1 列是否匹配/dev/*或/dev/nvme*?模式
C++ 封装时别硬编码单位换算逻辑
用户需要的是“剩余 23.4 GB”,不是“24985673728 字节”。但单位换算(B/KB/MB/GB)必须明确采用 1024 进制(IEC 标准),而非 1000 进制;同时要保留 1 位小数,且避免 23.0 GB 显示为 23 GB。
错误做法是用 std::format 或 printf("%.1f GB") 直接除 1024³ —— 浮点误差可能导致 999.999 四舍五入成 1000.0,单位错升一级。
- 推荐用整数累除:先判断数量级,再用
value / (1024LL 计算整数部分,余数单独算小数位 - 对
total、available、used三个值统一用同一套换算逻辑,否则 UI 上出现 “总空间 465.8 GB,可用 23.4 GiB” 这类单位不一致问题 - 如果库需跨平台,Windows 下应改用
GetDiskFreeSpaceEx(),且注意其*lpFreeBytesAvailable返回的是“非管理员可用字节数”,与 Linux 的f_bavail语义一致
实战中必须处理的三个边界场景
真实环境里,挂载点可能正在被 umount、磁盘已满触发只读、或路径属于 bind mount —— 这些不会报错,但数据会失真。
- 当
statvfs()返回f_blocks == 0,说明文件系统不支持该调用(如某些 fuse 实现),应跳过或标记为“未知容量” - 若
f_bavail == 0但f_bfree > 0,表示磁盘已满且 reserve blocks 被耗尽(典型于 ext4 默认 5% reserve),此时普通用户确实无法写入 - bind mount(如
mount --bind /a /b)在/proc/mounts中独立成行,但statvfs("/b")和statvfs("/a")返回完全相同的数据,需按设备号(st_dev)去重,否则 UI 重复显示同一分区
最易被忽略的是:获取信息后到 UI 渲染之间存在时间差,而磁盘状态变化极快。不要缓存结果超过 2 秒,尤其在监控类应用中——一次 statvfs() 调用本身耗时不到 10μs,没必要省这点开销。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











