Buddy分配器之释放一页
Posted Loopers
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Buddy分配器之释放一页相关的知识,希望对你有一定的参考价值。
在上面一节我们讲述了buddy分配器是如何分配一页的,本节我们在学习buddy分配器是如何释放一页的
分配一页的算法:比如当前释放order=n的页
- 获得当前释放order=n的页,对应的buddy,也就是兄弟页,俗话说先找到兄弟
- 找到兄弟buddy了之后,接下来就是看buddy和此页是否可以合并
- 检查buddy是否是空闲的页(通过检查page→private是否为0)
- 检查buddy是否和此页是相同的order
- 检查buddy和此page是否属于同一个zone
- 检查buddy的引用计数_refcount是否为0
- 如果上述的条件都符合,则认为此buddy和此page可以合并
- 将此buddy从lru链表中删除,页的个数减去1
- 再将order++,从更高的order去寻找buddy,检查是否可以合并
- 找到兄弟buddy了之后,接下来就是看buddy和此页是否可以合并
- 当找不到buddy的时候,则将此order的页加到lru链表中
- 将order此页的nr_free加1
举例:当释放order=2的页
- 找到order=2的buddy页,也就是兄弟页
- 检查此buddy页是否可以和当前释放的页合并
- 如果可以合并,则order+1=3
- 则找到order=3的buddy页
- 检查此buddy页是否可以和需要分配页是否可以合并
- 如果合并则将order+1=4。 继续检查是否可以合并到最大order
- 如果当order=4时无法合并,则将此page添加到order=4的freelist中即可。
那怎么获取一个将要释放页的buddy呢,内核提供了一个函数,可以获取buddy的pfn
static inline unsigned long
__find_buddy_pfn(unsigned long page_pfn, unsigned int order)
return page_pfn ^ (1 << order);
我们在zone那一节讲解了page和pfn的关系,这里在复习一下。物理页如果按照4K的大小来描述的话,一个4K大小的物理页的区域我们称之为page frame页帧,而如果给page frame编号的话,就出现了page frame num。
而内核提供的page_to_pfn和pfn_to_page可以轻松的相互转化pfn和page的关系,在内存模型为稀疏模型的情况下,page=vmemmap+pfn
假设当前的order=0, page_pfn=0的话,则对应的buddy_pfn=0^(1<<0)=1,则物理页0和物理页1是buddy,就是好兄弟
假设当前的order=3, page_pfn=0的话,则对应的buddy_pfn=0^(1<<3)=8,则物理页0和物理页8是buddy,中间刚好是order个页。
说以说可以通过上述的函数来计算出buddy所在的位置
我们接下来看看buddy释放页的核心函数:
static inline void __free_one_page(struct page *page, unsigned long pfn, struct zone *zone, unsigned int order,int migratetype)
unsigned long combined_pfn;
unsigned long uninitialized_var(buddy_pfn);
struct page *buddy;
unsigned int max_order;
struct capture_control *capc = task_capc(zone);
max_order = min_t(unsigned int, MAX_ORDER, pageblock_order + 1);
VM_BUG_ON(!zone_is_initialized(zone));
VM_BUG_ON_PAGE(page->flags & PAGE_FLAGS_CHECK_AT_PREP, page);
VM_BUG_ON(migratetype == -1);
if (likely(!is_migrate_isolate(migratetype)))
__mod_zone_freepage_state(zone, 1 << order, migratetype);
VM_BUG_ON_PAGE(pfn & ((1 << order) - 1), page);
VM_BUG_ON_PAGE(bad_range(zone, page), page);
continue_merging:
while (order < max_order - 1)
if (compaction_capture(capc, page, order, migratetype))
__mod_zone_freepage_state(zone, -(1 << order),
migratetype);
return;
buddy_pfn = __find_buddy_pfn(pfn, order);
buddy = page + (buddy_pfn - pfn);
if (!pfn_valid_within(buddy_pfn))
goto done_merging;
if (!page_is_buddy(page, buddy, order))
goto done_merging;
/*
* Our buddy is free or it is CONFIG_DEBUG_PAGEALLOC guard page,
* merge with it and move up one order.
*/
if (page_is_guard(buddy))
clear_page_guard(zone, buddy, order, migratetype);
else
list_del(&buddy->lru);
zone->free_area[order].nr_free--;
rmv_page_order(buddy);
combined_pfn = buddy_pfn & pfn;
page = page + (combined_pfn - pfn);
pfn = combined_pfn;
order++;
if (max_order < MAX_ORDER)
/* If we are here, it means order is >= pageblock_order.
* We want to prevent merge between freepages on isolate
* pageblock and normal pageblock. Without this, pageblock
* isolation could cause incorrect freepage or CMA accounting.
*
* We don't want to hit this code for the more frequent
* low-order merging.
*/
if (unlikely(has_isolate_pageblock(zone)))
int buddy_mt;
buddy_pfn = __find_buddy_pfn(pfn, order);
buddy = page + (buddy_pfn - pfn);
buddy_mt = get_pageblock_migratetype(buddy);
if (migratetype != buddy_mt
&& (is_migrate_isolate(migratetype) ||
is_migrate_isolate(buddy_mt)))
goto done_merging;
max_order++;
goto continue_merging;
done_merging:
set_page_order(page, order);
/*
* If this is not the largest possible page, check if the buddy
* of the next-highest order is free. If it is, it's possible
* that pages are being freed that will coalesce soon. In case,
* that is happening, add the free page to the tail of the list
* so it's less likely to be used soon and more likely to be merged
* as a higher order page
*/
if ((order < MAX_ORDER-2) && pfn_valid_within(buddy_pfn))
struct page *higher_page, *higher_buddy;
combined_pfn = buddy_pfn & pfn;
higher_page = page + (combined_pfn - pfn);
buddy_pfn = __find_buddy_pfn(combined_pfn, order + 1);
higher_buddy = higher_page + (buddy_pfn - combined_pfn);
if (pfn_valid_within(buddy_pfn) &&
page_is_buddy(higher_page, higher_buddy, order + 1))
list_add_tail(&page->lru,
&zone->free_area[order].free_list[migratetype]);
goto out;
list_add(&page->lru, &zone->free_area[order].free_list[migratetype]);
out:
zone->free_area[order].nr_free++;
- 首先会进行一系列合法性检查,zone是否初始化? 迁移类型是否等于-1? 等等
- 接着就会进入一个while循环,当前order到最大order减去1之间 寻找合适的buddy,然后合并
- 根据当前page和pfn,获取buddy的buddy_pfn,根据buddy_pfn在获取buddy页
- 通过调用pfn_valid_within来检查buddy_pfn是否是有效的,通过物理地址>>PAGE_SHIFT来判断
- 调用page_is_buddy函数来检查此buffy页是否可以合并
- page_order(buddy) == order //buddy的所在order和释放的页的order是否相同
- page_zone_id(page) != page_zone_id(buddy) //检查是都都在一个相同的zone中
- page_count(buddy) != 0 //检查buddy页的索引技术是否等于0
- 检查到此buddy可以和此page可以完美合并则进行下一步操作
- 将buddy页从page的lru链表中删除,同时将此order可用的page数目减去1,设置此buddy页不在buddy系统中
- 计算buddy页和释放页合并后的pdn和page,同时将order数目加1
- 继续进入到while循环中再次判断,是否有空闲的页可以合并,
- 当出现无法找到合适的buddy页时,则调到done_merging进行做最后的收尾
- set_page_order(page, order); //设置最终page的order数目
- 如果当前的order不是最大的order,系统还可以再尝试一波,还是上面相同的操作
- 将最终合并后的页假如到属于他们order的lru链表中
- 将此order的可用的页的个数加1
以上是关于Buddy分配器之释放一页的主要内容,如果未能解决你的问题,请参考以下文章