分类: LINUX
2015-11-17 15:06:02
法律声明:《LINUX3.0内核源代码分析》系列文章由谢宝友()发表于http://xiebaoyou.blog.chinaunix.net,文章中的LINUX3.0源代码遵循GPL协议。除此以外,文档中的其他内容由作者保留所有版权。谢绝转载。
释放页面的主函数是__free_pages:
/**
* 释放内存页
* page: 要释放的内存页
* order: 与释放的页数量为2^order。
*/
void __free_pages(struct page *page, unsigned int order)
{
if (put_page_testzero(page)) {/* 一般来说,应当在页面引用计数变为0时才会调用释放函数,这里进行引用计数判断是安全起见 */
if (order == 0)/* 如果是释放单个页面,则释放到管理区的每CPU页面缓存中,以加快单页分配速度 */
free_hot_cold_page(page, 0);
else
__free_pages_ok(page, order);/* 否则直接释放到伙伴系统中 */
}
}
/**
* 释放单个页面到伙伴系统(页面缓存)中。
* page: 要释放的页面
* cold: 要释放的页面是热页还是冷页。决定是页面放入热池还是冷池。
*/
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);/* 页面是否被锁住 */
/**
* free_pages_prepare主要是检查页面状态是否允许释放,避免错误的释放页面。
* 并且处理一些调试相关的事务。
*/
if (!free_pages_prepare(page, 0))
return;
migratetype = get_pageblock_migratetype(page);
set_page_private(page, migratetype);
local_irq_save(flags);
if (unlikely(wasMlocked))/* 如果页面是由mlock锁住的页,这里调用free_page_mlock进行统计计数 */
free_page_mlock(page);
__count_vm_event(PGFREE);/* 统计本CPU上的页面释放次数 */
/*
* 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
*/
if (migratetype >= MIGRATE_PCPTYPES) {/* 该页面目前不受每CPU的PCP链表管理 */
if (unlikely(migratetype == MIGRATE_ISOLATE)) {/* 该页面是从邻近的CPU管理区中调配过来的,仍然放到MIGRATE_ISOLATE中,不转化为可迁移的页面 */
free_one_page(zone, page, 0, migratetype);
goto out;
}
/* 如果是从保留的链表中分配的页面,也将其放入可移动页中 */
migratetype = MIGRATE_MOVABLE;
}
/* 取得本CPU的页面缓存对象 */
pcp = &this_cpu_ptr(zone->pageset)->pcp;
if (cold)/* 如果是冷页,则CPU缓存中还没有页面数据,将页面放入页面缓存链表的尾部。 */
list_add_tail(&page->lru, &pcp->lists[migratetype]);
else
list_add(&page->lru, &pcp->lists[migratetype]);/* 否则放入列表的首部,这样后续的分配过程可以利用CPU缓存中的热缓存 */
pcp->count++;/* 当前CPU页面缓存计数 */
if (pcp->count >= pcp->high) {/* 页面缓存中保留的页面太多,超过上限了 */
free_pcppages_bulk(zone, pcp->batch, pcp);/* 将页面缓存中的页面释放一部分到伙伴系统中 */
pcp->count -= pcp->batch;/* 递减PCP中的缓存页面数量 */
}
out:
local_irq_restore(flags);/* 恢复中断 */
}
/**
* 直接释放页面到伙伴系统中
*/
static void __free_pages_ok(struct page *page, unsigned int order)
{
unsigned long flags;
int wasMlocked = __TestClearPageMlocked(page);
if (!free_pages_prepare(page, order))/* 如果释放页面失败(如重要的管理结构被破坏,或者错误的调用参数)则退出 */
return;
local_irq_save(flags);/* 接下来要操作伙伴系统了,由于在中断里面也会操作伙伴系统,因此这里需要关中断 */
if (unlikely(wasMlocked))/* 如果页面是被mlock系统调用锁住的,就进行一些统计计数 */
free_page_mlock(page);
__count_vm_events(PGFREE, 1 << order);/* 统计计数,对释放的页面数量进行计数 */
free_one_page(page_zone(page), page, order,
get_pageblock_migratetype(page));/* 将页面释放到伙伴系统中 */
local_irq_restore(flags);/* 恢复中断 */
}
可以看到,释放页面的流程比较简单,基本上就是伙伴系统函数的简单封装。接下来我们开始介绍伙伴系统。