写入内存映射文件后更新mtime的内容是什么?

Joh*_*nck 4 linux mmap memory-mapped-files xfs

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

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

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

我的问题:

  1. 什么更新mtime?
  2. 更新间隔是否可配置?
  3. 为什么有些mtimes每30秒才更新一次,但是我写的频率较低的一些文件有更新鲜(不规则但总是不到30秒)的时间?

hob*_*bbs 5

当您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更新)note 1。但是,当脏页刷新到磁盘时,它变得干净,并且再次变为“只读”,因此对其进行任何进一步的写入将触发另一个最终的磁盘写入以及另一个mtime更新。

因此,现在,基于一些假设,我们可以开始解决这个难题。

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

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

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