buddy system
伙伴系统是内核中用来管理物理内存的一种算法,我们知道内存中有一些是被内核代码占用,还有一些是被特殊用途所保留,那么剩余的空闲内存都会交给内核内存管理系统来进行统一管理和分配,
内核中会把内存按照页来组织分配,随着进程的对内存的申请和释放,系统的内存会不断的区域碎片化,到最后会发现,明明系统还有很多空闲内存,却无法分配出一块连续的内存,这对于系统来说并不是好事。而伙伴系统算法就是为了缓解这种碎片化。
伙伴系统(buddy system)把系统中要管理的物理内存按照页面个数分为不同的组,确切来说是分成了11个组,分别对应11种大小不同的连续内存块,每组中的内存块大小都相等,为2的幂次个物理页。
那么系统中就存在2 ^ 0~2 ^ 10这么11种大小不同的内存块,对应内存块大小为4KB ~ 4KB * 2^10。也就是4KB ~ 4M。内核用11个链表来管理11种大小不同的内存块。
内存分配
当分配内存时,会优先从需要分配的内存块链表上查找空闲内存块,当发现对应大小的内存块都已经被使用后,那么会从更大一级的内存块上分配一块内存,并且分成一半给我们使用,剩余的一半释放到对应大小的内存块链表上。
比如我们想要分配一个8KB大小的内存,但是发现对应大小的内存已经没有了,那么伙伴系统会从16KB的链表中查找一个空闲内存块,分成两个8KB大小,把其中的一个8KB大小返回给申请者使用,剩下的8KB放到8KB对应的内存块链表中进行管理。更坏的一种情况是,系统发现16KB大小的连续内存页已经没有了,那么以此会向更高的32KB链表中查找,如果找到了空闲内存块,那么就把32KB分成一个16KB和两个8KB,16KB的内存块放到16KB的链表进行管理,两个8KB的内存块一个返回给申请者,另一个放到8KB大小的链表进行管理。
内存释放
当释放内存时,会扫描对应大小的内存块链表,查看是否存在地址能够连续在一起的内存块,如果发现有,那么就合并两个内存块放置到更大一级的内存块链表上,以此类推。比如我们释放8KB大小的内存,那么会从对应的链表扫描是否有能够合并的内存块,如果有另一个8KB大小的内存和我们使用的内存地址连续,那么就合并它们组成一个16KB大小的内存块,然后接着扫描16KB大小的内存块链表,继续查找合并的可能,以此类推下去。
最大分配内存限制
从前面介绍可知,一共有11个链表,最大order为10,那么最大可分配的内存就是4M。
static inline
struct page *__rmqueue_smallest(struct zone *zone, unsigned int order,
int migratetype)
{
unsigned int current_order;
struct free_area *area;
struct page *page;
/* Find a page of the appropriate size in the preferred list */
for (current_order = order; current_order < MAX_ORDER; ++current_order) {
area = &(zone->free_area[current_order]);
if (list_empty(&area->free_list[migratetype]))
continue;
page = list_entry(area->free_list[migratetype].next,
struct page, lru);
list_del(&page->lru);
rmv_page_order(page);
area->nr_free--;
expand(zone, page, order, current_order, area, migratetype);
set_freepage_migratetype(page, migratetype);
return page;
}
return NULL;
}
这个伙伴系统分配的底层底层实现来看,如果order是不能超过MAX_ORDER=11的,超过了此值,不再进行for循环,而是直接返回NULL了。