• Unreal is funny !!!

flush(FILE*) 和 fsync(FILE*)

Linux 站长 4年前 (2020-12-25) 1623次浏览 已收录 0个评论
文章目录[隐藏]

文件写入时间的问题

今天在读redis的aof源码时,看到这样一段代码:此代码是 redis aof 时将 数据库中的数据转换为操作命令
并写入aof文件时所调用的:

/* Returns 1 or 0 for success/failure. 
 *
 * 将长度为 len 的内容 buf 写入到文件 r 中。
 *
 * 成功返回 1 ,失败返回 0 。
 */
static size_t rioFileWrite(rio *r, const void *buf, size_t len) {
    size_t retval;

    retval = fwrite(buf,len,1,r->io.file.fp);
    r->io.file.buffered += len;

    // 检查写入的字节数,看是否需要执行自动 sync
    if (r->io.file.autosync &&
        r->io.file.buffered >= r->io.file.autosync)
    {
        fflush(r->io.file.fp);
        aof_fsync(fileno(r->io.file.fp));
        r->io.file.buffered = 0;
    }

    return retval;
}

这里调用了 fflush ,和 fsync 方法,查阅资料:
https://stackoverflow.com/questions/2340610/difference-between-fflush-and-fsync
https://superuser.com/questions/1288890/will-buffer-be-automatically-flushed-to-disk-when-a-process-exits
得知,在计算的应用层,os层,设备层 为了性能 都设置有对应的缓冲区,当你向其中写入数据,并非真的数据写入了,而是写入了其缓冲区。
fflush 为将 应用层的数据写入os层,
fsync 为将os层的缓存写入设备层。
(TODO: 大致可以先这么理解吧,虽然不是特别清楚)

每一次 fflush ,fsync 都是阻塞的,redis 为了防止阻塞时间过长,每当从应用层试图写入最多 REDIS_AOF_AUTOSYNC_BYTES (32M)的数据时,就会调用一次 fflush 和 fsync.

实验

那是不是 不调用fflush 写入就会有延迟呢,验证一下:

void testFread() {
    struct stat stbuf;
    char *file_name = "/home/x/Downloads/a.txt";
    char *log_out_f = "/home/x/Downloads/log.txt";
    FILE *data_file = fopen(file_name, "w+");
    FILE *log_file = fopen(log_out_f, "w+");
    int fd = fileno(data_file);
    int n = 0;
    for (int j = 0; j < 1024; ++j) {
        char buf[10240];//10k
        for (int i = 0; i < 10240; i++) {
            buf[i] = (char) i;
        }
        fwrite(buf, 1024, 10, data_file);
        n += 10240;
        //flush buffer data
//        fflush(data_file);
        fstat(fd, &stbuf);
        //log
        fprintf(log_file, "%d  %d  %ld \n", j, n, stbuf.st_size);
    }
    fflush(data_file);
    fstat(fd, &stbuf);
    fprintf(log_file, "%d  %d  %ld \n", 1024, n, stbuf.st_size);
}

这段代码,循环每次写入都记录两个数据,应用层试图写入的数据量 n , 文件的大小,通过日志比对两个大小,即可看到不使用 fflush的效果:
997 10219520 10219520
998 10229760 10227712
999 10240000 10240000
1000 10250240 10248192
1001 10260480 10260480
1002 10270720 10268672
1003 10280960 10280960
1004 10291200 10289152
1005 10301440 10301440
1006 10311680 10309632
1007 10321920 10321920
1008 10332160 10330112
1009 10342400 10342400
1010 10352640 10350592
1011 10362880 10362880
1012 10373120 10371072
1013 10383360 10383360
1014 10393600 10391552
1015 10403840 10403840
1016 10414080 10412032
1017 10424320 10424320
1018 10434560 10432512
1019 10444800 10444800
从上面可以看到,文件实际的大小和 已写入的数据了是有减少的,这就是 应用层所做的缓存(TODO )
而当我们打开 fflush的注释,则第二列和第三列就完全一样了。

TODO 疑惑

1. 通过 fstat 函数调用得到的文件大小是 设备的文件大小还是 os 层的文件大小,是否包含os层的缓冲区大小?
2. 什么情况下可以模拟fflush的大的阻塞?
3. 当应用关闭,是否会调用fflush 或者类似的功能?


本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:flush(FILE*) 和 fsync(FILE*)
喜欢 (14)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址