假设我想对可能非常大的文件执行顺序写入。
如果我在整个区域上mmap()一个巨大的区域和madvise(MADV_SEQUENTIAL),那么我可以以相对有效的方式写入内存。我已经开始工作了。
现在,为了在我写作时释放各种OS资源,我偶尔会在已经写入的小块内存上执行munmap()。我担心的是munmap()和msync()会阻塞我的线程,等待数据物理地提交到磁盘。我根本不能放慢作家的速度,所以我需要找到另一种方式。
在已经写好的小块内存上使用madvise(MADV_DONTNEED)会更好吗?我想告诉操作系统懒惰地将内存写入磁盘,而不是阻止我的调用线程。
madvise()的联机帮助页有这样说,这是相当模糊的:
MADV_DONTNEED
Do not expect access in the near future. (For the time being, the
application is finished with the given range, so the kernel can free
resources associated with it.) Subsequent accesses of pages in this
range will succeed, but will result either in re-loading of the memory
contents from the underlying mapped file (see mmap(2)) or
zero-fill-on-demand pages for mappings without an underlying file.
答案 0 :(得分:14)
为了你自己的利益,远离MADV_DONTNEED
。 Linux将不将其作为一个提示,在写完之后将页面丢弃,但要立即将它们丢弃。这不是一个错误,而是一个慎重的决定。
具有讽刺意味的是,推理是MADV_DONTNEED
已经给出了非破坏性msync(MS_INVALIDATE|MS_ASYNC)
的功能,另一方面MS_ASYNC
没有启动I / O(事实上,它完全没有做任何事情,按照脏页回写工作正常的原因),fsync
总是阻止,sync_file_range
可能阻止如果你超过一些模糊的限制并被考虑无论这意味着什么,文件都“非常危险”。
无论哪种方式,您必须msync(MS_SYNC)
或fsync
(两种阻止)或sync_file_range
(可能阻止),然后fsync
,或将< / strong>使用MADV_DONTNEED
丢失数据。如果你不能阻止,你可能别无选择,但是要在另一个线程中这样做。
答案 1 :(得分:0)
首先,madv_sequential启用积极的预读,因此您不需要它。 第二,即使你什么都不做,os也懒得把脏文件烘焙的内存写到磁盘上。但是madv_dontneed会指示它立即释放内存(你称之为“各种os资源”)。第三,不清楚顺序写入的mmapping文件是否有任何优势。只需写(2)(但使用缓冲区 - 手动或stdio),你可能会得到更好的服务。
答案 2 :(得分:0)
对于最近的 Linux 内核(刚刚在 Linux 5.4 上测试),当映射非私有时,MADV_DONTNEED
会按预期工作,例如mmap
没有 MAP_PRIVATE
标志。我不确定以前版本的 Linux 内核的行为是什么。
来自 Linux 手册页项目的 madvise
手册页 4.15 版:
MADV_DONTNEED
操作成功后,指定区域内存访问的语义发生变化:该范围内页面的后续访问将成功,但会导致从上重新填充内存内容- 底层映射文件的最新内容(用于共享文件映射、共享匿名映射和基于 shmem 的技术,例如 System V 共享内存段) 或匿名私有映射的按需零填充页面.
Linux 在 Linux 4.5 中添加了一个与 BSD 系统中行为相同的新标志 MADV_FREE
它只是在需要时将页面标记为可释放,但不会立即释放它们,从而可以重用内存范围而不会产生再次损坏页面的成本。
有关为什么 MADV_DONTNEED
用于私有映射可能会在将来访问时导致页面填充为零的原因,请观看 @Damon 回答的评论中提到的 Bryan Cantrill's rant。剧透:它来自 Tru64 UNIX。
答案 3 :(得分:0)
如前所述,MADV_DONTNEED
不是您的朋友。从 Linux 5.4 开始,您可以使用 MADV_COLD
告诉内核它应该在有内存压力时换出该内存。在这种情况下,这似乎正是我们想要的。
在此处阅读更多信息: https://lwn.net/Articles/793462/