好的,我们来深入学习 sync_file_range 系统调用
1. 函数介绍
在 Linux 系统中,为了提高性能,当你调用 write() 将数据写入文件时,这些数据通常不会立即写入到物理磁盘上。相反,它们会被暂时存储在内核的页缓存 (Page Cache) 中。操作系统会在稍后的某个时间点(或者当缓存满时)自动将这些数据刷新到磁盘。这种机制大大提高了写入速度,但也带来了一个问题:如果在数据被实际写入磁盘之前系统崩溃或断电,那么这部分数据就会丢失。
data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">fsync() 和 fdatasync() 函数可以强制将文件的所有(或部分)修改数据从内核缓存刷新到磁盘,确保数据的持久化。但是,它们通常会刷新整个文件相关的数据和元数据,这可能比你需要的更多,导致不必要的性能开销。
sync_file_range (Sync File Range) 系统调用提供了更精细的控制。它允许你指定文件的一个特定字节范围,并决定对这个范围内的数据执行什么样的同步操作。你可以选择:
将数据从缓存写入磁盘(但不等待完成)。
等待数据写入磁盘完成。
将缓存中的脏数据(已修改但未写回)回写到磁盘。
简单来说,sync_file_range 就是让你用程序精确地告诉操作系统:“请帮我确保文件的第 X 字节到第 Y 字节的数据已经安全地保存到硬盘上了”,并且你可以控制这个过程是异步还是同步。
典型应用场景:
数据库系统:数据库需要精确控制其事务日志和数据文件的刷新,以确保事务的原子性和持久性,同时最大化性能。它们可以只刷新刚刚写入的关键日志部分,而不是整个文件。
大文件处理:在写入一个非常大的文件时,可以分段调用 sync_file_range,确保关键部分的数据已经落盘,而不需要等待整个大文件都写完。
高性能应用:需要在数据安全性和写入性能之间取得平衡的应用。
2. 函数原型
1 | #define _GNU_SOURCE // 需要定义这个宏才能使用 sync_file_range |
注意:函数名和参数类型可能因系统架构(32/64位)和内核版本而略有不同(如 sync_file_range2),但 glibc 会处理好兼容性,使用 sync_file_range 即可。
3. 功能
对文件描述符 fd 对应的文件,在从 offset 开始的 nbytes 字节范围内,根据 flags 指定的操作,执行同步操作。
4. 参数
fd:
int 类型。
一个已打开文件的有效文件描述符。
offset:
off64_t 类型。
指定要同步的文件范围的起始字节偏移量。必须是非负数。
nbytes:
off64_t 类型。
指定要同步的字节数。如果 nbytes 为 0,则表示同步从 offset 开始到文件末尾的所有数据。
flags:
- unsigned int 类型。
指定要执行的同步操作。可以是以下一个或多个标志的按位或 (|) 组合:
SYNC_FILE_RANGE_WAIT_BEFORE: 等待在 offset 和 nbytes 范围内之前发起的任何写入操作完成。
SYNC_FILE_RANGE_WRITE: 启动将指定范围内的脏页面(修改过的缓存数据)回写到磁盘的操作。这个操作通常是异步的,即函数可能在数据实际写入磁盘之前就返回。
SYNC_FILE_RANGE_WAIT_AFTER: 等待在 offset 和 nbytes 范围内由 SYNC_FILE_RANGE_WRITE 启动的回写操作完成。
常见的标志组合:
SYNC_FILE_RANGE_WRITE: 启动回写,但不等待。适用于“尽快开始保存数据,但我不等它完成”。
SYNC_FILE_RANGE_WRITE | SYNC_FILE_RANGE_WAIT_AFTER: 启动回写并等待其完成。这是最常用的方式,确保指定范围的数据确实写入了磁盘。
SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE | SYNC_FILE_RANGE_WAIT_AFTER: 等待之前的写入完成 -> 启动回写 -> 等待回写完成。非常彻底,但可能性能较低。
5. 返回值
成功: 返回 0。
失败: 返回 -1,并设置全局变量 errno 来指示具体的错误原因。
6. 错误码 (errno)
EBADF: fd 不是有效的文件描述符,或者该文件不支持同步操作。
EINVAL: flags 参数无效,或者 offset 为负数。
EIO: I/O 错误。
ENOMEM: 内核内存不足。
ENOSPC: 设备上没有足够的空间。
ESPIPE: fd 指向的是管道、套接字或 FIFO,这些不支持 sync_file_range。
7. 相似函数或关联函数
fsync: 同步文件的所有数据和元数据(如修改时间、文件大小等)。它会等待所有操作完成。
fdatasync: 同步文件的数据和必要的元数据(不包括访问时间等非必要元数据),通常比 fsync 快一些。它也会等待完成。
msync: 用于同步 mmap 内存映射区域到文件。
sync: 同步所有已挂载文件系统上的缓冲数据到磁盘。
open with O_SYNC / O_DSYNC: 在打开文件时指定同步标志,使得后续的 write 操作具有同步语义。
8. 示例代码
下面的示例演示了如何使用 sync_file_range 来同步文件的特定部分。
1 | #define _GNU_SOURCE // 启用 GNU 扩展以使用 sync_file_range |
9. 编译和运行
1 | # 假设代码保存在 sync_file_range_example.c 中 |
10. 预期输出 (时间值会有所不同)
1 | --- Demonstrating sync_file_range --- |
11. 总结
sync_file_range 是一个强大的、用于精确控制文件数据同步的系统调用。它允许程序只同步文件的特定部分,而不是整个文件,从而在需要数据持久化保证的场景下提供了比 fsync 更好的性能。理解其标志位的含义(WRITE, WAIT_BEFORE, WAIT_AFTER)对于正确使用它至关重要。它是构建高性能、数据安全应用(如数据库、文件系统工具)的重要工具。