fwrite断电丢数据的根本原因是三层缓存:libc用户态缓冲、内核页缓存、磁盘控制器缓存,仅fflush清第一层,必须fsync才能强制落盘;原子写入推荐临时文件+fsync+rename+父目录fsync。

为什么 fwrite 直接写文件在断电时大概率丢数据
因为 fwrite 默认只写入 libc 的用户态缓冲区,不保证落盘。内核页缓存(page cache)和磁盘控制器缓存还会进一步延迟实际写入。断电瞬间,三层缓存里未刷出的数据全部丢失——哪怕你调了 fflush,它只清到内核缓存,fsync 才真正等磁盘确认。
常见错误现象:fclose 返回成功,但断电后文件内容截断、乱码或空;用 rename 做原子替换却仍损坏,是因为重命名前的写入没持久化。
-
fflush(fp)→ 清 libc 缓冲区,进内核页缓存 -
fsync(fileno(fp))→ 强制刷页缓存 + 磁盘缓存(需硬件支持) - 某些 SSD/RAID 卡默认禁用写缓存,
fsync实际不等磁盘,需用hdparm -I /dev/sdX确认
用 O_SYNC 打开文件是否足够安全
不够。虽然 open(..., O_SYNC) 让每次 write 都隐式 fsync,但它有严重性能代价(同步 I/O 串行化),且无法绕过部分存储设备的写缓存欺骗问题(例如某些 NVMe 盘谎报 fsync 完成)。更关键的是:C++ 流(std::ofstream)不支持 O_SYNC,必须用底层 open/fdopen。
实操建议:
- 若坚持用 C 风格 I/O:
int fd = open("data.bin", O_WRONLY | O_CREAT | O_SYNC, 0644); FILE* fp = fdopen(fd, "w"); - 避免混合使用:
write(fd, ...)和fwrite(fp, ...)混用会导致缓冲错乱 -
O_SYNC在 ext4/xfs 上可靠,但在某些网络文件系统(NFSv3)上被忽略,需实测
推荐的原子写入模式:临时文件 + fsync + rename
这是 Linux 上最通用、兼容性最好的方案。核心是把风险控制在“全有或全无”:旧文件始终完好,新文件要么完整写入并重命名成功,要么失败不污染原文件。
关键步骤顺序不能错:
- 用唯一临时名打开文件:
int tmpfd = open("data.bin.tmp.XXXXXX", O_WRONLY | O_CREAT | O_EXCL, 0644); - 写入全部数据后,调用
fsync(tmpfd)(不是fflush) - 关闭文件描述符:
close(tmpfd)(fsync必须在close前) - 用
rename("data.bin.tmp.XXXXXX", "data.bin")替换原文件(该操作在同文件系统下原子) - 最后对目标路径再
fsync父目录(防止 rename 元数据丢失):int dirfd = open(".", O_RDONLY); fsync(dirfd); close(dirfd);
注意:O_EXCL 防止临时名冲突;fsync 父目录这步常被忽略,但 ext4 默认 data=ordered 模式下,缺它可能导致 rename 后目录项存在但文件内容为空。
C++ std::ofstream 怎么安全落地
标准库不提供 fsync 接口,必须穿透到底层文件描述符。Linux/macOS 可用 fileno(),Windows 需用 _fileno() + _commit()(后者等价于 fsync)。
示例(POSIX 环境):
std::ofstream ofs("data.bin.tmp", std::ios::binary);
ofs.write(data, size);
ofs.flush(); // 清 libc 缓冲区
int fd = fileno(ofs.rdbuf()->pubsync()); // 获取 fd,注意 pubsync() 可能不返回 fd,稳妥做法是自己 open 后 fdopen
fsync(fd);
close(fd); // 或用 ofs.close(),但需确保之前已 fsync
rename("data.bin.tmp", "data.bin");
更稳妥的做法是放弃 std::ofstream,直接用 open/write/fdatasync/close/rename 流程——尤其当数据关键性高时。fdatasync 比 fsync 轻量(不刷 mtime 等元数据),多数场景够用。
容易被忽略的点:临时文件和目标文件必须在同一文件系统(否则 rename 失败并退化为 copy+unlink,失去原子性);SSD 的掉电保护电容是否有效,直接影响 fsync 的实际可靠性。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











