4

我在 Linux 上使用 XFS,并且有一个内存映射文件,我每秒写入一次。我注意到文件 mtime(由 显示watch ls --full-time)定期但不规则地更改。mtimes 之间的差距似乎在 2 到 20 秒之间,但并不一致。系统上几乎没有其他东西在运行——特别是我只有一个程序写入文件,加上一个读取。

同一个程序更频繁地写入其他一些映射文件,并且它们的 mtime 每 30 秒更改一次。

我没有使用msync()(调用时会更新 mtime)。

我的问题:

  1. 什么更新mtime?
  2. 更新间隔是否可配置?
  3. 为什么有些 mtime 每 30 秒只更新一次,但我写的一些不那么频繁的文件有更新(不规则但总是少于 30 秒)的 mtime?
4

1 回答 1

6

当你mmap创建一个文件时,你基本上是在你的进程和内核的页面缓存之间直接共享内存——同一个缓存保存从磁盘读取的文件数据,或者等待写入磁盘。页面缓存中与磁盘上的页面不同的页面(因为它已被写入)称为“脏”。

有一个内核线程在几个参数的控制下扫描脏页并将它们写回磁盘。一项重要的是dirty_expire_centisecs. 如果某个文件的任何页面的脏页时间超过dirty_expire_centisecs了该文件的所有脏页,则该文件的所有脏页都将被写出。默认值为 3000 厘秒(30 秒)。

另一组变量是dirty_writeback_centisecsdirty_background_ratiodirty_ratiodirty_writeback_centisecs控制内核线程检查脏页的频率,默认为 500(5 秒)。如果脏页的百分比(作为可用于缓存的内存的一部分)小于dirty_background_ratio则什么也不会发生;如果大于dirty_background_ratio,那么内核将开始将一些页面写入磁盘。最后,如果脏页的百分比超过dirty_ratio,那么任何试图写入的进程都会阻塞,直到脏数据量减少。这样可以保证未写入的数据量不会无限制的增加;最终,产生数据的速度超过磁盘写入速度的进程将不得不放慢速度以匹配磁盘的速度。

mtime 如何更新的问题与内核如何首先知道页面是脏的问题有关。在 的情况下mmap,答案是内核将映射的页面设置为只读。这并不意味着您不能编写它们,而是意味着您第一次这样做时,它会触发内存管理单元中的异常,该异常由内核处理。异常处理程序(至少)做了四件事:

  1. 将页面标记为脏,以便将其写回。
  2. 更新文件 mtime。
  3. 将页面标记为可读写,以便写入成功。
  4. 跳回到程序中写入mmaped 页面的指令,这一次成功。

因此,当您将数据写入干净的页面时,它会导致 mtime 更新,但也会导致页面变为可读写,因此进一步的写入不会导致异常(或 mtime 更新)注 1。但是,当脏页被刷新到磁盘时,它变得干净,并且再次变为“只读”,因此任何进一步的写入都会触发另一个最终的磁盘写入,以及另一个 mtime 更新。

所以现在,有了一些假设,我们就可以开始拼凑这个谜题了。

首先,dirty_background_ratio并且dirty_ratio可能不会发挥作用。如果您的写入速度足以触发后台刷新,那么您很可能会在所有文件上看到“不规则”行为。

其次,“不规则”文件和“30 秒”文件之间的区别在于页面访问模式。我推测“不规则”文件正在以某种附加模式或循环缓冲区方式写入,因此您每隔几秒钟就开始写入一个新页面。每次您弄脏以前未触及的页面时,都会触发一次 mtime 更新。但是对于显示 30 秒模式的文件,您只能写入一页(可能它们的长度为一页或更短)。在这种情况下,mtime 会在第一次写入时更新,然后不会再次更新,直到文件超过dirty_expire_centisecs30 秒刷新到磁盘。

注 1:从技术上讲,这种行为是错误的。这是不可预测的,但标准允许某种程度的不可预测性。但它们确实要求 mtime位于最后一次写入文件时或之后,以及msync(如果有的话)之前或之前。如果页面在刷新到磁盘之前的时间间隔内被多次写入,则不会发生这种情况 - mtime 获取第一次写入的时间戳。这已经被讨论过了,但是一个可以修复它的补丁没有被接受。因此,在使用时mmap,mtimes 可能会出错。dirty_expire_centisecs某种程度上限制了该错误,但只是部分限制,因为其他磁盘流量可能导致刷新必须等待,从而延长写入窗口以进一步绕过 mtime。

于 2017-07-02T04:01:31.627 回答