跳转到内容

C 编程/POSIX 参考/sys/stat.h/stat

来自维基教科书,开放的书籍,为一个开放的世界

stat() 是一个 Unix 系统调用,它返回有关文件 inode 的有用数据。stat() 的语义在不同的 操作系统 之间有所不同。例如,Unix 命令 ls 使用它来检索有关(除许多其他信息外)的信息

  • mtime: 时间 最后修改(ls -l),
  • ctime: 最后状态更改时间(ls -lc)和
  • atime: 最后访问时间(ls -lu)。

stat() 函数和 stat 结构

[编辑 | 编辑源代码]

在所有符合 POSIX 标准的 类 Unix 操作系统上都可以找到的 POSIX 库头文件 sys/stat.h 声明了 stat()fstat()lstat() 例程

 int stat(const char *filename, struct stat *buf);
 int lstat(const char *filename, struct stat *buf);
 int fstat(int filedesc, struct stat *buf);

并定义了 struct stat 结构,其中至少包含以下成员

     dev_t       st_dev;     /* ID of device containing file */
     ino_t       st_ino;     /* inode number */
     mode_t      st_mode;    /* protection */
     nlink_t     st_nlink;   /* number of hard links */
     uid_t       st_uid;     /* user ID of owner */
     gid_t       st_gid;     /* group ID of owner */
     dev_t       st_rdev;    /* device ID (if special file) */
     off_t       st_size;    /* total size, in bytes */
     time_t      st_atime;   /* time of last access */
     time_t      st_mtime;   /* time of last modification */
     time_t      st_ctime;   /* time of last status change */
     blksize_t   st_blksize; /* blocksize for filesystem I/O */
     blkcnt_t    st_blocks;  /* number of blocks allocated */
[编辑 | 编辑源代码]

lstat() 是一个 库函数,它检索 文件 的状态。它与 stat() 相同,不同之处在于当文件是 符号链接 时,它返回有关链接本身的信息,而不是链接到的文件的信息。

fstat() 是一个 库函数,它检索文件的狀態。它与 stat() 相同,不同之处在于文件的标识作为 文件描述符 而不是作为 文件名 传递。

atime 的批评

[编辑 | 编辑源代码]

写入文件会更改其 mtimectime,而读取文件会更改其 atime。因此,在符合 POSIX 标准的系统上,读取文件会导致写入,这一点受到了批评。这种行为通常可以通过在 /etc/fstab 中添加一个挂载选项来禁用。

但是,关闭 atime 更新会破坏 POSIX 兼容性,并且一些应用程序(尤其是 mutt 邮件阅读器(在某些配置中)和一些文件使用监视工具,尤其是 tmpwatch)也会因此而无法正常工作。在最坏的情况下,不更新 atime 会导致某些备份程序无法备份文件。

Linux 内核开发人员 Ingo Molnár 将 atime 称为“可能是历史上最愚蠢的 Unix 设计理念”,[1][2] 并补充道:“想一想:‘对于从磁盘读取的每个文件,让我们执行...写入磁盘!并且,对于已经缓存并从缓存中读取的每个文件...执行写入磁盘!’”他还强调了这种性能影响

Atime 更新是 Linux 今天面临的最大的 IO 性能缺陷。摆脱 atime 更新将比过去 10 年所有页面缓存加速的总和,_加在一起_,为我们带来更多日常的 Linux 性能。

解决方案

[编辑 | 编辑源代码]

当前版本的 LinuxMac OS XSolarisFreeBSDNetBSDOpenBSD 支持 noatime 挂载选项,该选项会导致 atime 字段永远不会被更新。这会破坏与 POSIX 的兼容性。

当前版本的 Linux 支持四个挂载选项,可以在 fstab 中指定

  • strictatime(以前是 atime,以前是默认值;从 2.6.30 开始是 strictatime)– 始终更新 atime。
  • relatime –(相对 atime),在 2.6.20 中引入,从 2.6.30 开始是默认值
  • nodiratime – 永远不要更新目录的 atime
  • noatime – 永远不要更新 atime;包括 nodiratime;最高性能,最低兼容性

strictatime 符合 POSIX 标准;Alan Cox 将其他选择描述为

关闭 atime,它与标准不兼容,切换到 relatime,它与标准不兼容,但不会破坏任何东西(这很好)

使用 noatime 选项挂载的文件系统不会在读取时更新 atime,而 relatime 选项仅在先前 atime 早于 mtime 或 ctime 或者先前 atime 超过 24 小时之前时才会更新 atime。许多用户使用 noatime 没有任何问题,只要他们不使用依赖 atime 的应用程序,并且这比 relatime 提供了一些好处(读取时永远不会写入 atime)。

从 2.6.30(2009 年 6 月 9 日)开始,Linux 默认使用 relatime,[3] 这样它就不会在所有文件读取时更新 atime。这种行为为大多数用途提供了足够的性能,并且不应破坏任何重要的应用程序。在做出决定之前,对文件系统性能进行了扩展讨论。[4] 事实上,relatime 默认是 Linux 在 2.6.29 版本发布后应用的首个补丁。在最初的补丁中,relatime 仅在 atime < mtime 或 atime < ctime 时更新 atime;后来进行了修改,以更新 24 小时或更长时间之前的时间,以便 tmpwatch 和 Debian 的流行度计数器(popcon)能够正常运行。

在参考文献中可以找到进一步的讨论。[5][6]

请注意,ctime 与文件创建时间无关。它在每次文件内容更改时(与 mtime 同时)更新,以及在元数据更改时更新,例如文件权限文件所有权以及硬链接的创建和删除。在某些实现中,ctime 会因文件重命名而受到影响(原始 Unix 和现代 Linux 都倾向于这样做)。

atimemtime 不同,ctime 无法通过 utime() 设置(如 touch 所使用的);唯一能将其设置为任意值的方法是更改系统时钟。

mtime 等的粒度

[编辑 | 编辑源代码]

time_t 提供精确到 1 秒的时间。

某些文件系统提供更高的粒度。在 Linux 内核 2.5.48 及以上版本中,stat 结构支持三个文件时间戳字段的纳秒级分辨率。这些以 stat 结构中的附加字段形式公开。

FAT 文件系统提供的 timestamps 粒度为 2 秒。[7]

参考资料

[编辑 | 编辑源代码]
  1. Kernel Trap: Linux: Replacing atime With relatime,由 Jeremy 于 2007 年 8 月 7 日撰写
  2. Once upon atime,LWN,由 Jonathan Corbet 于 2007 年 8 月 8 日撰写
  3. Linux 2 6 30,Linux 内核新手
  4. That massive filesystem thread,LWN,由 Jonathan Corbet 于 2009 年 3 月 31 日撰写
  5. Installing Linux on USB – Part 4: noatime and relatime mount options
  6. Relatime Recap,Valerie Aurora
  7. How accurate is ruby mtime and friends at StackOverflow.com
[编辑 | 编辑源代码]
华夏公益教科书