内存平衡

2000年1月由Kanoj Sarcar <kanoj@sgi.com>发起

对于!__GFP_HIGH和!__GFP_KSWAPD_RECLAIM以及非__GFP_IO分配,都需要内存平衡。

调用者避免回收的第一个原因是,由于持有自旋锁或处于中断上下文中,调用者无法睡眠。第二个原因是调用者愿意在不产生页面回收开销的情况下使分配失败。这可能发生在具有order-0后备选项的机会性高阶分配请求中。在这种情况下,调用者可能还希望避免唤醒kswapd。

发出__GFP_IO分配请求是为了防止文件系统死锁。

在没有不可睡眠的分配请求的情况下,进行平衡似乎是不利的。页面回收可以延迟启动,也就是说,只有在需要时(即区域可用内存为0时),而不是使其成为一个主动的过程。

也就是说,内核应该尝试从直接映射池中满足对直接映射页面的请求,而不是退回到dma池,以便保持dma池为dma请求(原子与否)填充。类似的论点适用于highmem和直接映射页面。另一方面,如果有大量空闲dma页面,最好通过从dma池分配一个来满足常规内存请求,而不是产生常规区域平衡的开销。

在2.2中,内存平衡/页面回收只有在空闲页面_总_数低于总内存的1/64时才会启动。使用适当的dma和常规内存比例,很可能即使dma区域完全为空,也不会进行平衡。2.2已经在各种内存大小的生产机器上运行,并且即使存在这个问题,似乎也运行良好。在2.3中,由于HIGHMEM,这个问题更加严重。

在2.3中,区域平衡可以通过两种方式之一完成:根据区域大小(以及可能的较低类区域的大小),我们可以在初始化时决定在平衡任何区域时我们应该追求多少空闲页面。好处是,在平衡时,我们不需要查看较低类区域的大小,坏处是,由于忽略了较低类区域中可能较低的利用率,我们可能会过于频繁地进行平衡。此外,通过对分配例程进行稍微修改,可以将memclass()宏简化为一个简单的相等性。

另一种可能的解决方案是,只有当一个区域_及其_所有较低类区域的空闲内存低于该区域及其较低类区域总内存的1/64时,我们才进行平衡。这解决了2.2的平衡问题,并且尽可能接近2.2的行为。此外,平衡算法在具有不同数量和类型的区域的各种架构上以相同的方式工作。如果我们想要变得花哨,我们可以在未来为不同区域中的空闲页面分配不同的权重。

请注意,如果常规区域的大小与dma区域相比很大,那么在决定是否平衡常规区域时,考虑空闲dma页面就变得不那么重要了。第一个解决方案那时就更具吸引力了。

附加的补丁实现了第二个解决方案。它还“修复”了两个问题:首先,正如2.2中那样,在低内存条件下唤醒kswapd以进行不可睡眠的分配。其次,也平衡HIGHMEM区域,以便为replace_with_highmem()获得HIGHMEM页面提供一个机会,并确保HIGHMEM分配不会退回到常规区域。这也确保了HIGHMEM页面不会泄漏(例如,在HIGHMEM页面在交换缓存中但未被任何人使用的情况下)

kswapd还需要知道它应该平衡的区域。kswapd主要是在无法进行平衡的情况下需要的,可能是因为所有分配请求都来自intr上下文,并且所有进程上下文都在休眠。对于2.3,kswapd实际上不需要平衡highmem区域,因为intr上下文不请求highmem页面。kswapd查看区域结构中的zone_wake_kswapd字段以决定是否需要平衡区域。

如果从进程内存和shm窃取页面会缓解页面节点中任何已低于其水印的区域的内存压力,则会执行页面窃取。

watermark[WMARK_MIN/WMARK_LOW/WMARK_HIGH]/low_on_memory/zone_wake_kswapd:这些是每个区域的字段,用于确定何时需要平衡区域。当页面数量低于watermark[WMARK_MIN]时,滞后字段low_on_memory将被设置。这会一直保持设置状态,直到空闲页面数变为watermark[WMARK_HIGH]。当设置了low_on_memory时,页面分配请求将尝试释放该区域中的一些页面(前提是在请求中设置了GFP_WAIT)。与此正交的是,决定触发kswapd以释放一些区域页面。该决定不是基于滞后的,而是在空闲页面数低于watermark[WMARK_LOW]时完成的;在这种情况下,zone_wake_kswapd也会被设置。

我听到的(好的)想法

  1. 动态经验应该影响平衡:可以跟踪区域的失败请求数并将其馈送到平衡方案中(jalvo@mbay.net

  2. 实现一个类似replace_with_highmem()的replace_with_regular()来保留dma页面。(lkd@tantalophile.demon.co.uk