statvfs 中总容量 = f_blocks × f_frsize,已用容量 = (f_blocks − f_bfree) × f_frsize;必须用 f_frsize(逻辑块大小),不可用 f_bsize;f_bavail 才是用户真正可用空间。

statvfs 返回的 f_blocks 和 f_frsize 到底怎么换算成字节
直接用 f_blocks * f_frsize 是常见误区——它给出的是「总块数 × 基本块大小」,但实际可用总空间应是 f_blocks * f_frsize,而「已用空间」得靠 (f_blocks - f_bfree) * f_frsize 算。注意:f_bfree 是未分配且非保留的空闲块数,f_bavail 才是普通用户真正能写的空闲块(考虑了 root 保留空间)。
常见错误现象:用 f_blocks * f_bsize(f_bsize 是 I/O 块大小,和容量无关)或忽略 f_frsize 直接乘 f_blocks,导致结果偏差几倍。
-
f_frsize是文件系统用于空间计算的“逻辑块大小”,必须用它,不是f_bsize - 总容量 =
static_cast<uint64_t>(vfs.f_blocks) * vfs.f_frsize</uint64_t> - 已用容量 =
(static_cast<uint64_t>(vfs.f_blocks) - static_cast<uint64_t>(vfs.f_bfree)) * vfs.f_frsize</uint64_t></uint64_t> - 别用
f_bsize参与容量计算,它只影响 read/write 缓冲建议
statvfs 能否返回挂载点路径?不能,得自己传
statvfs() 本身不返回挂载点名称,它只接受一个路径(如 "/" 或 "/home"),然后填充该路径所在文件系统的统计信息。所谓“获取挂载点名称”,其实是你得先知道目标路径——它在哪挂,你就查哪。
使用场景:想查某块磁盘的总空间,得明确指定其挂载目录(比如 "/mnt/data"),而不是设备名("/dev/sdb1");statvfs() 对设备路径会失败并设 errno = ENOTDIR。
- 传入路径必须是已挂载的目录,且进程有读权限(否则
statvfs()返回 -1,errno = EACCES) - 若想枚举所有挂载点,需读取
/proc/mounts或调用getmntent(),再对每个mnt_dir字段调用statvfs() - 不要试图从
statvfs()结构体里“解析出”挂载点——它压根不存这个字段
statvfs 在不同文件系统上的兼容性差异
statvfs() 是 POSIX 标准接口,在 Linux、macOS、FreeBSD 上都可用,但字段语义略有出入。最典型的是 f_flag:Linux 返回的是 ST_* 位掩码(如 ST_RDONLY),而 macOS 的 f_flag 是保留字段、始终为 0。
性能影响几乎可忽略——单次调用耗时在微秒级;但频繁调用(比如每秒查 100 次)可能触发 VFS 层缓存刷新,建议结果缓存 1–5 秒,尤其在监控程序中。
- Linux 下
f_fsid是文件系统 ID,可用于去重;但其他系统可能全零,不可依赖 - 某些嵌入式文件系统(如 FAT32、exFAT)可能不填满所有字段,
f_files/f_ffree可能为 0,此时应跳过 inode 相关计算 - 容器环境(如 Docker)中,若挂载为
shared或slave,statvfs()仍返回宿主机视角的值,不会隔离
std::filesystem::space() 能替代 statvfs 吗?可以但有坑
C++17 的 std::filesystem::space() 底层通常就调用 statvfs()(Linux/macOS)或 GetDiskFreeSpaceEx()(Windows),看起来更现代,但它不提供 f_flag、f_fsid 等元信息,也无法区分 f_bfree 和 f_bavail ——它只返回一个 space_info 结构,含 capacity、free、available 三个字段。
关键区别:它的 available 字段对应的是 f_bavail(用户可用),而 free 对应 f_bfree(总空闲),但没暴露原始 f_blocks,无法反推总块数或做高级判断(比如是否快满、是否启用了配额)。
- 如果只要总/剩/可用字节数,用
std::filesystem::space()更简洁、跨平台性更好 - 如果要判断只读、获取文件系统唯一 ID、或做内核级调试,必须用
statvfs() - 注意:MSVC 早期版本(std::filesystem::space() 在网络路径上支持不全,会抛异常
事情说清了就结束。真正容易被忽略的,是 f_frsize 和 f_bsize 的混淆,以及以为 statvfs() 能“发现”挂载点——它只是个查询工具,不是发现工具。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











