我希望能够在不调用任何 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(¤t->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(¤t->mm->mmap_sem); // Acquire read lock. Why?
return error;
}