write()返回0不表示成功,因c++流缓冲会掩盖底层i/o错误;必须flush()后检查failbit/badbit或用fsync()+errno捕获eio等物理层错误。

写入文件流时 write() 返回值为 0 不代表成功
很多 C++ 开发者默认 write() 或 ofstream::write() 写入后只要没抛异常就万事大吉,但磁盘扇区损坏(如坏道)常导致底层 write() 系统调用返回 0 或小于请求字节数,而 C++ 标准库流对象的 good()、fail() 在这种情况下可能仍返回 true——因为错误尚未被“刷新”到设备层。
关键点在于:C++ 流的缓冲机制会掩盖底层 I/O 错误,直到 flush() 或析构自动刷盘时才暴露问题。
-
ofstream::write()后立即检查!os.good()很可能为false,不能反映真实写入状态 - 必须在
flush()后立刻检查failbit或badbit,否则可能误判成功 - 若使用
std::ios_base::unitbuf模式(或std::cerr默认行为),每次输出都强制刷盘,但性能代价大,且不解决根本检测逻辑
用 fsync() + errno 捕获底层磁盘 I/O 错误
标准 C++ 流不暴露底层文件描述符,也无法直接获取 errno。要真正捕获扇区级写入失败(比如内核返回 EIO),必须绕过流封装,用 POSIX 接口控制。
典型路径是:先用 open() 获取 fd → write() → fsync() → 检查 errno。其中 fsync() 是关键:它强制将数据和元数据刷到物理介质,坏扇区在此刻才会触发 EIO 或 ENOSPC 等错误。
-
write()成功只说明数据进了内核页缓存,不代表落盘 -
fsync()失败(返回 -1)且errno == EIO,基本可判定为物理介质故障(如扇区损坏、NVMe 坏块) - 注意:Windows 上需用
FlushFileBuffers()替代fsync(),且需通过_get_osfhandle()从FILE*或流中提取 HANDLE(不推荐混用 C++ 流和 WinAPI)
避免 ofstream 自动析构掩盖错误
ofstream 对象离开作用域时会自动 close(),而 close() 内部会尝试 flush() 和系统 close() 调用——但 C++ 标准规定:如果此时发生错误,close() 的失败**不会抛异常**(除非设置了 exceptions(ios_base::failbit | ios_base::badbit)),且错误状态可能被忽略。
这意味着:你没显式 flush() 和检查,又让流自动析构,坏扇区错误就彻底丢掉了。
- 务必手动调用
os.flush(),然后立刻检查os.fail() || os.bad() - 更稳妥做法:设置异常掩码,例如
os.exceptions(std::ofstream::failbit | std::ofstream::badbit),这样flush()失败会抛std::ios_base::failure - 不要依赖析构做错误检测;析构期抛异常是危险行为(可能引发
std::terminate)
为什么 stat() 或 df 查不到扇区级损坏
文件系统工具(如 stat()、df -h)只报告逻辑空间使用和挂载状态,对已分配但物理不可写的扇区完全无感。坏扇区通常由硬盘固件在写入时首次发现并重映射(reallocation),此时上层看到的是 EIO,而非容量变化。
-
smartctl -a /dev/sdX中的Reallocated_Sector_Ct或Current_Pending_Sector才是线索,但这属于事前预警,不是写入时的实时检测 - 应用层唯一可靠方式,就是在每次关键写入后执行同步刷盘 + 错误检查
- 高频检测成本高,建议只对关键日志、配置、事务性数据做
fsync()+errno判断,普通数据流保持默认缓冲即可
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











