http://lwn.net/Articles/447405/node
June 14, 2011linux
分配大块物理上连续内存的问题一直在被讨论。虚拟内存,天生的在系统上分散使用内存页,内核运行一下子就只剩下不多的连续空闲的页。多年以来,内核开发者处理这个问题的方法是尽可能避免对大的连续物理页分配的依赖。只有至关少的内核代码会尝试分配多于两个物理连续页。算法
最近,对大的连续页分配的需求持续增加。一个是大页和THP,另外一个是有新麻烦的旧事:不能执行分散/汇集DMA操做的硬件。任何只能对物理连续区域作DMA的设备(没有IOMMU)须要一个物理连续的缓存。这种需求通常出如今相对来讲比较低端的硬件,有人可能但愿这种硬件会随着时间流逝愈来愈少。但咱们如今看到的倒是在得到能力提高的同时仍有连续DMA缓存需求的设备。举个例子,能获取全高清数据的视频捕捉引擎能执行不少转换,但它仍须要一个连续的缓存来保存结果。高清视频的出现加剧了这个问题--这些物理连续的缓存比之前更大也更难分配了。
大约一年之前,LWN发表过想要解决这个问题的CMA(contiguous memory allocator)补丁。这个补丁仍然遵循了传统的为知足大的分配请求的特殊目的而在启动的时候保留一部份内存的方法。几年之前,这种技术就已经被用在“bigphysarea”补丁中了,它简单的传给内核“mem=”启动参数来保留一部分物理内存不被使用。安卓的pmem驱动也是从一个保留的区域里分配内存。近20年的经验证实这种方法是可行的。不足是保留的内存就不能再被其余地方使用了;若是设备没有使用这部份内存,它就保持闲置。这种浪费愈来愈不被内核开发者,还有用户所欢迎。
由于这个和其余的缘由,CMA补丁未被合并。可是问题还没有解决,而如今也没人继续研究这两种方法。CMA补丁的最新版看起来有点不一样,尽管还有一些问题须要解决,这个补丁仍是有更大的机会合入主线的。
CMA分配器仍然能够工做在一个保留的内存区域,但很明显这不是你们想要的操做模式。做为代替,新的CMA尝试维护内存区域,当有需求的时候连续的内存就会被建立。为实现这个目的,CMA依赖于内存管理代码中的“迁移类型”机制。
每个区里,页块中的页被标记为(或非)可移动的或可重声明的。可移动页通常是指页缓存或匿名页,它们经过页表和页缓存基树来被访问。只要页表和基树作相应的改动,这些页的内容就能够被移到其余地方。可重声明页是指可能会按需返回给内核的页,它们保存数据结构好比inode缓存。不可移动页通常是内核直接指向的页,好比,经过kmalloc()得到的内存就不能在不破坏数据的状况下被移动。
内存管理子系统会尽可能把可移动页放在一块儿。(由于)若是想经过移动页来释放大块内存,只须要一个不可移动的页就能够摧毁全部的努力。经过把可移动页聚在一块儿,内核但愿能按要求释放大块区域从而避免了这种问题。内存压缩代码依赖这些可移动页区域来工做。
CMA经过增长一个新的“CMA”的迁移类型来扩展这种机制,它就像“movable”类型,但有两点不一样。“CMA”类型是粘贴的,被标记为CMA的页就不会再被内核改变了。内存分配器再也不从CMA区域分配不可移动页,而且只有在其它方法不可用的状况下才会为其余使用来分配CMA页。因此,被标记为用做CMA的内存区域只包含可移动页,而且有相对较多的空闲页。
换句话说,被标记用做CMA的内存仍然能够被系统的其余部分使用,但有个限制是它只能包含可移动页。当一个驱动须要连续的内存时,CMA分配器会尝试从其中一个内存区域中分配足够的页来建立适当大小的连续缓存。若是那些内存区域的页是可移动的(现实中有时不会知足),驱动就会得到它须要的缓存。但这个缓存不须要时,内存能够被用做其余目的。
有人可能会疑惑为什么须要这种机制,内存压缩已经能够为THP建立大块物理连续的内存了。答案是DMA缓存有一些跟大页不一样的需求。它们可能更大,举个例子,大多数架构上THP是2MB,而DMA缓存能达到10MB或更多。也有可能存在只能映射DMA缓存到特定内存范围的奇葩硬件--CMA的开发者Marek Szyprowski貌似就有这种硬件。最后,一个2MB的大页必须2MB对齐,而DMA缓存的对齐要求则更宽松一些。CMA分配器能够得到正好是须要的数量的内存(不用像伙伴分配器那样扩展到下一个2的幂的大小)而不用担忧过分严格的对齐要求。
CMA补丁提供了一些函数以方便建立内存区域和为特定范围的内存创建“内容/上下文”。而后有cm_alloc()和cm_free()来获取和释放缓存。可是,设备驱动最好不要直接调用CMA,而是把感知CMA集成到DMA函数里。当一个驱动调用好比dma_alloc_coherent()函数,CMA会被自动使用来知足要求。大多数状况下,这就能够知足需求了。
剩余的关于CMA的疑问中的一个是关于如何在第一时间建立特殊区域。当前的方案是但愿在系统的板文件(board file:系统相关的设置)中加入一些特殊的调用,这很像ARM的作事方法。社区的目的是再也不使用板文件,因此得找其它方法。就像Arnd Bergmann指出的,把这些信息移到设备树也不是一个选项;这其实就是一个策略的问题。Arnd正在推进能够在大多数系统上设置一些合理的默认设置的方法,特殊挑战的奇葩系统的修复能够随后加上。
最终的结果是这个补丁进入主线以前起码还得再有一轮迭代。可是CMA知足了一个真实的需求。这些代码有潜力使物理连续内存分配更加可靠,同时最小化对系统其它部分的影响。看起来值得拥有。编程
http://lwn.net/Articles/368869/数组
January 6, 2010缓存
长期存在的内存碎片化问题被讨论过好几回了。简短来讲:随着系统运行,各个用户之间的页趋于分散,致使找到成组的物理连续页愈来愈难。社区在尽量的避免高阶(多页)内存分配的需求方面作了大量工做,历来防止大部份内核功能被页碎片化破坏。但仍然存在须要高阶分配需求的存在,须要这种分配的代码在碎片化的系统上会失败。
值得注意的是,某种程度上,这个问题事实上正变得愈来愈糟糕。当前的处理器并不局限于4K大小的页,它们能够在一个进程的一部分地址空间中使用更大的页(大页)。使用大页能够带来性能优点,这是由减小处理器TLB压力来得到的。可是使用大页须要系统能够提供物理连续的内存区域,这些内存不但要足够大并且还要适当的对齐。在运行了一段时间的系统上找到这种空间具备至关大的挑战。
几年以来,内核开发者为减缓这个问题作了大量努力,带来了“ZONE_MOVABLE”和“lumpy reclaim”技术。可是还有更多的能够作,特别是在修复碎片化来恢复更大的大块内存方面。在这方面沉寂了一段时间后,Mel Gorman最近带着实现了内存压缩的补丁归来了。咱们大致看一下这个补丁是如何工做的。
想象这样一个很小的内存区:服务器
这里,白色的页是空闲的,红色的已经被分配使用了。就像咱们看到的,这个区已经碎片化了,从这个区中分配一个4个页的块会失败。事实上,甚至两个页的分配也会失败,由于没有一对空闲页是适当对齐的。
是时候调用内存压缩代码了。这些代码经过两个独立的算法来工做:第一个算法从区的底部开始并建立一个可移动的已分配页的链表:网络
同时,在区的顶部,另外一个算法建立一个可被页迁移使用的空闲页链表:数据结构
最终在朝着区的中间的方向上会相遇。剩下的就是在相遇点上调用页迁移代码(再也不只是用在NUMA系统上了)把已使用的页移到区顶部的空闲区中,产生像这样的一个图:架构
如今咱们有了一个好的八页的连续的空闲页,能够用来知足高阶分配的要求。
固然,这里的图片相对于现实中发生的已经作过简化了。一开始,内存区会更大,那意味着有更多的工做须要作,但产生的空闲区域也会更大。
但全部的这些只有在有问题的页是可移动的才能工做。不是全部的页都会按照但愿的能够移动,只有那些经过一个间接的层来寻址和未被钉住的页才能够移动。因此,大部分用户空间页--经过用户虚拟地址来访问--能够移动,所须要作的是对相关的页表项作相应的调整。大部分被内核直接使用的内存是不能移动的--但一些是能够重声明的,意味着它们能够按需被释放。只要一个不可移动的页就能够毁坏一个连续的内存段。好消息是内核已经把可移动的和不可移动的页分开了,因此,事实上,不可移动页的问题比想象中的要小。
内存压缩算法能够经过二者之一来触发。一是往/proc/sys/vm/compact_node(CentOS 6.5上是一个只写的compact_memory)中写入节点号,就会在指定的NUMA节点上调用内存压缩。另外一个是在分配高阶页失败时,这种状况下,相对于经过直接重声明来释放页,内存压缩会被优先使用。若是没有明确的触发,内存压缩不会被调用,移动页会有消耗,因此在不须要的时候最好避免使用。
Mel运行了一些简单的测试显示,在内存压缩使能的状况下,他能够把超过90%的系统内存分配成大页同时减小重声明活动的数量。因此这看起来是有用的工做。可是这是内存管理代码,因此合入主线的时间很差提早预计。
http://lwn.net/Articles/157066/
October 25, 2005
NUMA系统从设计上就有相对于特定节点(一组处理器)的本地内存。尽管全部的内存都是可访问的,可是本地内存工做起来比远程内存更快。内核考虑到NUMA的这种行为会尽可能为进程分配本地内存,同时尽可能避免在节点间移动进程。可是有时进程必须移动,从而致使本地分配优化反而变成了坏的。在这种状况下,若是在进程被移到了一个新的节点时,进程的内存也能够随之移动那就行了。
内存迁移补丁已经存在一段时间了。最新版是Christoph Lameter发布的。这个补丁其实并无解决整个问题,不过它志在创建一个足够最终的彻底的迁移方案被引入的基础架构。
这个补丁不会自动的为被移动的进程迁移内存,而是把迁移的决定留给用户空间。一个新的系统调用:
long migrate_pages(pid_t pid, unsigned long maxnode, unsigned long *old_nodes, unsigned long *new_nodes);
这个调用会试图把任何属于给定进程的页从old_nodes移到new_nodes。同时也有一个新的MPOL_MF_MOVE选项给set_mempolicy()系统调用,这会起到相同的左右。任何一种方法,用户空间均可以请求为给定进程释放一批节点。这种操做能够被执行以响应一个明确的进程移动自己(举个例子,被一个系统调度守护进程移动),或响应其余事件,好比一个节点即将关闭和移除。
目前实现还比较简单:代码会重复扫描进程的内存并试图强制每一个须要迁移的页交换出去。当进程在页上发生错误,而后相应页就会被分配到目前进程所在的节点。强制换出进程实际上执行了一些流程,开始它略过被锁定的页只关注那些能够容易释放的页。而后它等待被锁定的页并把最后的页换出内存。
经过换出设备迁移页不是在NUMA系统上最有效的方法。补丁的后期目标是增长直接节点到节点迁移,固然还有其余特性。可是同时,开发者想把这个补丁合入2.6.15.可是Andrew Morton发表了一些保留意见:他想看到一个关于这个补丁如何可靠工做的解释。有不少东西会阻止页迁移,包括被用户空间锁住的页,执行直接IO的页,还有更多。Christoph响应说补丁最终会解决这些问题。这个声明是否能说服从而合入2.6.15还有待观察。
http://lwn.net/Articles/160201/
November 8, 2005
页迁移是把进程的页从系统的一部分移到另外一部分。通常,在NUMA节点间移动页的动机是但愿提高性能。上次提到它是经过强制目标页换出到swap设备工做的。当相应的进程在页上发生错误,这些页就会被换回到但愿的节点上。这种技术能够工做但不是最优的:避免把页写到磁盘而后再读回来将会更好。
Christoph Lameter带来了直接迁移补丁,它把swap设备放到了一边。让咱们看看为何这个补丁不首先使用这种方式,直接页迁移不仅仅引入了复制数据。第一步,选定目标页后,锁定页以防止其它人访问它。页可能正在执行IO,因此内核必须等待IO完成。而后真正的迁移才开始执行。
内核必须为页创建一个换出缓存,即便它想避免把页写入swap。若是进程在它正在被移动的过程当中发生错误,这个缓存就能够保证一切正常。而后全部对这个页的引用(页表项)都取消映射。幸运的话,全部的引用都没了,若是因为某种缘由引用还在,页就不能移动。
事实上,移动一个页包括复制一部分页状态,复制页数据自己,而后复制剩余的状态。旧页被清理并释放。若是有任何写回已经在新页上排队,它们就会被执行。而后就是简单的清理,页成功的移动了。
若是内核在目标节点上用光了空闲页,那就退回到基于swap的机制。因此补丁前期的阶段仍然有用。
使用这些代码,内核能够保持一个进程的页在本地内存中。迁移代码在热插拔内存方面也证实是有用的,它须要指定范围内全部的页都必须被回收。事实上,这些代码有一部分原本就是为热插拔应用而写的。可是,从这点上,迁移有一个很好的基础。对NUMA系统来讲,移动页失败会致使更坏的性能,但没什么特别坏的地方。而对热插拔内存,这种失败会彻底阻碍一个内存删除操做。100%的肯定能够移动一个范围的全部页仍然是个很难的问题,目前尚未完整的方案。
http://lwn.net/Articles/101230/
September 8, 2004
内核里的核心内存分配机制是基于页的,它会试图找到必定数量连续的页以响应一个请求(这个地方的“必定数量”是指2的幂)。可是随之系统运行了一段时间,对多个连续页的更高阶分配请求变得很难知足。虚拟内存系统碎片化了物理内存以致于空闲页都相互分离了。
好奇的读者能够查询/proc/buddyinfo看看目前空闲页是多么碎片化了。在一个1G的系统上,我看到:
Node 0, zone Normal 258 9 5 0 1 2 0 1 1 0 0
在这个系统上,258单个页可当即分配,但只有9个连续的双页和5个4连续页。若是须要不少高阶分配的东西到来,可用内存很快就会用光,这些分配可能就会失败。
Nick Piggin最近看到了这个问题并发现有一个地方能够作提高。问题在于kswapd进程,它在后台运行并为内存分配器提供空闲页(经过释放用户页)。目前的kswapd代码只是注意可用空闲页的数量,若是这个数目足够高,kswapd就休息而无论这些页是不是互相连续的。这会致使即便高阶分配失败,但系统也不打算作任何特别的努力来释放更多的连续页。
Nick的补丁很直接,它只是简单的阻止kswapd休息直到一个足够数量的高阶分配是可用的。
可是有人指出kswapd使用的方法其实没有真的改变:它选择页来释放但没考虑这些页是否能够合并成更大的组。结果,它可能不得不释放不少页才偶尔建立一些高阶的页组。在之前的内核,没有更好的可用方法。但2.6内核包含翻转映射代码。使用翻转映射,就有可能定位连续内存来释放并在这方面大幅度提高系统性能。
Linus反对这种方法由于它推翻了目前的页替换策略,这种策略尽力释放在不远的未来不会使用的页。改变这种策略来定位连续的块会使高阶分配更容易,但它一样也会经过释放有用的页而对整个系统的性能不利。因此,Linus说,若是一个“反碎片化”模式必定要实现,那它应该不多运行并做为一个独立的进程。
另外一个针对这个问题的方法是简单的在一开始就避免高阶分配。转向4K的内核栈是这个方向上的进展,它消除了为进程执行一个两页的分配。在目前的内核,高阶分配最大的用户之一是高性能网络适配器驱动。这些适配器处理的大包不适合单个页,因此内核必须执行多页分配来保存这些包。
实际上,这种分配只有在驱动(和它的硬件)不能处理分散在内存里的“非线性”包的时候才须要。大多数现代硬件可执行分散/汇集DMA操做,因此不关心包是否是保存在单个的连续内存里。可是,当向驱动写入时使用硬件的分散/汇集功能不但须要额外的工做,而且对不少驱动来讲,这些工做还没完成。可是,从需求方面解决高阶分配问题可能比给页重声明代码增长另一个东西更有效率。
http://lwn.net/Articles/105021/
October 5, 2004
在内核里,高阶分配是指试图为一个须要多于单个物理连续块的应用获取多个连续的页。这种分配一直是内核的一个问题,一旦系统运行了一段时间,物理内存通常被碎片化从而几乎没有连续空闲页存在。上个月,Nick Piggin尝试对kswapd作修改来必定程度上缓解这个问题。可是,也有其余人的方案。
其中之一是Marcelo Tosatti,他发布了一个给内核增长主动内存反碎片化的补丁。在一个高的级别上,它使用的算法相对简单:为得到N阶空闲块,首先找到最大的、比N略小的块,而后试着重定位块先后的页的内容。若是有足够的页可被移动,一个更大的空闲页块就会被建立。
天然地,这种方法仔细看的话会至关复杂。不是全部的页时可重定位的,举个例子,那些被锁住或保留的,是不能动的。这个补丁也排除了正在写回的页,除非写回IO完成,不然这些页是不能移动的。不少更复杂的例子,好比移动属于非线性映射的页,这个补丁目前也不能处理。
若是一个页是不能重定位的,就必须先把它锁住而后把它的内容复制到一个新页上。而后全部引用旧页的页表必须从新指向新页。翻转映射信息,若是有的话,就必须被正确设置。若是在swap里有这个页的复制,那这个复制必须链接到新页。诸如此类。Marcelo的补丁针对不少更复杂的状况只是简单地拒绝移动这些页。即便如此,Marcelo报告了在建立大的连续空闲内存的好的结果。
固然,有些小故障,包括在SMP系统上的问题。可是Marcelo说,不用担忧:
可是在UP上它工做的很好,能够很轻松的建立大的物理连续的内存区域。
有人指出这个补丁跟另外一项工做有一些共同的特性:支持热插拔内存。当内存要从系统中移除,目前存储在这块内存里的全部的页必须被重定位。本质上,热插拔内存补丁寻找并建立正好覆盖一片特定物理地址的一大块空闲内存。
Dave Hansen描述了增长热插拔内存支持的两个补丁--一个来自IBM,另外一个来自FUJITSU。每一个都有它的优势和不足。
在Marcelo和热插拔补丁之间,有不少在移动页来释放大块内存的经验。在任何一个合入主线以前,把这些补丁的优势合到一块儿多是必须的。但最终的结果多是对高阶分配问题的一个终结。
http://lwn.net/Articles/121618/
February 1, 2005
不少开发者尝试解决内存碎片化和在内核中分配大的连续的内存块的问题。最近的方法包括Marcelo Tosatti的主动反碎片化和Nick Piggin的kswapd提高。如今Mel Gorman采用不一样的方法加入争论。
在很高的级别上,内核以下图的方式管理空闲页。
系统的物理内存被分红区:在x86系统上,这些区包括可被ISA设备访问的小的空间(ZONE_DMA),正常内存区(ZONE_NORMAL)和内核非直接访问的内存(ZONE_HIGHMEM)。NUMA系统经过为每一个节点建立区来进一步区分。每一个节点里,内存被分红块(chunks)并按“阶数”(order)排列--每一个块的大小的基于2算法。对每个阶数都有一个这种大小的可用的块的链表。因此,这个数组的底部,阶数0链表包含单个页,阶数1链表包含双页,等等,直到系统的最大阶数。当一个给定阶数的分配申请到来时,一个块会从相应的链表上取下。若是这种大小的块没有了,一个更大的块会被拆分。当块被释放了,伙伴分配器试着把它们跟它们附近的块合并来建立更高阶的块。
在现实中的linux系统,随着时间流逝,较大的块趋于被拆分以致于较大的分配变得很难。在一个运行的系统上能够经过/proc/buddyinfo看到不少0阶的页可用,但相对少的更大的块。为此,高阶分配在一个运行了一段时间的系统上更可能会失败。
Mel的方法是把内存分配划分为三种,用新增长的GFP_标志来标明,这些标志能够在内存请求的时候提供。带__GFP_USERRCLM标志的内存分配用于用户空间,且很容易重声明。通常,重声明一个用户空间的页就是把它写到后备存储(若是它已经被修改了)。__GFP_KERNRCLM标志用在可重声明的内核内存,好比从slab中获取的和用在可按需释放的缓存里的。最后,未被标明的分配是被认为用简单的方法没法重声明的。
而后,伙伴分配器的数据结构被扩展成这样:
当分配器被初始化,全部未经使用的内存还没碎片化,free_area_global域指向一个长的最大大小块内存的链表。三个free_area数据--每种分配类型一个--一开始是空的。每一个分配请求会在相应的可用free_area数组获得知足,不然,一个来自free_area_global的MAX_ORDER块就会被拆分。块未被分配的部分会被放到跟目前分配类型相同的数组里。
当内存被释放而且块被合并,它们仍然留在相应类型数组里直到达到最大大小,而后被放回到全局数据里。
这种组织的一个直接好处是最难回收的页--那些内核不可重声明的--被组织到本身的块里。一个钉住的页就能阻止一个大块的合并,因此,汇集这些难的内核也让剩余内存的管理更容易。除了这个,这种组织让主动页释放变得可能。若是一个高阶请求不能被知足,只是简单的从一个较小的块并释放其附近的页。但主动释放还没有在Mel当前的补丁里实现。
即便没有主动释放,这个补丁也帮助了内核知足大的分配。Mel给出了他跑的测试结果。没打补丁,160次10阶分配申请里成功3次,打补丁后,81次成功。因此,新的分配技术和数据结构确实帮助了这种状况。可是,接下来发生的还有待观察,要想让这个补丁合入主线貌似尚有一个很大的障碍须要克服。
http://lwn.net/Articles/158211/
November 2, 2005
Mel Gorman的避免碎片化补丁把全部的内存分配划分为三类:“用户可重声明”,“内核可重声明”和“内核不可重声明”。这个主意是经过把可重声明分配分到一块儿来支持多个连续页分配。若是没有连续内存可用,能够经过强制换出可重声明页建立一个。因为不可重声明页都被分到了它们本身的地方,一个这种页阻碍一个连续空闲页建立的概率相对很小。
Mel最近发布了第19版避免碎片化补丁并请求合入-mm内核。这个请求引发了关于这个补丁是不是好主意的大讨论。对这部分代码是否属于内核好像还存在至关多的不肯定。有一些须要避免碎片化的缘由,在每一个缘由上的争论也不一样。
这些缘由的第一个是它增长了内核中高阶分配的可能性。没人否定Mel的补丁达到了这个目的,但有些开发者声明说一个更好的方法是简单的消除这种分配。事实上,大多数多页分配在之前被处理了。但有一些仍存在,包括两页的内核栈仍然在大多数系统上被默认使用。当内核栈分配失败,它会阻塞新进程的建立。内核可能最终在全部状况下都转而使用单页栈,但一些高阶分配仍会遗留。把请求的内存分红单页的块也不是总会成功。
下一个缘由,跟第一个有很大的关联,就是大页。大页机制被用来在大型系统上为某些应用提高性能,目前只有不多用户,可是若是大页很容易工做的话状况可能会改变。大页不能在缺少大的且适当对齐的连续内存的系统上分配。事实上,它们很难在运行了一段时间的系统上建立。分配大页失败相对来讲没多大事,应用程序只需简单的用常规页并承受性能打击。可是,若是你有一个大页机制,让它更可靠的工做是很值得的。
避免碎片化补丁对高阶分配和大页都有帮助。可是存在它是否是这个问题的正确方法的争论。常常被讨论的可选方法是为可重声明内存建立一个或更多新的内存区。这种方法会用到已内建在内核里的区系统,因此避免了一个新层的建立。一个基于区的系统也可能会避免潜在的避免碎片化补丁带来的性能影响(尽管未经证明)。考虑到这种影响出如今很关键的应用场景--内核编译--这个争论在内核开发者中引发了共鸣。
可是基于区的方法并非没有问题。目前的内存区是静态的,结果,人们不得不决定怎么划份内存为可重声明和不可重声明的。这种调整看起来很难用可靠的方法实现。在过去,区系统也是不少性能问题的来源,大多数与区以前的分配平衡相关。增长区系统的复杂性和增长区极可能让这些问题重现。
另外一个避免碎片化的动机是带来不一样限制的对热插拔内存的支持。这种特性在高可用系统上有用,但它同时在虚拟化领域用的不少。一个跑了不少虚拟的linux实例的主机能够经过热插拔的方式在不一样实例之间转移内存资源以知足各个实例的要求。
在内存能够从一个运行着的系统从移除以前。它的内容必须被移到别处--至少,若是你还想你的系统能继续运行的话。避免碎片化补丁能经过只把可重声明的分配放到可能会移除的内存上来起到帮助。只要一个区域里的全部页均可以被重声明,那这个区域就是可移动的。
一个很不一样的争论出现了:Ingo Molnar强调任何声明支持热插拔内存的机制都要保证100%的成功率。目前的代码没必要按照这个标准,可是须要一个照着这个目标的清晰的路径。不然,内核开发者就是在冒险推广一个还没提供可靠支持的特性。避免碎片化的支持者但愿合并补丁以解决90%的问题,把剩余的90%留到之后(评论:这是内核特性有技巧的东西,对一个解决方法来讲,第二个90%通常比第一个90%更难,有时甚至有第三个90%须要考虑。)。然而,Ingo担忧第二个90%,想知道怎么实现它。
为何目前的补丁不能提供100%的可靠性若是只把可重声明内存放在可热插拔区域?有可能锁定已经被标明可重声明的页,这包括DMA操做和被用户空间明确锁住的页。也有可能在内核用光不可重声明内存时出现问题。不是让一个不可重声明的分配失败,内核会在可重声明区域分配页。这种回退在防止破坏内核其余方面的可靠性方面是必须的。 可是在一个可重声明区域里的一个不可重声明页会阻止系统清空这块区域。
这个问题能够经过彻底放弃不可重声明分配来解决。这能够经过改变内核地址空间工做方式来作。目前,内核运行在一个单独的连续的虚拟地址空间,它是直接映射到物理内存的--常用一个单独的大页表项。(vmalloc()是例外,不在讨论之列)。若是内核使用就像系统其余地方使用的常规大小的页,它的内存就再也不须要是物理连续的了。而后,若是一个内核页阻碍了,它就能够简单的被移动一个更方便的地方。
这种方法除了在根本上改变了内核的内存模型外,还有两个小问题。更高的转换缓存使用致使的性能破坏和为储存内核页表的内存数量的增长。某些内核操做--特别是DMA--不能容忍物理地址可能在任意时间发生改变。因此不得不增长一个新的API,驱动能够用它来申请物理上钉住的区域,并被内核告知把它们释放。换句话说,破坏内核的地址空间会带来大量的蠕虫障碍。若是没有强烈的动机是不会被接受的,热插拔内存还不是一个足够引人注目的理由。
因此对避免碎片化尚未最终的结论。可是近期看,Andrew Morton的避免争论机制可能会阻止补丁进入-mm树。可是对这种能力的渴望是有正当理由的,问题也不会消失。除非有人提出更好的方法,否则很难永远不合并Mel的补丁。
http://lwn.net/Articles/159110/
November 8, 2005
上周关于避免碎片化的文章总结以下:
可是对这种能力的渴望是有正当理由的,问题也不会消失。除非有人提出更好的方法,否则很难永远不合并Mel的补丁。
可是一个能阻止补丁进入内核的事情是Linus的反对,它就发生在这个补丁上。他认为避免碎片化是“彻底没用的”并总结说:
不要这样作。咱们从没这样作,咱们一直很好。
根据Linus的想法,正确的方法是在有释放大的,连续内存的需求的系统(不多)上建立一个特殊的内存区。在这个区里不能进行内核内存分配,因此它只包含用户空间页。这些页在须要的时候相对容易移动,因此大部分需求会被知足。会须要一些内核调优,但这是为运行高特殊化应用程序的代价。
这种方法不被全部的人接受。Andi Kleen提醒:
一个负载用光了内核可分配页时你有两个选择。要么使用可重声明区或让分配失败。第一个意味着大页之类的是不可靠的,第二个意味着全部的关于受限lowmem的问题将会复现。
其余人则提醒为全部类型负载来调优一个机器是很难的,特别是在有不少用户的系统上。尽管有反对,看起来主动避免碎片化不会很快进入2.6内核。
http://lwn.net/Articles/160201/
November 8, 2005
这种方法的其中之一多是主动内存反碎片化,它不在可能会移除的内存区域中执行不可移动的内存分配。当咱们上周看主动反碎片化时,这个补丁看起来有麻烦了。反碎片代码的负载可能过高,并且还有不少开发者(包括Linus)感受这种功能应该用内核的区系统来实现,而不是在内存分配器中增长一个新的层。
可是反碎片化的做者Mel Gorman不会轻易放弃。他发表了一个新的反碎片化补丁的轻量级版本,他但愿它会更容易被接受。就像他描述的:
这是一个简化了的反碎片方法,它简单的试着让内核分配以2^(MAX_ORDER-1)为组和容易重声明的分配以2^(MAX_ORDER-1)为组。它不使用起平衡做用的可调整的特殊保留内存,也没在主路径里引入新的分支。对小内存系统来讲,它能够经过一个配置选项关掉。它一共增长了275行代码同时只对主路径作了最小的改动。
这个版本的补丁增长了一个新的GFP标志(__GFP_EASYRCLM),它的存在标明一个在须要的时候会很容易的被内核回收的分配。它用于用户空间页(通常可强制换出到后备存储中)和一些其余的场合,好比一些内核缓存(buffer)。伙伴分配器已经能够跟踪成块的内存,新的代码简单的控制在一些块上的可重声明分配,同时把不可重申明分配放在其余块上。它但愿经过这种方法解决一个不可移动的页阻塞大的连续区域的释放的问题。
这补丁经过建立一个跟踪每一个大块内存上执行那种分配的“usemap”数组来工做。Mel也不得不拆分用来执行快速单页分配的per-CPU空闲链表,如今有两个这样的链表,每种分配类型一个。而后就只是依靠__GFP_EASYRCLM标志在适当的内存里执行分配了。
这个版本固然减小了反碎片化补丁的足迹和负载。但还不是其余一些人但愿的基于区的方法。因此主动反碎片化是否比它的前者更易被接受还有待观察。
http://lwn.net/Articles/211505/
November 28, 2006
内存碎片化是一个好久的内核编程问题。随着系统运行,页分配给各个进程从而致使内存碎片化。一个运行了很长时间的繁忙的系统可能只有不多的物理上连续的页了。因为linux是虚拟内存系统,碎片化通常也不是问题,物理上分散的内存能够经过页表实现虚拟的连续。
可是存在一些须要物理上连续内存的场合。这包括大的内核数据结构(除了经过vmalloc()分配的)和任何须须为外部设备保持连续的内存。若是一个大的(高阶)内存块在须要的时候不可用,一些东西就会失败,用户也开始考虑转向BSD。
几年以来,你们考虑了不少针对内存碎片化的方法,但尚未一个被合并。给核心内存管理代码增长任何负载都很难被接受。可是这并不意味着人们中止了尝试。在这个领域最持久的人之一是Mel Gorman,他已经在一个反碎片化补丁上工做了好几年。Mel带着他的补丁的27版回来了,从新命名为“页集群化”。这个版本的补丁吸引了一些兴趣并可能合入主线。
Mel补丁的核心内容仍然是一些类型的内存比另外一些更容易重声明。好比,一个为文件系统作缓存的页是准备好被删掉和被从新使用的,而一个保存一个进程的task数据结构的页则被钉住了。一个顽固的页就能阻止一整个大内存块被用做物理连续的总体。可是若是全部容易重声明的页被放在一块儿,不可重声明的页被放在另外一个单独的内存区域,建立一个更大的空闲内存块就更容易了。
因此Mel的补丁把每一个内存区分红了三类内存块:不可重声明的,容易重声明的和可移动的。“可移动的”是这个补丁里的一个新的特性,它被用在那些使用内核的页迁移机制就能够容易的转移到其余地方的页。不少状况下,移动一个页可能比重声明它更容易,由于不须要用到后备存储设备。经过这种方式给页分组页可使更大的块的建立发生在当一个进程从一个NUMA节点迁移到另外一个时。
因此,这个补丁里,可移动的页(使用__GFP_MOVABLE标记的)通常是那些属于用户空间进程的页。移动一个用户空间页只是复制数据和修改页表项。因此相对容易。而可重声明页(使用__GFP_RECLAIMABLE标记)则通常属于内核。它们要么是短暂存在(好比,一些只在IO操做期间存在的DMA缓存)的分配或者是在须要的时候被删掉(各类缓存)的分配。其余任何东西都被认为是很难重声明的。
经过简单的用这种方式把不一样种类的分配分组,Mel获得了一些好的结果:
在基准和压力测试里,咱们能够在测试后找到80%的内存用于连续的块。做为比较,一个桌面系统上的标准内核能够获得小于1%的可用做大页的内存和在压力测试结束后大约8-12%的内存用做大页。
在过去,Linus一直反对减小内存碎片化的努力。但此次他的评论更注重细节:分配默认是可移动的仍是不可移动的?答案应该是“不可移动的”,由于有人老是不得不为了确保一个特定的分配是可移动的而作一些努力。因为讨论已经到了这个程度,一些避免碎片化补丁可能找到了进入内核的方法。
针对碎片化的一个相关的方法是Andy Whitcroft发表的但最初是Peter Zijlstra的“块状重声明”机制。linux里的内存重声明通常是经过一个最少-最近-使用(LRU)链表,想法是,若是须要删除一个页,用最少最近使用的页会最小化扔掉一个很快就用到的页的概率。可是这种机制会释放随机分散在物理地址空间的页,让建立更大的空闲内存快更难。
块状重声明补丁尝试经过轻微的修改LRU算法来解决这个问题。当须要内存时,仍是像之前同样从LRU链表上选择下一个目标。而后重声明代码会观察目标附近的页(它们足够构成一个更高阶的块)并同时试着释放它们。若是成功了,块状重声明会很快建立一个更大的空闲块的同时重声明最小数量的页。
很明显,这种方法在附近的页能够被释放的状况下会工做的更好。结果显示,它跟一个集群化机制,好比像Mel Gorman那样的,结合的很好。LRU变形的方法会有性能影响,由于附近的页在块状重声明代码运行的时候可能正处在重负荷下。一个最小化这种影响的方法是块状重声明代码只在内核知足大块内存申请有困难时才执行。
是否--什么时候--这些补丁被合入还有待观察。核心内存管理补丁须要很当心,它们很容易在暴露给现实负载时致使混乱。可是问题不会本身消失,因此有些东西早晚会发生。
http://lwn.net/Articles/224829/
March 6, 2007
内存管理在2.6.x版本内核中是个相对平静的话题。不少最坏的问题已经解决了,内存管理的开发者继续作其余事情了。可是这并不意味着没有问题了,事实上,有些问题将要热起来了。一些最近的讨论揭露了一些可能致使在不远的未来引发新的兴趣的压力。
Mel Gorman的避免碎片化补丁在过去已经讨论好几回了。Mel的核心思想是区分哪些能够容易移动或重声明的页并把它们分组。可移动的页包括那些分配给用户空间的页,移动它们只须要修改相关的页表项。可重声明页包括能够按需释放的内核缓存。把它们放在一块儿让内核释放大的内存块更容易,这会对使能高阶分配或清空整个内存区域都有用。
在过去,Mel补丁的审核者在它工做方式上有不一样意见。一些人支持为不一样的分配类型维护单独的空闲链表,而其余的感受这种内存分区的方法正是内核的区系统的建立目的。因此此次,Mel发布了两个补丁:基于链表的分组机制和一个仅限于可移动分配的新的ZONE_MOVABLE区。
此次的不一样是这两个补丁是设计一块儿工做的。默认的,没有可移动区,因此基于链表的机制处理把类似分配放在一块儿的所有工做。管理员能够在启动的时候用kernelcore=选项配置ZONE_MOVABLE,它用来指定*不*放在这个区里的内存的数量。另外,Mel发布了一些关于这些补丁是怎么影响性能的有理解力的信息。用不一样寻常的方法,Mel使用了一些视频,这些视频显示了内存分配是如何响应使用不一样分配机制的系统压力的。演示是有说服力的,可是为了在未来进入内核而建立多媒体演示是没必要要的。
这些补丁找到了它们进入-mm树的方法,但Andrew Morton仍然不清楚它们是否有价值的。另外,他还担忧它们与其余相关工做的适应状况,特别是内存热拔除和每容器内存限制。尽管解决这两个问题的补丁已经发布了,但都还没达到合入内核的程度。
Mel的工做很明显对内存的热移除是有帮助的--可能被移除的内存能够被限于可移动的和可重声明的分配,这会运行它在须要的时候被清空。但不是全部人都认为热拔除是一个有用的特性。特别是Linus反对这种想法。热拔除最大的潜在用途是在虚拟化,它容许一个管理器能够在客户端在需求改变的时候移动内存资源。Linus指出大多数虚拟化机制已经拥有容许客户端增长和删除单个页的机制,因此,他说,不必再为内存变更提供其余支持。
这种技术的另外一个用途是容许系统经过在一些内存不使用的时候把它们关掉来保存电量。很明显,必须在关闭以前把全部有用的数据移出内存。Linus更加不一样意这种作法:
整个DRAM电源的故事就是一个给容易受骗的孩子们的睡前故事。不要陷进去。它是不真实的。支持它的硬件如今还不存在,或许几年后也不存在。真正的修复在其余地方...
简短说来,Linus认为关掉整个NUMA节点比单独的内存更行得通。尽管如此,Mark Gross发布了一个包含一些基本的反碎片化技术的使能内存掉电的补丁。Mark说:
内存电源管理是没用的除非你有利用它的工做环境。验证过的工做环境不是桌面环境。可是,有一些感兴趣的用户有一些合适的工做环境,这些工做环境让使能内存掉电补丁进入社区是值得的。这些工做环境通常在用内存利用率来跟踪流量负载的网络元素和服务器里。
有人也建议常驻集合大小(resident set size)限制(通常跟容器联系在一块儿)能够解决不少反碎片化工做想要解决的问题。Rik van Riel回应抱怨说RSS限制会加剧目前内存管理系统正在经历的可伸缩性问题。这引发了之前不知道这些问题的人的--好比Andrew--疑问。Rik用一些相对模糊的例子回应,很明显,他的特定的行为受限于经历这些问题的用户的协议。
这致使了关于在没有测试实例来验证内存管理问题的状况下解决这些问题是否说的通的大讨论。Rik争论说修复测试实例会破坏现实世界的东西。Andrew回应说:
我不相信一个没能力提供哪怕一个简单的测试实例的人或组织会有能力在不破坏东西的状况下修复这样的问题。
Rik为让讨论继续提供了一些有问题的工做场景。
Andrew的观点之一是在内核里修复由特定场景引发的内存管理问题老是很难,内核不老是有信息来知道哪些页会很快被用到和哪些会被删掉。可能,他说,正确的方法是让用户空间更容易的联系它未来但愿的需求。为了这个目的,他为测试提出了一个页缓存管理工具。它做为一个LD_PRELOAD库来工做,截取文件相关的系统调用,跟踪应用程序使用量并通知内核在使用后从缓存里扔掉页。结果是通常操做(好比复制内核树)能够在不强制其余有用数据换出页缓存的状况下被实现。
有一些对这个的有怀疑的回复。也存在一些关于怎么引入更智能的、特定应用的策略到这个工具的兴趣和讨论。好比,一个可能的备份工具策略会强制输出文件当即刷出内存,跟踪从其余文件读出的页并强制它们刷出--但它们必须尚未在页缓存里,等等。是否有人会用这个工具解决真实的工做环境问题还有待观察,可是有潜力。内核不老是什么都知道的。
(转载本站文章请注明做者和出处 http://www.cnblogs.com/baiyw/,请勿用于任何商业用途)