std::ofstream::fail() 检测不到磁盘配额超限,因为 write(2) 在配额不足时仍可能返回成功,真正报错发生在 fsync(2) 或 close(2) 时;C++ 流不暴露 errno 且析构时忽略 close 错误,必须用 POSIX 接口显式 fsync 并检查 errno == EDQUOT。

std::ofstream::fail() 为什么检测不到磁盘配额超限?
因为磁盘配额不足时,write(2) 系统调用通常仍返回成功(写入 0 字节或部分字节),但后续 fsync(2) 或 close(2) 才真正报错 —— 而 C++ 标准流在 close() 前不主动刷盘,也不暴露底层 errno。所以仅靠 os.fail() 或 os.bad() 会漏掉配额问题。
如何捕获 write 后的 errno == EDQUOT?
必须绕过流缓冲,直接调用系统调用并检查 errno。C++ 标准库不提供接口,需用 POSIX:open() + write() + 显式 fsync() + close()。关键点是:写入后立即 fsync(),它会在配额不足时设置 errno = EDQUOT。
-
open()用O_SYNC不够 —— 它只保证单次write()持久化,但不触发配额检查;fsync()才强制元数据落盘,此时才爆配额错误 - 每次
write()后都应检查返回值是否等于请求长度;若小于,立刻查errno(可能是EDQUOT、ENOSPC或EAGAIN) -
fsync()返回 -1 且errno == EDQUOT是最可靠的配额中断信号
int fd = open("data.bin", O_WRONLY | O_CREAT, 0644);
if (fd == -1) { /* 处理 open 失败 */ }
ssize_t n = write(fd, buf, len);
if (n != len) {
if (errno == EDQUOT || errno == ENOSPC) {
// 配额或空间不足
}
}
if (fsync(fd) == -1 && errno == EDQUOT) {
// 这里才是配额耗尽的确定信号
}
close(fd);
std::ofstream 能否补救?
不能直接补救,但可辅助诊断:在 ofstream 写完后,用 fileno(os.rdbuf()) 获取底层 fd,再调用 fsync() 并检查 errno。注意:rdbuf() 必须是非 const 的,且流需处于 good 状态;os.flush() 不等价于 fsync(),它只清 C++ 缓冲区,不保持久化。
-
os.flush()后仍要手动fsync(fileno(os.rdbuf())) - 某些 libc 实现中,
std::ofstream析构时自动close(),但该close()的错误会被忽略 —— 无法捕获EDQUOT - 跨平台不推荐:Windows 没有
EDQUOT,对应错误是ERROR_DISK_FULL,需用GetLastError()
配额检测的时机和代价
配额检查必须在数据真正落盘后做,不是写入内存缓冲区就结束。频繁 fsync() 会显著拖慢性能(尤其机械盘),但省略它就等于放弃配额感知能力。折中方案是:大文件分块写入,每写入 1–4MB 就 fsync() 一次,并检查 errno。
- 不要依赖
statfs()预判配额 —— 它返回的是文件系统总空间,不是用户配额(quotactl()才能查,但 Linux 下需 root 权限) - 容器或 cgroup 环境下,
EDQUOT可能被替换成ENOSPC,需同时检查两者 - 日志类场景建议:写入前先
touch一个临时小文件并fsync(),快速探活配额状态
配额错误永远发生在“落盘确认”那一刻,而不是“写入调用”那一刻 —— 所有跳过 fsync() 或 close() 错误检查的逻辑,本质上都在假装它不存在。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











