close() 不保证数据落盘,仅关闭文件描述符;需配合 fsync()(posix)或 flushfilebuffers()(windows)确保内核缓冲区写入磁盘,尤其在日志、金融等强一致性场景。

为什么 close() 之后文件还是没写完?
因为 close() 不等于“数据已落盘”——它只是关闭文件描述符,而内核缓冲区里的脏页可能还在排队等刷盘。尤其在程序崩溃、断电或 sync() 未触发时,write() 成功返回 ≠ 数据进了磁盘。这是 C++(以及底层 C)I/O 的默认行为,不是 bug,是设计取舍。
常见错误现象:std::ofstream 析构或调用 close() 后立刻读文件,发现内容缺失或截断;用 fsync() 前就退出进程,日志丢失。
- 使用场景:日志写入、配置持久化、金融交易记录等强一致性要求的场合
-
std::ofstream::close()会先调用flush(),但仅刷新 C++ 流缓冲区,不保证内核页缓存刷盘 - 若用
FILE*(如fopen),fclose()同样只做用户态 flush,不调用fsync()
fflush() 和 fsync() 到底该用谁?
fflush() 是 C 标准库函数,作用于 FILE*,把 libc 缓冲区内容推到内核;fsync() 是 POSIX 系统调用,作用于文件描述符(int fd),强制将内核缓冲区 + 元数据刷入磁盘。二者必须配合使用,缺一不可。
容易踩的坑:fflush(fp) 之后没获取对应 fd 就直接 fsync(),或者用错了 fd(比如从 ofstream 拿不到原始 fd)。
- 对
FILE*:先fflush(fp),再用fileno(fp)拿 fd,最后fsync(fd) - 对
std::ofstream:标准库不暴露 fd,需用rdbuf()->pubsync()(仅部分实现支持),更可靠的是改用open()/write()/fsync()手动控制 - Windows 上对应的是
FlushFileBuffers(),不是fsync()
std::ofstream 析构时自动 flush,但不 fsync
std::ofstream 在析构或 close() 时会调用 basic_filebuf::close(),内部执行 sync() —— 这个 sync() 对应的是 fflush() 级别,不是 fsync()。所以即使你没显式 flush(),只要对象生命周期结束,用户缓冲区就清了,但磁盘安全仍无保障。
性能影响明显:fsync() 是阻塞 I/O,可能耗时毫秒级(尤其机械盘),频繁调用会拖慢吞吐。不要把它当成普通 close() 的替代品。
- 若需强持久化,应在关键点手动
fsync(),而不是依赖析构 - 可考虑用
O_SYNC标志打开文件(如open(path, O_WRONLY | O_SYNC)),让每次write()都隐式fsync(),但开销更大 - 注意:C++20 的
std::filesystem::sync_filesystem()是同步整个挂载点,不是单个文件,慎用
一个最小可行的落盘保障片段(POSIX)
不用 C++ 流,直击系统调用,可控性最强:
int fd = open("data.bin", O_WRONLY | O_CREAT, 0644);
if (fd == -1) { /* handle error */ }
write(fd, buf, len);
// 关键两步
if (fsync(fd) != 0) { /* log error */ }
close(fd); // close 可以在 fsync 之后任意时机,但别漏掉
注意:fsync() 失败时,write() 的数据未必丢,但无法确认是否落盘;此时不应继续业务逻辑,应按错误路径处理(如重试、告警、降级)。fsync() 的失败往往意味着磁盘故障或只读挂载,不是临时抖动。
真正难的不是调用哪个函数,而是判断“此刻是否必须落盘”——日志每条都 fsync?还是攒批后统一刷?这取决于你的数据语义和 SLA,代码只是执行者。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











