linux bootmem memblock的演进

有人可能认为在系统启动的时候内存的分配应该非常容易:基本上所有的内存都是可用的,不需要考虑并发性。尽管如此,boot阶段的内存管理仍然不是一个简单的任务。物理内存不一定是连续的,系统之间他们的分布更是有很大的不同,如何检测内存的布局就不是一个简单的任务。在NUMA的机器上这件事情更加复杂,在内存分配的时候还需要考虑在当前节点上分配,构建内存的拓扑图。为了应对这些问题,在启动过程中的早期阶段需要一些复杂的机制来进行内存管理工作。

有人会问,为什么不一开始就使用buddy这些系统来管理呢?因为linux的页分配管理器本身就很复杂,也需要分配内存来初始化自身,所以在早期阶段page分配器是不可用的。另外在NUMA上页分配器的数据结构应该带有NUMA的特征,即在node上分配该cpu的页面申请请求。所以需要额外的内存管理器来管理内存,直至内存管理系统状态变为可用的。

在linux早期开发阶段,linux并没有boot内存管理;在1.0内核中,内存初始化并没有像今天一样健壮和灵活。早期的子系统初始化仅仅是调用start_kernel,它通过全局的memory_start变量来访问单个bank内存的起始地址。如果需要申请内存,只需要像sbrk一样增长memory_start来表示内存被占用。在2.0版本中,linux已经被移植到多于5个平台上,不过boot阶段的内存管理仍然和1.0版本上一样简单,唯一不一样的是增加了平台探测自身可用内存的代码,而且那时候的硬件更加简单,可以很轻松的探测到内存信息。

一直到2.3.23pre3版本上,boot早期的内存分配仍然使用全局变量来表示可用内存的起始和结束地址。很幸运的是page和slab分配器在boot不久就可用了,所以严重依赖内存的功能,像buffers_init() and page_cache_init()就直接可以使用core的内存管理器。硬件演进的越来越复杂,平台相关代码处理内存变得也越来越复杂。

在2.3.23pre3的patch中第一次包含了bootmem分配器的视线,它使用一个bitmap来代表页的状态,0代表可用,1代表页缺失或者已经被使用。所有操作全局变量memory_start的函数还有i386的初始化代码开始使用bootmem,一直到2.3.48版本这种转换才完成,于此同时其他平台仍然使用老的方式。当linux被移植到Itanium(ia64)平台上时,它就完全使用bootmem。

随着时间的推移,内存布局探测也变的更加复杂,从刚开始只需要向BIOS询问内存块的大小到后来需要处理复杂的表,内存片,内存块还有内存簇。Power64时一个使用LMB(Logical Memory Block allocator)的平台,在LMB中,使用两个区间数组来表示内存信息,第一个数组描述了系统中可用的物理上连续的内存,第二个数组描述了已经被申请走的区域。LMB分配器之后被SPARC平台使用,之后其他的平台也接受了这个管理,逐步演变成了现在的memblock.

memblock分配器提供了两个最基本的接口,其他复杂的分配api都是基于这两个接口的:memblock_addmemblock_reserve,前者用来添加可用物理内存,后者用来表示被使用的内存,他们最终都是通过memblock_add_range来向两个数组中添加区间。

bootmem最主要的问题时它的bitmap初始化,为了创建bitmap,必须要知道物理内存的配置。第一个时bitmap究竟应该多大,哪块内存有这样足够的连续物理内存来存储它?而且随着内存增大,bitmap占用的空间也增大了,在一个具有32GB的RAM中,bitmap需要占据1MB的空间。而memblock它是基于一个非常大的静态数组,它可以随时使用的,最起码第一个内存的注册和分配是没有问题的。如果需要添加或者预留内存的区间数量超过了静态数组的大小,这个数组会double一下,而这种情况发生的时候我们相信会有足够的内存来存放扩张之后的数组。

memblock的设计时,它就假设在core的页分配器可用之前,不会有太多场景需要频繁申请和释放内存的,所以它并不没有做的非常复杂和职能,在将所有可用内存释放给buddy管理器之后就完全没有作用了。

为了过度bootmem到memblock,一个兼容层nobootmem被设计出来。nobootmem提供了bootmem一样的api,但是底层没有使用bitmap来管理页的状态而是使用了memblock的管理数据。在4.17版本,24个平台中只有5个仍然使用bootmem,14个平台使用nobootmem,剩余的5个平台同时支持memblock和bootmem。

当前正在进行的工作是在所有平台上都过渡到memblock和nobootmem。几个平台上使用设备树来进行内存管理的因为最近的变动也转而使用memblock,alpha,c6x,m68k和nios2平台上已经发patch在review了,其中的一些已经被arch maintaine合并了,另外一些还在review中。

希望在4.20合并窗口中所有平台都不再使用bootmem,之后就很可能会开始清理早期的内存管理代码,包括移除bootmem分配器和相关的内核配置。之后可能会整合更早期的启动代码,从平台完全相关到通用框架管理(译者:你们要不要搞这么复杂,轮子造的我都看不过来了),在内存管理系统中从来不存在完美这一说。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章