我正在阅读Linux 编程接口。
49.9 MAP_NORESERVE 和交换空间过度使用
一些应用程序创建大型(通常是私有匿名)映射,但只使用映射区域的一小部分。例如,某些类型的科学应用程序分配一个非常大的数组,但只对数组中几个相隔很远的元素(所谓的稀疏数组)进行操作。
如果内核总是为整个此类映射分配(或保留)足够的交换空间,那么可能会浪费大量的交换空间。相反,内核可以仅在实际需要时(即,当应用程序访问页面时)为映射的页面保留交换空间。这种方法称为惰性交换保留,其优点是应用程序使用的总虚拟内存可以超过 RAM 加上交换空间的总大小。
换句话说,惰性交换保留允许过度使用交换空间。只要所有进程不尝试访问其映射的整个范围,这就可以正常工作。...
据我所知,交换空间是磁盘中的一块空间,保留用于内存交换。当内存中的这些页面处于非活动状态时,它们会被交换到磁盘中的交换空间中。它就像内存/内存的二级缓存。
那么这个惰性交换预留机制到底是什么鬼?
让我用一个例子来说明我的困惑。
一些应用程序创建大型(通常是私有匿名)映射......
好的,然后假设我malloc
有一个大数组16384(4096*4)
字节(创建大型(通常是私有匿名)映射),并且只对数组中几个广泛分离的元素进行操作。
然后一些非活动页面被交换到交换空间,对吗?假设0-4095(4096B)
,8192-12287(4096B)
在内存中,所有其他非活动页面4096-8191(4096B)
,12288-16383(4096B)
被交换到交换空间中。
那么这句话是什么意思:
相反,内核可以仅在实际需要时(即,当应用程序访问页面时)为映射的页面保留交换空间。
如果不留在交换空间4096-8191(4096B)
中,这些非活动页面(和)还能留在哪里?文本似乎表明交换空间存在 3 级缓存。 12288-16383(4096B)
memory -> swap space (disk) -> ????
Swap 并不是真正的内存二级缓存。它是内存的几个后备存储之一。当内核需要分配一页物理内存,但没有足够的空闲内存时,需要驱逐另一页;只有当被驱逐页面的内容是可丢弃的,或者可以从其他地方恢复时,它才能做到这一点。其他地方是后备存储:它可以是磁盘上的文件(例如,用于可执行文件或映射文件),或者某些交换区域。
当内存记帐跟踪过度使用时,交换保留开始发挥作用(参见LPI中的表 49-4 )。当不允许过度使用时,内核需要在分配时确定是否可以进行分配。对于私有可写映射和共享匿名映射,这意味着它必须有足够的地址空间,以及足够的交换空间(这样内核才能保证映射内存的内容可以写入那里,从而保证写入到映射内存永远不会导致
SIGSEGV
)。过度使用需要惰性交换保留:这意味着内核可以分配交换支持的内存映射,而无需保留相应的交换空间。正如LPI中提到的,这允许程序分配比实际可用更多的内存,并且应该使用
MAP_NORESERVE
. 只有在写入页面时才会发生保留,这意味着写入可能会失败SIGSEGV
或导致 OOM 杀手介入。这对于比您的 16KiB 示例更大的分配变得重要。想象一下,您想要一个 64GiB、262,144×262,144 的稀疏数组,以使您的程序更易于编写:在严格保留的情况下,您需要拥有所有可用的内存;没有严格的保留,你不会,只有你写的页面才会被实际分配。
请注意,这都是 Linux 特定的,并且与所选的系统过度使用策略 ( ) 紧密相关
/proc/sys/vm/overcommit_memory
:在模式 1(总是过度使用)和 2(从不过度使用)中,MAP_NORESERVE
不会改变任何东西,它只会在模式 0 中起作用。一块什么样的空间?虚拟内存。
内核很容易为每个进程提供所需的虚拟内存。那是懒惰的,过度使用的部分。稀疏科学数组的例子想表明,偷懒通常是个好主意。整个数组不会一次使用,因此可以同时运行其他程序。
但大多数时候(没有专用交换设备)并不比“磁盘”快
恰恰相反,尤其是在过度使用的情况下:
交换将(物理)RAM 变成存储设备的缓存。
(即 RAM 不是进程拥有和持有的东西,但它是每个进程的活动页面的工作区域,包括映射文件)。
这里是关于过度使用交换空间,而不仅仅是 RAM。无论是否使用交换,都可能导致过度使用后出现 OOM 的情况。