在内核初始化完成后,内存管理的责任由伙伴系统承担。
1 伙伴系统的结构
系统内存中的每个物理内存页(页桢),都对应于一个struct page。每个内存域关联一个struct zone,其中保存了用于管理伙伴数据的主要数组。
struct zone{
...
struct free_area free_area[MAX_ORDER];
...
};
struct free_area{
struct list_head free_list[MIGRATE_TYPES];
unsigned long nr_free;
};
阶是伙伴系统中一个重要术语,它描述了内存分配的数量单位。内存块的长度为2的order(阶)次方。该常数通常设置为11,这意味着一次分配可以请求的页数最大是2的11次方(2048),IA-64或许大一些,ARM或许小一些。
free_area[]数组中各个元素的索引就解释为阶,用于指定对应链表中的连续内存区包含多少个页桢,简单的说,就是第0个链表代表第一阶,内存区为单页,第一个链表代表第一阶内存区为2的1次方为2页,以此类推。
每个内存区又是如何连接的呢?内存区的第一个页内的链表元素就干这件事,它将内存区维持在链表中。
伙伴不必是彼此连接的。如果在一个内存区在分配期间,内核把一半占用了,另一半加到了链表中,如果在未来的模个时刻,由于内存释放的缘故,两个内存区都为空闲,可通过地址判断是不是伙伴。
如果内存分配请求超出内存域或者结点,那么要首先尝试另一个内存域,接下来是下一个结点。从小范围到大范围尝试,知道满足。
有关伙伴系统的信息可以通过下面方式获得
chechunli@chechunli-desktop:~ $ cat /proc/buddyinfo
Node 0, zone DMA 56 12 7 5 6 4 2 2 1 0 1
Node 0, zone Normal 693 306 67 34 12 9 11 20 20 1 0
Node 0, zone HighMem 519 94 13 3 2 0 2 0 1 0 0
上述输出给出了各个内存域中每个分配阶中空闲项的数目,从左到右,阶依次升高。
2 释放页
__free_pages是一个基础函数,用于实现内核API中所有涉及内存释放的函数。
__free_pages
|->是否为单页 --是-->free__hot_page
|--否-->__free_pages_ok->__free_one_pages
释放时候,首先判断释放的内存是否为单页,要是单页的话,直接释放到per-CPU缓存中,对很可能出现在CPU高速缓存的页,则放置到热页列表中。如果per-CPU中的页数超过了自己所能容纳的数量,则释放一部分,还给伙伴系统,这个策略称为惰性合并,这样避免了单页返回给系统,系统合并,然后再拆分的麻烦。惰性合并阻止了大量白费的时间浪费。要是多于一页,则放回伙伴系统。
3 内核中不连续页的分配
我们知道连续的映射对内核是最好的,但并不总能成功的使用,在分配一大块内存时,可能竭尽全力也无法找到连续的内存块。在用户空间不存在这样的问题,因为普通进程设计为使用处理器的分页机制,当然这会降低速度并占用TLB。
内核中也可以这样实现,内核分配了虚拟地址空间的一部分,用于建立连续映射。在不连续的地址空间中就是这样实现的,vmalloc区就是虚拟地址空间。vmalloc是一个管理不连续内存的区域。
阅读(979) | 评论(0) | 转发(0) |