mmap/mprotect-readonly 零页是否计入提交的内存?
我想在进程中保留虚拟地址空间,用于以前使用过但目前不需要的内存。我对主机内核是 Linux 的情况感兴趣,它被配置为防止过度使用(它通过详细计算所有提交的内存来实现)。
如果我只是想防止我的应用程序不再使用的数据占用物理内存或交换到磁盘(无论哪种方式都浪费资源),我可以madvise
内核认为它不再需要,或者mmap
在其之上添加新的零页。但这些方法都不一定会减少算作已提交的内存量,从而阻止其他进程使用这些内存量。
如果我用标记为只读的新零页替换这些页会怎样?我的意图是它们不计入提交的内存,而且我稍后可以使用 mprotect 使它们可写,并且如果使它们可写超出提交的内存限制,则会失败。我的理解正确吗?这行得通吗?
I want to keep virtual address space reserved in my process for memory that was previously used but is not presently needed. I'm interested in the situation where the host kernel is Linux and it's configured to prevent overcommit (which it does by detailed accounting for all committed memory).
If I just want to prevent the data that my application is no longer using from occupying physical memory or getting swapped to disk (wasting resources either way), I can madvise
the kernel that it's unneeded, or mmap
new zero pages over top of it. But neither of these approaches will necessarily reduce the amount of memory that counts as committed, which other processes are then prevented from using.
What if I replace the pages with fresh zero pages that are marked read-only? My intent is that they don't count towards committed memory, and further that I can later use mprotect
to make them writable, and that it would fail if making them writable would go over the committed memory limit. Is my understanding correct? Will this work?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果您不使用该页面(读取或写入该页面),则该页面不会提交到您的地址空间(仅保留)。
但你的地址空间是有限的,所以你不能随心所欲地玩它。
例如,请参阅 ElectricFence,由于插入“空页/保护页”(无法访问的匿名内存),大量分配可能会失败。
看看这些线程:“mprotect() 失败:无法分配内存”:
http://thread.gmane.org/gmane.comp .lib.glibc.user/538/focus=976052
If you're not using the page (reading or writing to it), it won't be commited to your address space (only reserved).
But your address space is limited, so you can't play as you want/like with it.
See for example ElectricFence which may fail for large number of allocations, because of insertion of "nul page/guard page" (anonymous memory with no access).
Have a look at these thread : "mprotect() failed: Cannot allocate memory" :
http://thread.gmane.org/gmane.comp.lib.glibc.user/538/focus=976052
在 Linux 上,假设尚未禁用过度使用,您可以对
mmap
使用MAP_NORESERVE
标志,这将确保有问题的页面不会被视为在之前分配的内存正在被访问。如果过度使用已完全禁用,请参阅下面有关多重映射页面的信息。请注意,Linux 对于零页的行为在过去有时会发生变化;对于某些内核版本,只需读取页面就会导致其被分配。对于其他人来说,写是必要的。请注意,保护标志不会直接导致分配;但是它们可以防止您意外触发分配。因此,为了获得最可靠的结果,您应该避免通过使用
PROT_NONE
进行 mprotect 来访问该页面。作为另一个更便携的选项,您可以在多个位置映射同一页面。也就是说,创建并打开一个空临时文件,取消链接,
ftruncate
到一些合理的页面数,然后在文件的偏移量 0 处重复mmap
。这绝对可以保证内存只对程序的内存使用量计数一次。您甚至可以在写入页面时使用 MAP_PRIVATE 自动重新分配它。然而,这可能比 MAP_NORESERVE 技术(对于内核跟踪数据和临时文件本身的页面)具有更高的内存使用量,因此我建议使用 MAP_NORESERVE 相反,当可用时。如果您确实使用此技术,请尝试使映射的区域相当大(如果在 Linux 上,请将其放入
/dev/shm
中,以避免实际的磁盘 IO)。每个单独的 mmap 调用都会消耗一定量的(不可交换的)内核内存来跟踪它,因此最好保持该计数。On Linux, assuming overcommit has not been disabled, you can use the
MAP_NORESERVE
flag tommap
, which will ensure that the page in question will not be accounted as allocated memory prior to being accessed. If overcommit has been completely disabled, see below about multiple-mapping pages.Note that Linux's behavior for zero pages has changed at times in the past; with some kernel versions, simply reading the page would cause it to be allocated. With others, a write is necessary. Note that the protection flags do not cause allocation directly; however they can prevent you from accidentally triggering an allocation. Therefore, for most reliable results you should avoid accessing the page at all by
mprotect
ing withPROT_NONE
.As another, more portable option, you can map the same page at multiple locations. That is, create and open an empty temp file, unlink it,
ftruncate
to some reasonable number of pages, thenmmap
repeatedly at offset 0 into the file. This will absolutely guarantee the memory only counts once against your program's memory usage. You can even useMAP_PRIVATE
to auto-reallocate it when you write to the page.This may have higher memory usage than the
MAP_NORESERVE
technique (both for kernel tracking data, and for the pages of the temp file itself), however, so I would recommend usingMAP_NORESERVE
instead when available. If you do use this technique, try to make the region being mapped reasonably large (and put it in/dev/shm
if on Linux, to avoid actual disk IO). Each individualmmap
call will consume a certain amount of (non-swappable) kernel memory to track it, so it's good to keep that count down.