8

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

std::memset(ptr, 0, length)如果页面尚未在内存中,即使整个页面都被覆盖,这样做也会导致从磁盘读取页面,从而完全破坏磁盘性能。

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

可惜MADV_ZERO不存在。尽管相应的标志FALLOC_FL_ZERO_RANGE确实存在fallocate并且可以用于fwrite实现类似的效果,但没有即时的跨进程一致性。

我猜一种可能的选择是使用MADV_REMOVE. 但是,据我了解,这可能会导致文件碎片化,并且还会在完成时阻止其他操作,这使我不确定其长期性能影响。我对 Windows 的体验是,类似的FSCTL_SET_ZERO_DATA命令在调用时会导致显着的性能峰值。

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

1./dev/zero/

我读过它被建议简单地/dev/zero入选定的范围。虽然我不太确定“读入范围”是什么意思以及如何去做。它就像fread/dev/zero内存范围内一样吗?不确定如何避免访问时出现常规页面错误?

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

如果通常这样做太难实现,我
建议 MADV_ZERO 应该有这样的效果:就像
将 /dev/zero 读入范围一样,但总是有效的。

编辑:进一步跟踪线程,事实证明它实际上不起作用。

当您处理共享映射时,它不会做任何技巧。

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;
}
4

1 回答 1

1

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

您可能想用稀疏文件中的文件孔(但这不是您想要的)替换文件段,但某些文件系统(例如 VFAT)没有孔或稀疏文件。参见lseek(2)SEEK_HOLE, ftruncate(2)

于 2015-09-01T08:59:06.280 回答