【问题标题】:How to implement or emulate MADV_ZERO?如何实现或模拟 MADV_ZERO?
【发布时间】:2015-11-26 02:23:08
【问题描述】:

我希望能够在不调用任何 io 的情况下将文件内存映射范围归零(以便有效地顺序覆盖大文件而不会导致任何磁盘读取 io)。

执行std::memset(ptr, 0, length) 将导致从磁盘中读取尚未在内存中的页面,即使整个页面都被覆盖,从而完全破坏磁盘性能。

我希望能够执行madvise(ptr, length, MADV_ZERO) 之类的操作,它将范围归零(类似于FALLOC_FL_ZERO_RANGE),以便在访问指定范围时导致零填充页面错误而不是常规的io 页面错误。

很遗憾MADV_ZERO 不存在。尽管fallocate 中确实存在相应的标志FALLOC_FL_ZERO_RANGE,并且可以与fwrite 一起使用以达到类似的效果,但没有即时的跨进程一致性。

我猜一个可能的替代方案是使用MADV_REMOVE。但是,据我了解,这可能会导致文件碎片并在完成时阻止其他操作,这使我不确定其长期性能影响。我使用 Windows 的经验是,类似的 FSCTL_SET_ZERO_DATA 命令在调用时会导致显着的性能峰值。

我的问题是如何为共享映射实现或模拟MADV_ZERO,最好是在用户模式下?

1。 /dev/zero/

我已将其读取为 suggested,以 只需将 /dev/zero 读入所选范围。虽然我不太确定“读入范围”是什么意思以及如何去做。是不是就像从/dev/zero 进入内存范围的fread?不确定如何避免访问时出现常规页面错误?

对于 Linux,只需将 /dev/zero 读入所选范围即可。这 内核已经针对匿名映射优化了这种情况。

如果一般来说执行起来太难了,我
建议 MADV_ZERO 应该有这样的效果:和阅读一模一样
/dev/zero 进入范围,但总是有效的。

编辑:跟随线程有点further 事实证明它实际上不起作用。

当您处理共享映射时,它不会起到任何作用。

2。 MADV_REMOVE

在 Linux 中实现它的一种猜测(即不在我更喜欢的用户应用程序中)可以通过简单地复制和修改 MADV_REMOVE,即 madvise_remove 以使用 FALLOC_FL_ZERO_RANGE 而不是 FALLOC_FL_PUNCH_HOLE。虽然我猜这个有点过头了,特别是因为我不太明白 vfs_allocate 周围的代码在做什么:

// madvice.c
static long madvise_remove(...)
  ...
  /*
   * Filesystem's fallocate may need to take i_mutex.  We need to
   * explicitly grab a reference because the vma (and hence the
   * vma's reference to the file) can go away as soon as we drop
   * mmap_sem.
   */
  get_file(f); // Increment ref count.
  up_read(&current->mm->mmap_sem); // Release a read lock? Why?
  error = vfs_fallocate(f,
            FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, // FALLOC_FL_ZERO_RANGE?
            offset, end - start);
  fput(f); // Decrement ref count.
  down_read(&current->mm->mmap_sem); // Acquire read lock. Why?
  return error;
}

【问题讨论】:

  • 很可能“只需将/dev/zero读入选定范围”指的是following technique seen in shmem_zero_setup() within the Linux kernel
  • @TheCodeArtist:不太清楚该怎么做...
  • do_mmap_pgoff()flags = MAP_ANONYMOUS | MAP_SHARED | MAP_NORESERVE 开始,然后按照代码路径直到shmem_zero_setup() 以获得完整的图片。对于匿名映射,在没有文件支持的情况下,最初使用零页来优化读取。当然,这不是解决您问题的方法。它只是一个正确实现的示例,如果您想自己在内核中实现建议,可以参考它。
  • 另一方面,如果你要将固定模式的块写入磁盘(即长零序列),为什么不打开O_DIRECT | O_WRONLYlseek() 中的文件文件到适当的偏移量并简单地转储大块(磁盘块大小的倍数),直到 len 字节数被清零。显然这是works with a couple of alignment and offset restrictions on mmap()。你怎么看?...
  • 持久的IPC带回了一些昔日的美好回忆。我以完全相同的意图在玩 mmap-ed 文件。我使用 inotify 来监控 文件支持的“共享内存”。因为没有equivalent to sync() that i could call to trigger a "inotification", i wrote one。 :P 在我的设计中,writer 线程将更新内存映射共享文件中的所有相关字段,最后触发一个msync()reader 线程在该msync() 上( s) 使用 inotify 等待将被解除阻塞。希望对您有所帮助...

标签: c linux shared-memory mmap fallocate


【解决方案1】:

你可能无法做你想做的事(在用户空间,不破解内核)。请注意,由于 page cache,写入零页可能不会产生物理磁盘 IO。

您可能希望用 sparse file 中的文件漏洞(但这不是您想要的)替换文件段,但某些文件系统(例如 VFAT)没有漏洞或稀疏文件。见lseek(2)SEEK_HOLEftruncate(2)

【讨论】:

  • 是的,这就是MADV_REMOVE 所做的。但是,正如我们都指出的那样,它会导致文件变得稀疏,并且在完成时打孔会锁定所有其他操作的文件(例如,Windows 非常慢,尚未在 Linux 上进行测试)。这让我对它的长期性能影响持怀疑态度。我需要这个的用例是 24/7 写入,没有任何时间进行碎片整理。
  • 我相信您实际上对文件/页面缓存也有同样的问题。由于页面缓存本身使用内存映射部分,但边界更大。不过不确定。
猜你喜欢
  • 2022-01-22
  • 2018-01-14
  • 2017-07-07
  • 2011-01-24
  • 2018-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多