Linux伙伴系统(四)--释放页
2015-10-19 10:12
531 查看
水平有限,描述不当之处还请之处,转载请注明出处http://blog.csdn.net/vanbreaker/article/details/7624628
Linux内存释放函数之间的调用关系如下图所示
![](http://my.csdn.net/uploads/201206/01/1338551771_2819.png)
可以看到,落脚点是__free_pages()这个函数,它执行的工作的流程图如下图所示
![](http://my.csdn.net/uploads/201206/01/1338552069_2058.png)
下面是该函数的具体代码
[cpp] view
plaincopy
void __free_pages(struct page *page, unsigned int order)
{
if (put_page_testzero(page)) {/*判断页没有被使用*/
trace_mm_page_free_direct(page, order);
if (order == 0)/*单页则释放到每CPU页框高速缓存中*/
free_hot_page(page);
else /*多页则释放到伙伴系统*/
__free_pages_ok(page, order);
}
}
[align=left]首先该函数要通过页描述符的引用计数来判断该页是否是空闲的[/align]
确定页是空闲的后,再判断要释放多少个页面,如果是单个页面则将该页作为热页释放到pcp中,如果是多页则释
放到伙伴系统中
[cpp] view
plaincopy
void free_hot_page(struct page *page)
{
trace_mm_page_free_direct(page, 0);
free_hot_cold_page(page, 0);
}
free_hot_page是free_hot_cold_page的封装
[cpp] view
plaincopy
static void free_hot_cold_page(struct page *page, int cold)
{
struct zone *zone = page_zone(page);
struct per_cpu_pages *pcp;
unsigned long flags;
int migratetype;
int wasMlocked = __TestClearPageMlocked(page);
kmemcheck_free_shadow(page, 0);
if (PageAnon(page))
page->mapping = NULL;
if (free_pages_check(page))
return;
if (!PageHighMem(page)) {
debug_check_no_locks_freed(page_address(page), PAGE_SIZE);
debug_check_no_obj_freed(page_address(page), PAGE_SIZE);
}
arch_free_page(page, 0);
kernel_map_pages(page, 1, 0);
/*获取对应的pcp结构*/
pcp = &zone_pcp(zone, get_cpu())->pcp;
/*获取迁移类型*/
migratetype = get_pageblock_migratetype(page);
set_page_private(page, migratetype);
local_irq_save(flags);
if (unlikely(wasMlocked))
free_page_mlock(page);
__count_vm_event(PGFREE);
/*
* We only track unmovable, reclaimable and movable on pcp lists.
* Free ISOLATE pages back to the allocator because they are being
* offlined but treat RESERVE as movable pages so we can get those
* areas back if necessary. Otherwise, we may have to free
* excessively into the page allocator
*/
/*只有不可移动页,可回收页和可移动页才能放到每CPU页框高速缓存中,如果
迁移类型不属于这个范围,则要将该页释放回伙伴系统*/
if (migratetype >= MIGRATE_PCPTYPES) {
if (unlikely(migratetype == MIGRATE_ISOLATE)) {
free_one_page(zone, page, 0, migratetype);
goto out;
}
migratetype = MIGRATE_MOVABLE;
}
if (cold)/*冷页插入表尾*/
list_add_tail(&page->lru, &pcp->lists[migratetype]);
else /*热页插入表头*/
list_add(&page->lru, &pcp->lists[migratetype]);
pcp->count++;
/*如果pcp中的页面数超过了high,则释放2^batch个单页给伙伴系统*/
if (pcp->count >= pcp->high) {
free_pcppages_bulk(zone, pcp->batch, pcp);
pcp->count -= pcp->batch;
}
out:
local_irq_restore(flags);
put_cpu();
}
伙伴系统的分支__free_pages_ok()先对释放的页做了些检查,然后具体的释放通过调用free_one_page()-->__free_one_page()来完成
[cpp] view
plaincopy
static inline void __free_one_page(struct page *page,
struct zone *zone, unsigned int order,
int migratetype)
{
unsigned long page_idx;
if (unlikely(PageCompound(page)))
if (unlikely(destroy_compound_page(page, order)))
return;
VM_BUG_ON(migratetype == -1);
/*得到页框在所处最大块中的偏移*/
page_idx = page_to_pfn(page) & ((1 << MAX_ORDER) - 1);
VM_BUG_ON(page_idx & ((1 << order) - 1));
VM_BUG_ON(bad_range(zone, page));
/*只要阶数小于MAX_ORDER-1就有合并的机会*/
while (order < MAX_ORDER-1) {
unsigned long combined_idx;
struct page *buddy;
/*找到page所处块对应的伙伴块*/
buddy = __page_find_buddy(page, page_idx, order);
/*如果伙伴块不是空闲的则不执行下面的合并操作*/
if (!page_is_buddy(page, buddy, order))
break;
/* Our buddy is free, merge with it and move up one order. */
list_del(&buddy->lru);/*将伙伴块从块链表中删除*/
zone->free_area[order].nr_free--;
rmv_page_order(buddy);
/*计算出合并块的起始页框的偏移*/
combined_idx = __find_combined_index(page_idx, order);
/*得到合并块的起始页描述符*/
page = page + (combined_idx - page_idx);
page_idx = combined_idx;/*修改块的起始页偏移*/
order++;/*阶数加1表明合并完成*/
}
/*重新设置块的阶数*/
set_page_order(page, order);
/*将新块添加到对应的链表中*/
list_add(&page->lru,
&zone->free_area[order].free_list[migratetype]);
zone->free_area[order].nr_free++;
}
这里面涉及到两个辅助函数,_page_find_buddy()用来找到是释放块的伙伴,如果找到了一个空闲的伙伴块要通过_find_combined_index()用来定位合并块的起始页框,因为一个块的伙伴块有可能在该块的前面,也有可能在该块的后面,这两个函数的实现非常简洁巧妙,全是通过位操作来实现的
[cpp] view
plaincopy
static inline struct page *
__page_find_buddy(struct page *page, unsigned long page_idx, unsigned int order)
{
unsigned long buddy_idx = page_idx ^ (1 << order);
return page + (buddy_idx - page_idx);
}
[cpp] view
plaincopy
<span style="font-size:12px;">static inline unsigned long
__find_combined_index(unsigned long page_idx, unsigned int order)
{
return (page_idx & ~(1 << order));
}</span>
我们可以通过一个简单的情形来模拟一下这个过程,假设现在有一个将要释放的页,它的order为0,page_idx为10
则先计算它的伙伴 10^(1<<0) = 11,然后计算合并后的起始页偏移为10&~(1<<0) = 10,现在就得到了一个order为1的块,起始页偏移为10,它的伙伴为10^(1<<1)=8,合并后的起始页偏移为10&~(1<<1)=8,如此推导下去,我们可以通过下图和下表更清晰地分析这个过程
![](http://my.csdn.net/uploads/201206/01/1338556256_3278.png)
![](http://my.csdn.net/uploads/201206/01/1338556311_5710.png)
其中pi代表page_idx, ci代表combined_idx
Linux内存释放函数之间的调用关系如下图所示
![](http://my.csdn.net/uploads/201206/01/1338551771_2819.png)
可以看到,落脚点是__free_pages()这个函数,它执行的工作的流程图如下图所示
![](http://my.csdn.net/uploads/201206/01/1338552069_2058.png)
下面是该函数的具体代码
[cpp] view
plaincopy
void __free_pages(struct page *page, unsigned int order)
{
if (put_page_testzero(page)) {/*判断页没有被使用*/
trace_mm_page_free_direct(page, order);
if (order == 0)/*单页则释放到每CPU页框高速缓存中*/
free_hot_page(page);
else /*多页则释放到伙伴系统*/
__free_pages_ok(page, order);
}
}
[align=left]首先该函数要通过页描述符的引用计数来判断该页是否是空闲的[/align]
确定页是空闲的后,再判断要释放多少个页面,如果是单个页面则将该页作为热页释放到pcp中,如果是多页则释
放到伙伴系统中
[cpp] view
plaincopy
void free_hot_page(struct page *page)
{
trace_mm_page_free_direct(page, 0);
free_hot_cold_page(page, 0);
}
free_hot_page是free_hot_cold_page的封装
[cpp] view
plaincopy
static void free_hot_cold_page(struct page *page, int cold)
{
struct zone *zone = page_zone(page);
struct per_cpu_pages *pcp;
unsigned long flags;
int migratetype;
int wasMlocked = __TestClearPageMlocked(page);
kmemcheck_free_shadow(page, 0);
if (PageAnon(page))
page->mapping = NULL;
if (free_pages_check(page))
return;
if (!PageHighMem(page)) {
debug_check_no_locks_freed(page_address(page), PAGE_SIZE);
debug_check_no_obj_freed(page_address(page), PAGE_SIZE);
}
arch_free_page(page, 0);
kernel_map_pages(page, 1, 0);
/*获取对应的pcp结构*/
pcp = &zone_pcp(zone, get_cpu())->pcp;
/*获取迁移类型*/
migratetype = get_pageblock_migratetype(page);
set_page_private(page, migratetype);
local_irq_save(flags);
if (unlikely(wasMlocked))
free_page_mlock(page);
__count_vm_event(PGFREE);
/*
* We only track unmovable, reclaimable and movable on pcp lists.
* Free ISOLATE pages back to the allocator because they are being
* offlined but treat RESERVE as movable pages so we can get those
* areas back if necessary. Otherwise, we may have to free
* excessively into the page allocator
*/
/*只有不可移动页,可回收页和可移动页才能放到每CPU页框高速缓存中,如果
迁移类型不属于这个范围,则要将该页释放回伙伴系统*/
if (migratetype >= MIGRATE_PCPTYPES) {
if (unlikely(migratetype == MIGRATE_ISOLATE)) {
free_one_page(zone, page, 0, migratetype);
goto out;
}
migratetype = MIGRATE_MOVABLE;
}
if (cold)/*冷页插入表尾*/
list_add_tail(&page->lru, &pcp->lists[migratetype]);
else /*热页插入表头*/
list_add(&page->lru, &pcp->lists[migratetype]);
pcp->count++;
/*如果pcp中的页面数超过了high,则释放2^batch个单页给伙伴系统*/
if (pcp->count >= pcp->high) {
free_pcppages_bulk(zone, pcp->batch, pcp);
pcp->count -= pcp->batch;
}
out:
local_irq_restore(flags);
put_cpu();
}
伙伴系统的分支__free_pages_ok()先对释放的页做了些检查,然后具体的释放通过调用free_one_page()-->__free_one_page()来完成
[cpp] view
plaincopy
static inline void __free_one_page(struct page *page,
struct zone *zone, unsigned int order,
int migratetype)
{
unsigned long page_idx;
if (unlikely(PageCompound(page)))
if (unlikely(destroy_compound_page(page, order)))
return;
VM_BUG_ON(migratetype == -1);
/*得到页框在所处最大块中的偏移*/
page_idx = page_to_pfn(page) & ((1 << MAX_ORDER) - 1);
VM_BUG_ON(page_idx & ((1 << order) - 1));
VM_BUG_ON(bad_range(zone, page));
/*只要阶数小于MAX_ORDER-1就有合并的机会*/
while (order < MAX_ORDER-1) {
unsigned long combined_idx;
struct page *buddy;
/*找到page所处块对应的伙伴块*/
buddy = __page_find_buddy(page, page_idx, order);
/*如果伙伴块不是空闲的则不执行下面的合并操作*/
if (!page_is_buddy(page, buddy, order))
break;
/* Our buddy is free, merge with it and move up one order. */
list_del(&buddy->lru);/*将伙伴块从块链表中删除*/
zone->free_area[order].nr_free--;
rmv_page_order(buddy);
/*计算出合并块的起始页框的偏移*/
combined_idx = __find_combined_index(page_idx, order);
/*得到合并块的起始页描述符*/
page = page + (combined_idx - page_idx);
page_idx = combined_idx;/*修改块的起始页偏移*/
order++;/*阶数加1表明合并完成*/
}
/*重新设置块的阶数*/
set_page_order(page, order);
/*将新块添加到对应的链表中*/
list_add(&page->lru,
&zone->free_area[order].free_list[migratetype]);
zone->free_area[order].nr_free++;
}
这里面涉及到两个辅助函数,_page_find_buddy()用来找到是释放块的伙伴,如果找到了一个空闲的伙伴块要通过_find_combined_index()用来定位合并块的起始页框,因为一个块的伙伴块有可能在该块的前面,也有可能在该块的后面,这两个函数的实现非常简洁巧妙,全是通过位操作来实现的
[cpp] view
plaincopy
static inline struct page *
__page_find_buddy(struct page *page, unsigned long page_idx, unsigned int order)
{
unsigned long buddy_idx = page_idx ^ (1 << order);
return page + (buddy_idx - page_idx);
}
[cpp] view
plaincopy
<span style="font-size:12px;">static inline unsigned long
__find_combined_index(unsigned long page_idx, unsigned int order)
{
return (page_idx & ~(1 << order));
}</span>
我们可以通过一个简单的情形来模拟一下这个过程,假设现在有一个将要释放的页,它的order为0,page_idx为10
则先计算它的伙伴 10^(1<<0) = 11,然后计算合并后的起始页偏移为10&~(1<<0) = 10,现在就得到了一个order为1的块,起始页偏移为10,它的伙伴为10^(1<<1)=8,合并后的起始页偏移为10&~(1<<1)=8,如此推导下去,我们可以通过下图和下表更清晰地分析这个过程
![](http://my.csdn.net/uploads/201206/01/1338556256_3278.png)
![](http://my.csdn.net/uploads/201206/01/1338556311_5710.png)
其中pi代表page_idx, ci代表combined_idx
相关文章推荐
- Linux防火墙:iptables禁IP与解封IP常用命令
- Linux防火墙:iptables禁IP与解封IP常用命令
- Linux伙伴系统(三)--分配页
- Linux伙伴系统(二)--伙伴系统的初始化
- Linux伙伴系统(一)--伙伴系统的概述
- Linux<九>程序与资源管理
- Linux下Nagios的安装与配置
- 王保明老师Linux学习笔记---------信号----signal()函数使用实例
- Centos 7 开启端口
- Linux开机自动挂载Windows分区的两种方法
- Linux驱动之内核定时器驱动设计
- 虚拟机出现“内部错误”捉鬼
- Linux的路由和策略路由
- Linux GCC常用命令
- Centos 6.4 python 2.6 升级到 2.7
- Linux 4.3 内核增加了 MOST 驱动子系统
- linux常用命令,知识在于总结
- [置顶] Linux命令大全----常用文件操作命令
- linux 文件查看目录的数
- 优麒麟和搜狗发布搜狗输入法 Linux 版 2.0 版