如果我只想防止应用程序不再使用的数据占用物理内存或被交换到磁盘上(无论哪种方式都浪费资源),我可以向内核发出“madvise”指令,表示这些数据不再需要,或者用新的零页覆盖它们。但这两种方法都不一定会减少计入已提交内存的内存量,这会阻止其他进程使用该内存。
如果我用新的零页替换这些页面,并将其标记为只读,那么我的意图是它们不计入已提交内存,并且我稍后可以使用“mprotect”将其设置为可写,如果将其设置为可写会超过已提交内存限制,则会失败。我的理解正确吗?这样做可行吗?
如果您没有使用页面(读取或写入它),它将不会提交到您的地址空间(仅保留)。
但是您的地址空间是有限的,因此您不能随心所欲地使用它。
例如,ElectricFence可能在分配大量内存时失败,因为插入了“nul页面/保护页面”(无访问权限的匿名内存)。请参阅以下线程:“mprotect() failed: Cannot allocate memory”: http://thread.gmane.org/gmane.comp.lib.glibc.user/538/focus=976052
在Linux上,假设overcommit没有被禁用,您可以使用MAP_NORESERVE
标志来使用mmap
,这将确保所涉及的页面在被访问之前不会被视为已分配的内存。如果overcommit已完全禁用,请参见下面有关多映射页面的内容。
请注意,Linux对零页面的行为在过去的某些内核版本中发生了变化;在某些版本中,仅读取页面就会导致其被分配。在其他情况下,需要进行写操作。请注意,保护标志不会直接导致分配;但是它们可以防止您意外触发分配。因此,为了获得最可靠的结果,您应该通过使用PROT_NONE
进行mprotect
以避免访问页面。
作为另一种更便携的选择,您可以在多个位置映射相同的页面。也就是说,创建并打开一个空的临时文件,取消链接它,ftruncate
到一些合理的页面数,然后在文件的偏移量0处重复地mmap
。这将绝对保证内存只计算一次,不会影响程序的内存使用情况。您甚至可以使用MAP_PRIVATE
在写入页面时自动重新分配它。
使用这种技术,可能会比MAP_NORESERVE
技术占用更多的内存(无论是内核跟踪数据还是临时文件本身的页面),因此,我建议在有条件的情况下使用MAP_NORESERVE
。 如果您确实使用了这种技术,请尝试使要映射的区域合理大(如果在Linux上,请将其放置在/dev/shm
中以避免实际磁盘I/O)。每个单独的mmap
调用都将消耗一定数量的(不可交换)内核内存来跟踪它,因此最好将该计数保持在较低水平。
MAP_NORESERVE
不是很有用。当完全禁用 overcommit 时,它会被忽略。 - R.. GitHub STOP HELPING ICE/dev/zero
映射为临时文件会像临时文件一样有效吗? - Zan Lynx/dev/zero
映射和普通的MAP_ANON
映射一样,因此将像任何其他内存分配一样被计算。 - bdonlan
mmap
(随机地)分配相同的虚拟地址,那么会发生糟糕的事情。:-)至于MAP_NORESERVE
,我担心即使我稍后将页面设置为可写,也可能导致页面未被计数。我想我可以用新的零页面再次进行mmap
覆盖它们。 - R.. GitHub STOP HELPING ICE