在Linux中,如何使用系统调用fsync()将参数fd所指的文件数据和元数据写入磁盘?

发布于:2025-05-01 ⋅ 阅读:(4) ⋅ 点赞:(0)

fsync()函数:系统调用可用于控制文件I/O内核缓冲

#include <unistd.h>
int fsync(int fd);
  • 功能:将文件描述符 fd 关联的文件数据及其元数据(如修改时间、权限等)强制写入磁盘,确保数据在系统崩溃或断电后不会丢失。

  • 参数fd 是已打开文件的描述符(通过 open() 获取)。

  • 返回值

    • 成功时返回 0

    • 失败时返回 -1,并设置 errno 表示错误原因(如 EBADFEIO 等)

2. 为什么需要 fsync()

  • 内核缓冲区机制:默认情况下,write() 操作会将数据写入内核的 页缓存(Page Cache),而非直接写入磁盘。这种延迟写入机制提升了性能,但存在风险:

    • 若系统崩溃或断电,缓存中的数据会丢失。

  • 数据持久化fsync() 强制将缓存中的数据刷新到磁盘,确保数据安全。


3. fsync() vs fdatasync()

  • fsync()

    • 同步文件数据 元数据(如 inode 中的修改时间、文件大小等)。

    • 适用于需要严格保证文件完整性的场景(如数据库事务日志)。

  • fdatasync()

    • 仅同步文件数据,不同步元数据(除非元数据对后续读取必要)。

    • 性能略优于 fsync(),适合对元数据不敏感的场景。


4. 使用场景

  1. 关键数据持久化

    • 数据库系统在提交事务时调用 fsync(),确保日志文件写入磁盘。

    • 配置文件更新后,立即同步以防配置丢失。

  2. 确保文件完整性

    • 在文件关闭前调用 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


网站公告

今日签到

点亮在社区的每一天
去签到