我在Linux上使用XFS,并在每秒写一次内存映射文件。我注意到文件mtime(watch ls --full-time
显示(定期但不规则地更改。MTIMES之间的差距似乎在2到20秒之间,但不一致。系统上几乎没有其他运行 - 尤其是我的程序编写文件,再加上一个读数。
同一程序将其他一些Mmapped文件写得更频繁,其MTIME每30秒更改一次。
我不使用 msync()
(呼叫时会更新mtime(。
我的问题:
- 是什么更新MTime?
- 更新间隔可配置?
- 为什么某些MTimes每30秒更新一次,而我写的某些文件的更频繁(不规则但始终少于30秒(?
当您 mmap
文件时,您基本上是在过程和内核页面缓存之间直接共享内存 - 同一的缓存持有从磁盘读取的文件数据,或者正在等待写信给磁盘。页面中的页面缓存与磁盘上的内容不同(因为已写入(被称为"肮脏"。
有一个内核线程扫描肮脏的页面,并在多个参数的控制下将它们写回磁盘。一个重要的是dirty_expire_centisecs
。如果文件的任何页面都脏的时间长于dirty_expire_centisecs
,则该文件的所有肮脏页面都将被写出。默认值为3000厘米(30秒(。
另一组变量是dirty_writeback_centisecs
,dirty_background_ratio
和dirty_ratio
。dirty_writeback_centisecs
控制内核线程检查脏页的频率,默认值为500(5秒(。如果肮脏页面的百分比(作为可缓存的存储器的一部分(小于dirty_background_ratio
,则什么也不会发生。如果比dirty_background_ratio
多,那么内核将开始写一些页面到磁盘。最后,如果肮脏页面的百分比超过 dirty_ratio
,则任何试图写入的过程都将阻塞直到肮脏的数据量减少为止。这样可以确保没有界限的不成文数据的数量就无法增加;最终,产生数据的过程比磁盘所能写的要快,它必须放慢以匹配磁盘的步伐。
MTIME如何更新的问题与内核如何首先知道页面很脏的问题有关。对于mmap
,答案是内核将映射的页面设置为只读。这并不意味着您不能写它们,但这意味着您第一次这样做会触发由内核处理的内存管理单元中的例外。例外处理程序(至少((至少(四件事:
- 将页面标记为脏页,以便将其写回。
- 更新文件mtime。
- 将页面标记为读写,以便写入可以成功。
- 跳回程序中写入
mmap
ED页面的指令,这次成功。
因此,当您将数据写入干净的页面时,它会导致MTIME更新,但也会导致页面成为读写,以便进一步写入不会引起异常(或MTIME更新(注意1 。但是,当肮脏的页面被冲入磁盘时,它变得干净,并且再次变得"只读",以便任何进一步写入它都会触发另一个最终的磁盘写入,也将触发另一个MTIME的更新。
现在,有了一些假设,我们可以开始拼凑拼图。
首先,dirty_background_ratio
和dirty_ratio
可能没有发挥作用。如果您的写作节奏足够快以触发背景冲洗,那么您很可能会在所有文件上看到"不规则"行为。
第二,"不规则"文件和" 30秒"文件之间的差异是页面访问模式。我推测,"不规则"文件正在以某种形式的附录模式或循环弹头方式编写,以便您每隔几秒钟开始写入新页面。每当您弄脏以前未经修复的页面时,它都会触发mtime更新。但是,对于显示30秒模式的文件,您只需写入一个页面(也许它们是一页或更小的长度(。在这种情况下,MTIME首次写入更新,然后不再再次通过超过dirty_expire_centisecs
(即30秒(冲洗到磁盘。
注1:从技术上讲,这种行为是错误的。这是不可预测的,但是标准允许一定程度的不可预测性。但是,他们确实要求在文件上或之后的某个时间在文件中或在msync
(如果有(之前或之后。如果页面在将其冲洗到磁盘之前多次将页面写入到磁盘之前多次,这不是发生的事情 - Mtime获得了 first 写入的时间戳。已经对此进行了讨论,但是可以固定的补丁没有被接受。因此,使用mmap
时,MTIME可能会出错。 dirty_expire_centisecs
限制了该错误,但仅部分限制了,因为其他磁盘流量可能会导致齐平的等待,从而将窗口扩展到写入MTIME的窗口。