fsync()函数:系统调用可用于控制文件I/O内核缓冲
#include <unistd.h>
int fsync(int fd);
功能:将文件描述符
fd
关联的文件数据及其元数据(如修改时间、权限等)强制写入磁盘,确保数据在系统崩溃或断电后不会丢失。参数:
fd
是已打开文件的描述符(通过open()
获取)。返回值:
成功时返回
0
。失败时返回
-1
,并设置errno
表示错误原因(如EBADF
、EIO
等)
2. 为什么需要 fsync()
?
内核缓冲区机制:默认情况下,
write()
操作会将数据写入内核的 页缓存(Page Cache),而非直接写入磁盘。这种延迟写入机制提升了性能,但存在风险:若系统崩溃或断电,缓存中的数据会丢失。
数据持久化:
fsync()
强制将缓存中的数据刷新到磁盘,确保数据安全。
3. fsync()
vs fdatasync()
fsync()
:同步文件数据 和 元数据(如
inode
中的修改时间、文件大小等)。适用于需要严格保证文件完整性的场景(如数据库事务日志)。
fdatasync()
:仅同步文件数据,不同步元数据(除非元数据对后续读取必要)。
性能略优于
fsync()
,适合对元数据不敏感的场景。
4. 使用场景
关键数据持久化:
数据库系统在提交事务时调用
fsync()
,确保日志文件写入磁盘。配置文件更新后,立即同步以防配置丢失。
确保文件完整性:
在文件关闭前调用
fsync()
,避免程序崩溃导致数据不一致。
5. 性能影响
磁盘 I/O 延迟:
fsync()
是阻塞操作,需等待磁盘写入完成,可能导致程序暂停。优化策略:
减少
fsync()
调用频率(如批量写入后同步一次)。使用
O_SYNC
标志(见下文),但需权衡性能。
6. 相关标志:O_SYNC
在 open()
时添加 O_SYNC
标志,可使每次 write()
自动等效于“写后调用 fsync()
”:
c
int fd = open("file.txt", O_WRONLY | O_CREAT | O_SYNC, 0666);
优点:数据写入更安全。
缺点:每次
write()
都会触发磁盘 I/O,性能显著下降。
7. 错误处理
需检查 fsync()
的返回值,并处理常见错误:
EBADF
:无效的文件描述符。EIO
:磁盘 I/O 错误(如硬件故障)。EROFS
/EINVAL
:文件系统不支持同步(如只读文件系统)。
8. 常见误区
close()
不会自动调用fsync()
:关闭文件描述符时,内核可能仍延迟写入磁盘。需显式调用fsync()
。fflush()
仅作用于用户缓冲区:C 标准库的fflush()
将用户态缓冲区数据提交到内核,但不会触发磁盘同步。需结合fsync()
使用
总结
何时使用
fsync()
:对数据持久性要求高的场景(如金融交易、日志文件)。避免滥用:频繁调用会严重降低性能,需根据业务需求权衡。
替代方案:使用电池备份的磁盘缓存(RAID 卡)、非易失性内存(NVM)等技术减少对
fsync()
的依赖。
代码演示:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
static char buf[4096];
int main(void)
{
int fd;
int ret;
fd = open("./test.txt",O_WRONLY | O_CREAT | O_EXCL,0666);
if (-1 == fd)
{
perror("open error\n");
return 1;
}
for ( int i = 0 ; i < 4096 ; i++){
ret = write(fd,buf,sizeof(buf));
if (ret == -1)
{
perror("write error\n");
close(fd);
return 1;
}
}
fsync(fd);
close(fd);
return 0;
}
运行结果: 4096*4096 = 16 M