Linux下用statvfs获取挂载点剩余空间最可靠,需传入有效挂载路径(如"/"),用f_frsize×f_bavail计算非root用户可用字节数,并配合/proc/mounts或getmntent枚举所有挂载点、过滤虚拟文件系统。

Linux 下用 statvfs 获取挂载点剩余空间最可靠
直接调 statvfs 是 C++(POSIX 环境)里获取某路径所在文件系统剩余空间的底层标准方式,不依赖 shell 命令或第三方库,结果和 df 一致。它返回的是原始块数,需手动换算成字节,且要注意 f_frsize 和 f_bsize 的区别——多数情况用 f_frsize 计算可用空间更准确。
常见错误是传入非目录路径(比如普通文件)或权限不足的路径,statvfs 会失败并设 errno 为 ENOTDIR 或 EACCES。务必检查返回值:
struct statvfs buf;
if (statvfs("/home", &buf) == 0) {
uint64_t free_bytes = static_cast<uint64_t>(buf.f_bavail) * buf.f_frsize;
}-
f_bavail是非特权用户可用块数(推荐用于“剩余空间”展示) -
f_blocks×f_frsize是总大小,f_bfree×f_frsize是所有用户可用总块数 - 别用
f_bsize换算剩余空间——它是 I/O 最佳块大小,和容量无关
遍历所有挂载点得靠 /proc/mounts 或 getmntent
想列出全部挂载点(比如实现类似 df -h 的功能),C++ 没有跨平台标准 API,Linux 下最稳的方式是解析 /proc/mounts。用 std::ifstream 读取即可,每行格式为:device mount_point fs_type options dump pass。注意过滤掉 none、tmpfs、devtmpfs 等伪文件系统,否则会得到无意义的“剩余空间”。
更便携的做法是用 setmntent/getmntent(定义在 <mntent.h>),它自动跳过注释和无效条目:
立即学习“C++免费学习笔记(深入)”;
FILE* fp = setmntent("/proc/mounts", "r");
struct mntent* ent;
while ((ent = getmntent(fp)) != nullptr) {
if (std::string(ent->mnt_type) != "proc" &&
std::string(ent->mnt_type) != "sysfs") {
// 对 ent->mnt_dir 调用 statvfs
}
}
endmntent(fp);-
getmntent返回的mnt_dir是挂载路径,不是设备名,正好适配statvfs - Windows 不支持
/proc/mounts或getmntent,若需跨平台,得切 Windows API(如GetDiskFreeSpaceEx) - 某些容器环境(如 Docker)中
/proc/mounts可能包含重复或嵌套挂载,建议按mnt_dir去重并按路径深度排序再处理
不要用 system("df -P") 解析输出
虽然简单,但问题极多:输出格式受 locale 影响(比如德语系统把 KB 写成 “KiB”),列顺序不固定(df -P 保证 POSIX 格式,但字段宽度仍可能变),且启动子进程开销大、不可控。曾见过因 df 被 alias 成带颜色选项的版本导致解析崩溃。
- 如果坚持用命令行,至少用
LANG=C df -P强制英文输出 - 必须按空格+制表符切割,并跳过首行(header),再取第1、4、5、6列(source、avail、used、%used)
- 但即便如此,遇到含空格的挂载点(如
/mnt/my disk)仍会出错——df默认不转义,-T也救不了
C++ 项目里该选哪个库?优先手写,次选 libstatgrab
没强必要就别引入新依赖。statvfs + getmntent 组合已覆盖 95% Linux 场景,代码不到 100 行,编译零额外链接。第三方库中,libstatgrab 是少数认真封装了多平台磁盘信息的(Linux/FreeBSD/macOS/Windows),提供 sg_get_disks_io 和 sg_get_disk_stats,但它的“剩余空间”字段实际也是封装了 statvfs 或等价系统调用。
-
boost::filesystem不提供磁盘容量接口(v1.70+ 的space函数只返回space_info,且 Windows 下可能不准) -
QStorageInfo(Qt)可用,但绑定 Qt 生态;std::filesystem::space(C++17)仅支持单路径,不枚举挂载点,且 glibc 实现早期有 bug(f_bavail误用f_bsize) - 真正麻烦的是 NFS 挂载点:某些 NFS 服务器会报告错误的
f_files,但f_bavail通常可信——这点文档几乎从不提,得实测
实际部署时,最容易被忽略的是挂载点热插拔(如 USB 盘拔插)。/proc/mounts 实时更新,但你的程序若缓存了挂载列表,就会 stale。要么每次需要时重读,要么监听 inotify 对 /proc/mounts 的修改——不过后者在容器里常不可用。


















