日期:2014-05-16  浏览次数:20653 次

linux内存伙伴算法(四:释放页)

__free_pages源代码详细分析如下:

fastcall void __free_pages(struct page *page, unsigned int order)
{
	if (put_page_testzero(page)) {//一般来说,只有当页面的引用计数变为0时,才会释放该页,此处进行页面引用计数的判断
		if (order == 0)//如果是释放单个页面,则释放到管理区的每CPU页面缓存中,以加快单页分配速度
			free_hot_page(page);
		else
			__free_pages_ok(page, order);//否则直接释放到伙伴系统中 
	}
}
void fastcall free_hot_page(struct page *page)
{
	free_hot_cold_page(page, 0);
}
free_hot_cold_page源代码详细分析如下:

static void fastcall free_hot_cold_page(struct page *page, int cold)
{
	struct zone *zone = page_zone(page);//找到页面所属的管理区
	struct per_cpu_pages *pcp;//为等下访问冷热区做准备,pcp是一个中间变量
	unsigned long flags;

	if (PageAnon(page))//说明page->mapping的第0位是1,说明该页是匿名页
		page->mapping = NULL;//则使page->mapping指向NULL
	if (free_pages_check(page))//这个函数就是检测page的flag和mapping,_count,_mapcount这几个成员,如果有误,就会进行相应的处理
		return;

	if (!PageHighMem(page))//如果该内存页不在高端内存域
		debug_check_no_locks_freed(page_address(page), PAGE_SIZE);//则调试相关代码
	arch_free_page(page, 0);//只有配置了HAVE_ARCH_FREE_PAGE才会起作用
	kernel_map_pages(page, 1, 0);

	pcp = &zone_pcp(zone, get_cpu())->pcp[cold];//获得本页区的当前cpu,然后找到对应的热区的高速缓存内存,用pcp指针指向它
	local_irq_save(flags);//禁止irq中断
	__count_vm_event(PGFREE);//统计本CPU上的页面释放次数
	list_add(&page->lru, &pcp->list);//把page的lru挂在pcp->list上,挂在pcp->list前面
	set_page_private(page, get_pageblock_migratetype(page));//获取页面的迁移类型,并将其保存在page->private中
	pcp->count++;//更新当前CPU页面缓存计数
	if (pcp->count >= pcp->high) {//页面缓存中保留的页面太多,超过上限了
		free_pages_bulk(zone, pcp->batch, &pcp->list, 0);//将页面缓存中的页面释放一部分到伙伴系统中
		pcp->count -= pcp->batch;//更新当前CPU中页面缓存计数
	}
	local_irq_restore(flags);//恢复中断
	put_cpu();//preempt_enable()是抢占使能
}

free_pages_bulk源代码详细分析如下:
static void free_pages_bulk(struct zone *zone, int count,
					struct list_head *list, int order)
{
	spin_lock(&zone->lock);//上锁
	zone_clear_flag(zone, ZONE_ALL_UNRECLAIMABLE);//在释放页之前,先将内存区中页不可回收的标志清除
	zone->pages_scanned = 0;//在上一次成功换出页之后,已扫描页计数器
	while (count--) {//循环,直到把count个页面全部释放
		struct page *page;

		VM_BUG_ON(list_empty(list));
		page = list_entry(list->prev, struct page, lru);//通过pcp->list找到第一个lru,再通过lru成员找到对应的struct page
		/* have to delete it as __free_one_page list manipulates */
		list_del(&page->lru);//lru取出后,pcp->list链表就不再指向它了,就要从链表里面移除
		__free_one_page(page, zone, order);//这是释放函数的主体,下面会详细讨论
	}
	spin_unlock(&zone->lock);//解锁
}

_free_pages_ok源代码详细分析如下:
static void __free_pages_ok(struct page *page, unsigned int order)
{
	unsigned long flags;
	int i;
	int reserved = 0;

	for (i = 0 ; i < (1 << order) ; ++i)
		reserved += free_pages_check(page + i);//在释放页之前,还得先用free_pages_check函数检查页是否有错误,如果有错误就要进行相应的处理,如果没有错误,还需要记录所有页面中需要保留的页面数
	if (reserved)//如果在需要释放的一段内存中有需要保留的内存页,那么该段内存就不能释放
		return;

	if (!PageHighMem(page))//判断页是否在高端内存域中
		debug_check_no_locks_freed(page_address(page),PAGE_SIZE<<order);//调试用的相关代码,还不理解,还望指点!
	arch_free_page(page, order);//只有配置了HAVE_ARCH_FREE_PAGE才会起作用,也不理解,还望指点
	kernel_map_pages(page, 1 << order, 0);

	local_irq_save(flags);//禁止中断
	__count_vm_events(PGFREE, 1 << order);//统计本CPU上释放的页数
	free_one_page(page_zone(page), page, order);//释放函数的主体__free_one_page的封装
	local_irq_restore(flags);//恢复中断
}

static void free_one_page(struct zone *zone, struct page *page, int order)
{
	spin_lock(&zone->lock);//获得管理区得自旋锁
	zone_clear_flag(zone, ZONE_ALL_UNRECLAIMABLE);//在释放之前,先将内存区中页的不可回收标志清除
	zone->pages_scanned = 0;//初始化在上一次成功换出页之后,已扫描页的数目。目前正在释放内存,将此清0,待回收过程随后回收时重新计数
	__free_one_page(page, zone, order);//释放函数的主体
	spin_unlock(&zone->lock);//解锁,有关锁的机制,自己还没有看到,后面的Blog会详细分析
}

__free_one_page源代码详细分析如下:
static inline void __free_one_page(struct page *page,
		struct zone *zone, unsigned int order)
{
	unsigned long page_idx;
	int order_size = 1 << order;
	int migratetype = get_pageblock_migratetype(page);

	if (unlikely(PageCompound(page)))//要释放的页是巨页的一部分
		destroy_compound_page(page, order);/