Linux/macOS用statvfs、Windows用GetDiskFreeSpaceEx最可靠:statvfs需传挂载点路径并用f_bavail×f_frsize计算用户可用字节数,GetDiskFreeSpaceEx须传宽字符根路径如L"C:"。

Linux/macOS 下用 statvfs 获取剩余空间最可靠
在类 Unix 系统上,statvfs 是获取挂载点级别剩余空间的标准方式,它返回的是文件系统层面的可用字节数(f_bavail * f_frsize),而非当前用户视角的剩余空间(f_bfree 已被保留空间影响,不推荐直接用)。注意:它操作对象是**路径所在挂载点**,不是单个文件或目录本身。
常见错误是传入一个不存在的路径,导致 statvfs 返回 -1 且 errno 为 ENOENT;或者传入符号链接未解引,可能误判挂载点。务必先用 realpath 或 std::filesystem::canonical(C++17)归一化路径。
-
statvfs的f_bavail是普通用户可用块数,比f_bfree更贴近实际可写量 -
f_frsize是文件系统“基本块大小”,必须用它而非f_bsize计算字节数(POSIX 要求f_frsize可靠) - 如果目标路径跨 bind mount 或 overlayfs,
statvfs仍返回底层挂载点数据,无法感知容器/命名空间隔离
Windows 下用 GetDiskFreeSpaceEx 替代 GetDiskFreeSpace
GetDiskFreeSpace 返回的是 16 位兼容的老式参数(如每簇扇区数),容易溢出且精度低;必须用 GetDiskFreeSpaceEx,它通过 ULARGE_INTEGER* 返回真实的空闲字节数,支持大于 4GB 的卷。
关键细节:第一个参数是**根目录路径字符串**(如 "C:\"),不是任意子路径;若传入 "C:\foo\bar",API 会自动截取到 "C:\",但若路径格式非法(缺冒号、双反斜杠错误),会失败并设 GetLastError() 为 ERROR_INVALID_NAME。
立即学习“C++免费学习笔记(深入)”;
- 必须确保传入路径以
"X:\"或"\\server\share"格式结尾,否则调用可能静默失败 - 返回的
*lpFreeBytesAvailable是“用户配额后”剩余空间,和 Linux 的f_bavail行为一致 - C++17
std::filesystem::space在 Windows 底层就调用GetDiskFreeSpaceEx,但要注意它对 UNC 路径支持依赖 Windows 版本(Win10 1607+ 才稳定)
C++17 std::filesystem::space 的坑:路径有效性与权限
std::filesystem::space 看似跨平台简洁,但它在路径不存在、无读权限、或指向挂载点外的符号链接时,会抛出 std::filesystem::filesystem_error,而不是返回无效值。很多人忽略异常处理,导致程序崩溃。
另一个隐性问题是:它返回的 available 字段在 Linux 下等价于 f_bavail * f_frsize,但在某些旧内核或 NFS 挂载中,f_bavail 可能为 0(即使还有空间),此时 available 也会是 0 —— 这不是 bug,而是文件系统驱动如实上报。
- 务必用
try/catch包裹调用,或提前用std::filesystem::exists和std::filesystem::is_directory做预检 - 不要假设
capacity > free > available恒成立;NFS 或 btrfs quota 场景下available可能远小于free - 若只需判断是否“足够写入 N 字节”,直接比较
space(p).available >= N即可,不必转成 GB 或做除法
为什么不用 df 或 du 命令解析?
调用 system("df -B1 /path | tail -n1 | awk '{print $4}'") 看似简单,但实际不可靠:输出格式随 locale 变化(德语系统列顺序不同)、df 默认显示磁盘使用率而非字节数、管道中断或子进程被信号终止时无 fallback。更严重的是,df 统计的是整个挂载点,而你的路径可能位于 bind mount 子树中,df 无法区分。
此外,频繁 fork/exec 对性能敏感场景(如日志轮转服务)有明显开销,且无法获取精确到字节的数值(df -B1 仍受 block size 对齐影响)。
- 除非调试临时查问题,否则不要在生产代码里 parse
df输出 -
statvfs和GetDiskFreeSpaceEx是系统调用,零 fork 开销,结果确定 - 如果必须用命令行(如容器环境限制),至少用
df --output=source,avail -B1 /path配合固定字段索引,而非依赖空格分割
跨平台封装时,最容易被忽略的是路径归一化和异常边界——比如 std::filesystem::space 在 Windows 上对长路径(>260 字符)默认失败,需启用 \? 前缀或修改组策略;而 Linux 下 statvfs 对只读文件系统返回成功,但 f_bavail 为 0,这时候该报错还是静默容忍,得看业务逻辑。


















