statvfs()可获取文件系统级块大小与使用率,但不支持用户/组配额;需用f_frsize×f_bavail计算普通用户可用空间,配额须调quotactl()等专用接口。

Linux 下用 statvfs() 读取文件系统块配额和使用率
直接调用 statvfs() 是获取挂载点级块使用信息最轻量、最可靠的方式,它不依赖 shell 命令或额外权限,所有主流 Linux 发行版都支持。注意它返回的是「文件系统层面」的统计(即整个挂载点),不是单个用户或目录的配额 —— 那需要 quotactl() 或 xfs_quota 等专用接口。
关键字段含义必须厘清:f_blocks 是总块数(以 f_frsize 为单位,不是 f_bsize),f_bfree 是非 root 可用空闲块,f_bavail 才是普通用户真正能写的空闲块(已扣除保留块)。误用 f_bfree 会导致预估偏高。
-
struct statvfs buf; if (statvfs("/path", &buf) == 0)成功后立刻检查buf.f_frsize,别默认用 512 或 4096 - 计算使用率时用
(buf.f_blocks - buf.f_bfree) * 100.0 / buf.f_blocks得到「已分配块占比」;但更实用的是「可用空间余量」:用buf.f_bavail换算成字节再对比阈值 - 若
buf.f_bavail == 0,不代表磁盘写满,可能是 reserved blocks 耗尽(df -h显示的 “Available” 列对应的就是f_bavail)
macOS 和 FreeBSD 上 statfs() 的兼容性陷阱
macOS 和 FreeBSD 不支持 statvfs() 的 POSIX 语义,必须改用 statfs(),且结构体字段名、单位、甚至保留策略都不同。最常见错误是把 Linux 代码直接编译过去,结果 f_bfree 返回值异常大或为负。
核心差异:struct statfs 中 f_bsize 是 I/O 块大小,f_blocks 单位是该 f_bsize,而 f_bavail 在 macOS 上是「非特权用户剩余块数」,但不扣除 reserved blocks —— 这意味着它可能比 df 显示的 “Avail” 更乐观。
立即学习“C++免费学习笔记(深入)”;
- 跨平台代码必须做宏判断:
#ifdef __linux__用statvfs(),#elif defined(__APPLE__) || defined(__FreeBSD__)用statfs() - macOS 下不要信任
f_bavail做容量告警,建议改用f_bfree并预留 5% 缓冲(因系统会动态调整 reserve) -
statfs()第二个参数是struct statfs*,不是statvfs*,类型错配会导致段错误或数据错位
获取用户级磁盘配额需绕过 statvfs(),改用 quotactl() 或解析 /proc/mounts
statvfs() 完全不感知用户/组配额(quota),哪怕启用了 project quota 或 user quota,它返回的仍是文件系统总量。要拿到某用户的已用配额,必须走内核 quota 接口 —— Linux 上是 quotactl(),需 root 权限或 CAP_SYS_ADMIN;macOS 则无原生支持,得靠第三方工具或 stat 用户主目录硬链接数+inode 分析来估算。
典型流程是:先查 /proc/mounts 确认目标挂载点是否启用 quota(含 usrquota 或 grpquota 标志),再调用 quotactl(QCMD(Q_GETQUOTA, ...)) 获取 struct dqblk。失败常见原因不是权限不足,而是 quota database(如 aquota.user)未生成或路径不对。
- 调用
quotactl()前必须确保挂载选项含usrquota,且/aquota.user在对应文件系统根目录下存在且可读 -
struct dqblk.dqb_curspace是当前已用字节数(含缓存未刷盘部分),dqb_bsoftlimit和dqb_bhardlimit是软硬限制,单位均为字节 - 没有 root 权限时,
getrlimit(RLIMIT_FSIZE)只限制单进程文件大小,跟文件系统配额无关,别混淆
避免用 system("df -P") 解析字符串——稳定性与性能双输
用 system() 调 df 看似简单,实则埋雷:输出格式随 locale 变化(如德语系统用逗号分隔千位),-P 仅保证字段对齐,不保证列顺序稳定;且每次 fork/exec 开销大,高频调用会拖慢服务。更糟的是,df 自身也调 statvfs(),你多包一层反而增加出错点。
唯一可接受的例外是调试阶段快速验证逻辑,生产环境必须用系统调用。若真需文本输出(比如日志记录),自己格式化 statvfs() 结果,用 std::format(C++20)或 snprintf 控制精度,避免依赖外部命令。
- 解析
df输出至少要跳过首行标题、处理多空格分隔、校验字段数 ≥ 6,否则遇到 LVM thin pool 或 overlayfs 挂载点极易崩溃 -
df -B1强制字节单位,但某些嵌入式 busybox 版本不支持,statvfs()天然跨设备统一 - 容器环境里
/proc/mounts和df显示的是宿主机视角,而statvfs()对 mount namespace 敏感,结果更准确
块级统计真正的复杂点不在 API 调用本身,而在「单位换算链」:从 f_frsize → 总字节数 → 可用字节数 → 百分比,中间任何一步漏掉乘法或搞反分子分母,结果就差一个数量级。更隐蔽的是 reserved blocks 在不同文件系统(ext4/xfs/btrfs)里触发条件不同,f_bavail 的实际意义得结合 tune2fs -l 或 xfs_info 输出交叉验证。


















