14. fdatasync - 文件数据同步 見出しへのリンク
函数介绍 見出しへのリンク
fdatasync
系统调用用于将文件的数据修改强制写入磁盘,与fsync
类似,但只同步文件数据和必要的元数据(如文件大小),而不同步所有元数据。这使得fdatasync
比fsync
更快。
函数原型 見出しへのリンク
#include <unistd.h>
int fdatasync(int fd);
功能 見出しへのリンク
将文件描述符对应的文件数据和必要元数据强制写入磁盘,不包括访问时间等非必要元数据。
参数 見出しへのリンク
int fd
: 文件描述符
返回值 見出しへのリンク
- 成功时返回0
- 失败时返回-1,并设置errno:
EBADF
: 文件描述符无效EIO
: I/O错误EINVAL
: 文件描述符不支持同步EROFS
: 文件在只读文件系统上
相似函数 見出しへのリンク
fsync()
: 同步文件数据和所有元数据sync()
: 同步所有文件系统缓冲区
示例代码 見出しへのリンク
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
int main() {
int fd;
struct stat stat_before, stat_after;
printf("=== Fdatasync函数示例 ===\n");
// 示例1: 基本的文件数据同步操作
printf("\n示例1: 基本的文件数据同步操作\n");
// 创建测试文件
fd = open("test_fdatasync.txt", O_CREAT | O_RDWR | O_TRUNC, 0644);
if (fd == -1) {
perror("创建测试文件失败");
exit(EXIT_FAILURE);
}
printf("成功创建测试文件,文件描述符: %d\n", fd);
// 写入数据
const char *data1 = "First line of data for fdatasync test\n";
if (write(fd, data1, strlen(data1)) == -1) {
perror("写入数据失败");
close(fd);
exit(EXIT_FAILURE);
}
printf("写入数据: %s", data1);
// 使用fdatasync同步数据到磁盘
printf("使用fdatasync同步数据...\n");
if (fdatasync(fd) == -1) {
perror("fdatasync失败");
} else {
printf("fdatasync成功完成\n");
}
// 再写入更多数据
const char *data2 = "Second line of data\nThird line of data\n";
if (write(fd, data2, strlen(data2)) == -1) {
perror("写入更多数据失败");
} else {
printf("写入更多数据成功\n");
}
// 再次使用fdatasync同步
if (fdatasync(fd) == -1) {
perror("第二次fdatasync失败");
} else {
printf("第二次fdatasync成功完成\n");
}
// 示例2: fdatasync与fsync对比
printf("\n示例2: fdatasync与fsync对比\n");
// 获取同步前的文件状态
if (fstat(fd, &stat_before) == -1) {
perror("获取同步前文件状态失败");
} else {
printf("同步前文件状态:\n");
printf(" 文件大小: %ld 字节\n", stat_before.st_size);
printf(" 最后修改时间: %s", ctime(&stat_before.st_mtime));
printf(" 最后访问时间: %s", ctime(&stat_before.st_atime));
}
// 写入大量数据
char large_data[1000];
memset(large_data, 'A', sizeof(large_data) - 1);
large_data[sizeof(large_data) - 1] = '\n';
for (int i = 0; i < 10; i++) {
if (write(fd, large_data, sizeof(large_data)) == -1) {
perror("写入大量数据失败");
break;
}
}
printf("写入大量数据完成\n");
// 使用fdatasync同步
clock_t start = clock();
if (fdatasync(fd) == -1) {
perror("fdatasync失败");
} else {
clock_t fdatasync_time = clock() - start;
printf("fdatasync完成,耗时: %f 秒\n",
((double)fdatasync_time) / CLOCKS_PER_SEC);
}
// 获取fdatasync后的文件状态
if (fstat(fd, &stat_after) == -1) {
perror("获取fdatasync后文件状态失败");
} else {
printf("fdatasync后文件状态:\n");
printf(" 文件大小: %ld 字节\n", stat_after.st_size);
printf(" 最后修改时间: %s", ctime(&stat_after.st_mtime));
printf(" 最后访问时间: %s", ctime(&stat_after.st_atime));
}
// 使用fsync同步进行对比
start = clock();
if (fsync(fd) == -1) {
perror("fsync失败");
} else {
clock_t fsync_time = clock() - start;
printf("fsync完成,耗时: %f 秒\n",
((double)fsync_time) / CLOCKS_PER_SEC);
}
printf("通常情况下,fdatasync比fsync更快\n");
// 示例3: 性能测试对比
printf("\n示例3: 性能测试对比\n");
// 创建性能测试文件
int perf_fd = open("perf_test.txt", O_CREAT | O_RDWR | O_TRUNC, 0644);
if (perf_fd != -1) {
const char *test_data = "Performance test data line\n";
const int iterations = 1000;
// 测试fdatasync性能
clock_t fdatasync_start = clock();
for (int i = 0; i < iterations; i++) {
char buffer[100];
sprintf(buffer, "Line %d: %s", i, test_data);
if (write(perf_fd, buffer, strlen(buffer)) == -1) {
perror("性能测试写入失败");
break;
}
if (fdatasync(perf_fd) == -1) {
perror("fdatasync失败");
break;
}
}
clock_t fdatasync_total = clock() - fdatasync_start;
// 重新创建文件测试fsync性能
close(perf_fd);
unlink("perf_test.txt");
perf_fd = open("perf_test.txt", O_CREAT | O_RDWR | O_TRUNC, 0644);
clock_t fsync_start = clock();
for (int i = 0; i < iterations; i++) {
char buffer[100];
sprintf(buffer, "Line %d: %s", i, test_data);
if (write(perf_fd, buffer, strlen(buffer)) == -1) {
perror("性能测试写入失败");
break;
}
if (fsync(perf_fd) == -1) {
perror("fsync失败");
break;
}
}
clock_t fsync_total = clock() - fsync_start;
printf("fdatasync %d次操作耗时: %f 秒\n",
iterations, ((double)fdatasync_total) / CLOCKS_PER_SEC);
printf("fsync %d次操作耗时: %f 秒\n",
iterations, ((double)fsync_total) / CLOCKS_PER_SEC);
printf("性能提升: %.2f%%\n",
((double)(fsync_total - fdatasync_total) / fsync_total) * 100);
close(perf_fd);
unlink("perf_test.txt");
}
// 示例4: 适用场景演示
printf("\n示例4: 适用场景演示\n");
// 场景1: 日志文件 - 需要保证数据不丢失
printf("场景1: 日志文件同步\n");
int log_fd = open("app.log", O_CREAT | O_RDWR | O_APPEND, 0644);
if (log_fd != -1) {
const char *log_entry = "2023-01-01 12:00:00 INFO Application started\n";
if (write(log_fd, log_entry, strlen(log_entry)) != -1) {
// 对于日志文件,通常使用fdatasync,因为不需要同步访问时间
if (fdatasync(log_fd) == -1) {
perror("日志同步失败");
} else {
printf("日志条目已同步到磁盘\n");
}
}
close(log_fd);
unlink("app.log");
}
// 场景2: 数据库事务日志 - 需要高性能
printf("场景2: 数据库事务日志\n");
int db_fd = open("transaction.log", O_CREAT | O_RDWR | O_APPEND, 0644);
if (db_fd != -1) {
const char *transaction = "TXN: UPDATE users SET balance=1000 WHERE id=1\n";
if (write(db_fd, transaction, strlen(transaction)) != -1) {
// 数据库通常使用fdatasync来提高性能
if (fdatasync(db_fd) == -1) {
perror("事务日志同步失败");
} else {
printf("事务日志已高效同步\n");
}
}
close(db_fd);
unlink("transaction.log");
}
// 示例5: 错误处理演示
printf("\n示例5: 错误处理演示\n");
// 尝试对无效文件描述符同步
if (fdatasync(999) == -1) {
printf("对无效文件描述符同步: %s\n", strerror(errno));
}
// 尝试对只读文件描述符同步
int readonly_fd = open("test_fdatasync.txt", O_RDONLY);
if (readonly_fd != -1) {
if (fdatasync(readonly_fd) == -1) {
printf("对只读文件同步: %s\n", strerror(errno));
} else {
printf("对只读文件同步成功\n");
}
close(readonly_fd);
}
// 清理资源
printf("\n清理资源...\n");
if (close(fd) == -1) {
perror("关闭文件失败");
} else {
printf("成功关闭文件描述符 %d\n", fd);
}
if (unlink("test_fdatasync.txt") == -1) {
perror("删除测试文件失败");
} else {
printf("成功删除测试文件\n");
}
return 0;
}