You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux文件时间(atime/ctime/mtime)测试异常:是否缓存及如何刷新?

文件时间(atime/ctime/mtime)触发时机与WSL下stat缓存问题

问题描述

我正在为LittleFS文件系统编写文件时间(atime、ctime、mtime)管理代码,希望尽量贴合Linux/POSIX规范,但对这些时间在文件生命周期中的触发时机存在疑问——比如atime是在打开、读取还是关闭文件时更新?为了搞清楚这个问题,我在WSL子系统里写了测试程序,结果意外发现程序执行期间文件时间完全没变化。想请教:stat信息是不是被缓存了?该怎么刷新?

测试程序

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

char* mytime(const time_t *timep)
{
    struct tm *tm = localtime(timep);
    static char text[20];
    sprintf(text, "%d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
    return text;
}

void test_stat(const char *filename)
{
    printf("Stat of %s ", filename);
    struct stat statbuf = { 0 };
    int rc = stat(filename, &statbuf);
    if (rc != 0)
    {
        printf("stat rc=%d, errno=%d, %s\n", rc, errno, strerror(errno));
        return;
    }
    printf("st_atime=%s, st_ctime=%s, st_mtime=%s\n",
           mytime(&statbuf.st_atime),
           mytime(&statbuf.st_ctime), mytime(&statbuf.st_mtime));
}

int main(int argc, char *argv[])
{
    static const char *filename = "testfile";
    static int perm = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;

    test_stat(filename);

    int fh = open(filename, 0, perm);
    if (fh < 0)
    {
        printf("open rc=%d, errno=%d, %s\n", fh, errno, strerror(errno));
    }
    else
    {
        test_stat(filename);
    }

    printf("Open Create\n");
    fh = open(filename, O_CREAT, perm);
    if (fh < 0)
    {
        printf("open O_CREAT rc=%d, errno=%d, %s\n", fh, errno,
               strerror(errno));
    }
    else
    {
        test_stat(filename);
    }

    printf("Write\n");
    char *buffer = "1234567890";
    write(fh, buffer, strlen(buffer));
    test_stat(filename);

    printf("Close fh=%d\n", fh);
    int rc = close(fh);
    if (rc < 0)
    {
        printf("close rc=%d, errno=%d, %s\n", rc, errno, strerror(errno));
    }

    sleep(2);

    test_stat(filename);

    sleep(2);

    printf("Reopen, O_RDWR\n");
    fh = open(filename, O_RDWR);
    if (fh < 0)
    {
        printf("open rc=%d, errno=%d, %s\n", fh, errno, strerror(errno));
    }
    else
    {
        test_stat(filename);
    }

    printf("Write more\n");
    write(fh, buffer, strlen(buffer));
    test_stat(filename);

    printf("Close\n");
    rc = close(fh);
    if (fh < 0)
    {
        printf("close rc=%d, errno=%d, %s\n", fh, errno, strerror(errno));
    }

    test_stat(filename);

    sleep(2);

    printf("Reopen, O_WRONLY\n");
    fh = open(filename, O_WRONLY);
    if (fh < 0)
    {
        printf("open rc=%d, errno=%d, %s\n", fh, errno, strerror(errno));
    }
    else
    {
        test_stat(filename);
    }

    printf("Write more\n");
    write(fh, buffer, strlen(buffer));
    test_stat(filename);

    printf("Close\n");
    rc = close(fh);
    if (fh < 0)
    {
        printf("close rc=%d, errno=%d, %s\n", fh, errno, strerror(errno));
    }

    test_stat(filename);

    sleep(2);
}

测试结果

waynej@SV3273:~/testTimes$ rm -f testfile; cc -o testTimes testTimes.c && ./testTimes
Stat of testfile stat rc=-1, errno=2, No such file or directory
open rc=-1, errno=2, No such file or directory
Open Create
Stat of testfile st_atime=10:06:48, st_ctime=10:06:48, st_mtime=10:06:48
Write
Stat of testfile st_atime=10:06:48, st_ctime=10:06:48, st_mtime=10:06:48
Close fh=3
Stat of testfile st_atime=10:06:48, st_ctime=10:06:48, st_mtime=10:06:48
Reopen, O_RDWR
Stat of testfile st_atime=10:06:48, st_ctime=10:06:48, st_mtime=10:06:48
Write more
Stat of testfile st_atime=10:06:48, st_ctime=10:06:48, st_mtime=10:06:48
Close
Stat of testfile st_atime=10:06:48, st_ctime=10:06:48, st_mtime=10:06:48
Reopen, O_WRONLY
Stat of testfile st_atime=10:06:48, st_ctime=10:06:48, st_mtime=10:06:48
Write more
Stat of testfile st_atime=10:06:48, st_ctime=10:06:48, st_mtime=10:06:48
Close
Stat of testfile st_atime=10:06:48, st_ctime=10:06:48, st_mtime=10:06:48

解答

1. Stat信息缓存问题及解决方法

WSL环境下(尤其是WSL1),文件系统会结合Windows的缓存机制与Linux内核的inode缓存,导致stat调用返回的是缓存的元数据,而非磁盘上的实时值。刷新缓存可以用以下方法:

  • 调用posix_fadvise(fh, 0, 0, POSIX_FADV_DONTNEED):告知内核无需再缓存该文件的数据和元数据,后续stat会重新读取磁盘;
  • 调用fsync(fh):在写入后强制将文件数据和元数据刷到磁盘,再执行stat;
  • 全局刷新用sync():将所有缓存的文件系统数据同步到磁盘,但开销较大;
  • 若使用WSL2,原生Linux内核的缓存机制和桌面Linux一致,上述方法同样适用;如果是挂载的Windows目录,可能需要额外禁用Windows端的缓存优化。

另外,测试程序中写入操作后内核可能延迟刷盘,导致stat看不到mtime变化,建议在write后加上fsync(fh)再测试。

2. POSIX规范下文件时间的触发时机

  • atime(访问时间):POSIX标准要求在文件被读取(如read()、pread()调用成功)时更新,但现代Linux默认启用relatime挂载选项(部分系统甚至用noatime),会延迟更新以提升性能:只有当atime早于mtime/ctime,或者距离上次更新超过一天时才会更新。若要测试严格的atime行为,需要用mount -o remount,atime /path重新挂载目标文件系统。
  • mtime(修改时间):当文件内容被修改(如write()、pwrite()成功写入数据)时更新,内核通常会延迟更新以合并多次写入操作,一般在文件关闭或fsync时同步到磁盘。
  • ctime(状态改变时间):只要文件的元数据(权限、所有者、大小、链接数等)或内容被修改,就会更新——它本质是inode的修改时间,任何导致inode变化的操作都会触发ctime更新。

内容的提问来源于stack exchange,提问作者wdtj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 15:44:56