Linux下用statvfs获取挂载点剩余空间最可靠,需传入挂载路径(如"/home")而非设备名,优先用f_bavail×f_frsize计算非特权用户可用字节数,并过滤伪文件系统以准确枚举有效挂载点。

Linux 下用 statvfs 获取挂载点剩余空间最可靠
直接调用 statvfs 是 C++(POSIX 环境)里获取挂载点容量、可用空间等信息的底层标准方式,比解析 /proc/mounts 或调用 df 命令更轻量、更可控,也避开了 shell 解析的稳定性风险。
关键点在于:传入的是**挂载路径**(如 "/"、"/home"),不是设备名(如 "/dev/sda1");返回的 f_bavail 是非特权用户可用块数,f_bfree 是所有用户可用块数,日常判断“还能写多少”应优先看 f_bavail。
示例片段:
#include <sys>
#include <iostream><p>struct statvfs buf;
if (statvfs("/home", &buf) == 0) {
uint64_t block_size = buf.f_frsize; // 推荐用 f_frsize 而非 f_bsize
uint64_t avail_bytes = static_cast<uint64_t>(buf.f_bavail) * block_size;
std::cout </uint64_t></p></iostream></sys>
-
statvfs不会自动解析符号链接目标路径,若传入软链接需先用realpath展开 - 某些网络文件系统(如 NFS)可能返回
ENOSYS,需检查返回值并降级处理 -
f_frsize是“基本块大小”,比f_bsize更准确,尤其在 XFS 等文件系统上
如何枚举当前所有有效挂载点(避开伪文件系统)
不能只靠 /proc/mounts 原样读取——里面混着 sysfs、debugfs、tmpfs 等无磁盘空间意义的条目。实际应用中必须过滤出真实块设备挂载点。
推荐做法:读取 /proc/self/mountinfo(比 /proc/mounts 结构更清晰),按 major:minor 字段查 /sys/dev/block/ 是否存在对应设备节点,并排除 nodev 类型(如 proc、devtmpfs)。
- 常见需跳过的文件系统类型:
proc、sysfs、devtmpfs、debugfs、securityfs、tracefs、tmpfs(除非你明确要监控内存盘) -
/proc/mounts中第 3 列是type,可快速做字符串匹配,但注意它不包含挂载选项细节,mountinfo第 12 列才含完整 options - 不要依赖
getmntent,它已被标记为废弃,且线程不安全
C++ 封装时要注意跨平台兼容性陷阱
Windows 没有 statvfs,得用 GetDiskFreeSpaceEx;macOS 虽支持 statvfs,但部分字段语义略有差异(比如 f_files 可能始终为 0)。硬写一套跨平台接口容易踩坑。
务实建议:用预处理器分条件实现,而非试图抽象统一接口。例如:
#ifdef __linux__
// 用 statvfs + /proc/mountinfo
#elif defined(__APPLE__)
// 用 statvfs,但跳过 f_files 相关逻辑
#elif _WIN32
// 用 GetDiskFreeSpaceExW + GetVolumeInformationByHandleW
#endif
- Windows 下路径必须是驱动器根路径(
"C:\"),不能是子目录("C:\Users")——否则GetDiskFreeSpaceEx返回错误 - macOS 的
statvfs对 APFS 容器卷返回的是整个容器的统计,不是单个 volume 的精确值 - 避免在多线程高频调用场景下反复打开/解析
/proc/mountinfo,可缓存结果并加简单超时(如 30 秒)
剩余空间单位换算与精度丢失问题
直接输出字节数对用户不友好,但转成 GB 时容易因整数除法或浮点精度出错。尤其当磁盘很大(>100TB)、块大小为 4K 时,uint64_t 乘法结果虽不会溢出,但后续除法若用 double 可能损失低比特精度。
- 推荐用整数运算分步换算:
bytes / 1024 / 1024 / 1024得 GiB(二进制单位),或bytes / 1000 / 1000 / 1000得 GB(十进制) - 避免写
bytes / (1024.0 * 1024.0 * 1024.0)—— 浮点中间结果可能四舍五入偏差几个字节 - 显示时保留一位小数即可,没必要到毫字节;但内部计算务必全程用
uint64_t - 注意
statvfs返回的f_bavail是fsblkcnt_t类型,32 位系统上可能是unsigned long,强制转uint64_t前先确认平台宽度
真正难的不是读数据,而是判断“哪个挂载点该被监控”——用户看到的路径和实际物理设备常不一致(LVM、RAID、btrfs subvolume),这时候光靠 statvfs 不够,得结合 libudev 或 lsblk 输出做映射。那已经是另一层问题了。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











