linux释放内存页面
一、简介
? linux内核中释放页面的核心函数是free_page() 。该函数最后会调用__free_pages() 函数。本文将分析该该函数。
二、__free_pages函数
? __free_pages 函数定义如下(/mm/page_alloc.c):
void __free_pages(struct page *page, unsigned int order)
{
if (put_page_testzero(page)) {
if (order == 0)
free_hot_cold_page(page, false);
else
__free_pages_ok(page, order);
}
}
由以上代码可知,根据order 是否等于0分为了两个执行流:
(1)free_hot_cold_page(page, false)执行流
(2)__free_pages_ok(page, order)执行流
(2-1)free_hot_cold_page()执行流
? (【注】该执行流属于特殊情况)
函数定义如下(/mm/page_alloc.c):
void free_hot_cold_page(struct page *page, bool cold)
{
struct zone *zone = page_zone(page);
struct per_cpu_pages *pcp;
unsigned long flags;
unsigned long pfn = page_to_pfn(page);
int migratetype;
if (!free_pages_prepare(page, 0))
return;
migratetype = get_pfnblock_migratetype(page, pfn);
set_freepage_migratetype(page, migratetype);
local_irq_save(flags);
__count_vm_event(PGFREE);
if (migratetype >= MIGRATE_PCPTYPES) {
if (unlikely(is_migrate_isolate(migratetype))) {
free_one_page(zone, page, pfn, 0, migratetype);
goto out;
}
migratetype = MIGRATE_MOVABLE;
}
pcp = &this_cpu_ptr(zone->pageset)->pcp;
if (!cold)
list_add(&page->lru, &pcp->lists[migratetype]);
else
list_add_tail(&page->lru, &pcp->lists[migratetype]);
pcp->count++;
if (pcp->count >= pcp->high) {
unsigned long batch = READ_ONCE(pcp->batch);
free_pcppages_bulk(zone, batch, pcp);
pcp->count -= batch;
}
out:
local_irq_restore(flags);
}
以上代码片段中,出现了struct per_cpu_pages结构体, struct per_cpu_pages 定义如下:
struct per_cpu_pages {
int count;
int high;
int batch;
struct list_head lists[MIGRATE_PCPTYPES];
};
【参数含义】
- count:list中的page数量。
- hign:指示当缓存中的页面高于该水位值时,将回收页面到伙伴系统。
- batch:表示一次回收到伙伴系统的页面数量。(batch值是通过
zone_batchsize() 函数计算出来的)
zone_batchsize()函数定义如下(/mm/page_alloc.c):
static int zone_batchsize(struct zone *zone)
{
#ifdef CONFIG_MMU
int batch;
batch = zone->managed_pages / 1024;
if (batch * PAGE_SIZE > 512 * 1024)
batch = (512 * 1024) / PAGE_SIZE;
batch /= 4;
if (batch < 1)
batch = 1;
batch = rounddown_pow_of_two(batch + batch/2) - 1;
return batch;
#else
return 0;
#endif
}
以下代码中,如果cold为false,页面将以堆栈方式释放到per_cpu_page->list 对应链表中;如果cold为true,页面以队列方式释放到per_cpu_page->list 对应链表中。
if (!cold)
list_add(&page->lru, &pcp->lists[migratetype]);
else
list_add_tail(&page->lru, &pcp->lists[migratetype]);
(2-2)__free_pages_ok()执行流
? (【注】该执行流属于正常情况)
__free_pages_ok() 函数定义如下(/mm/page_alloc.c):
static void __free_pages_ok(struct page *page, unsigned int order)
{
unsigned long flags;
int migratetype;
unsigned long pfn = page_to_pfn(page);
if (!free_pages_prepare(page, order))
return;
migratetype = get_pfnblock_migratetype(page, pfn);
local_irq_save(flags);
__count_vm_events(PGFREE, 1 << order);
set_freepage_migratetype(page, migratetype);
free_one_page(page_zone(page), page, pfn, order, migratetype);
local_irq_restore(flags);
}
以上代码中,调用free_one_page() 函数来释放页面。该函数将调用__free_one_page() 来执行核心操作。这个函数功能比较强大,不仅释放内存页面到伙伴系统,而且还将处理空闲页面的合并工作。具体定义如下(/mm/page_alloc.c):
static inline void __free_one_page(struct page *page,
unsigned long pfn,
struct zone *zone, unsigned int order,
int migratetype)
{
unsigned long page_idx;
unsigned long combined_idx;
unsigned long uninitialized_var(buddy_idx);
struct page *buddy;
int max_order = MAX_ORDER;
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 (is_migrate_isolate(migratetype)) {
max_order = min(MAX_ORDER, pageblock_order + 1);
} else {
__mod_zone_freepage_state(zone, 1 << order, migratetype);
}
page_idx = pfn & ((1 << max_order) - 1);
VM_BUG_ON_PAGE(page_idx & ((1 << order) - 1), page);
VM_BUG_ON_PAGE(bad_range(zone, page), page);
while (order < max_order - 1) {
buddy_idx = __find_buddy_index(page_idx, order);
buddy = page + (buddy_idx - page_idx);
if (!page_is_buddy(page, buddy, order))
break;
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_idx = buddy_idx & page_idx;
page = page + (combined_idx - page_idx);
page_idx = combined_idx;
order++;
}
set_page_order(page, order);
if ((order < MAX_ORDER-2) && pfn_valid_within(page_to_pfn(buddy))) {
struct page *higher_page, *higher_buddy;
combined_idx = buddy_idx & page_idx;
higher_page = page + (combined_idx - page_idx);
buddy_idx = __find_buddy_index(combined_idx, order + 1);
higher_buddy = higher_page + (buddy_idx - combined_idx);
if (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++;
}
【总结一下】以上代码功能主要有两个:1、释放页面到伙伴系统。2、合并空闲内存块。
(1)【释放页面到伙伴系统】:将页面添加到伙伴系统中适当的free_area 链表中。
(2)【合并空闲内存块】:当在释放内存块的时候,会查询相邻内存块是否空闲,如果存在空闲的内存块,那么将合并这两个内存块成为一个更大的内存块,并将其放置到高阶free_area 空闲链表中。该过程是一个循环过程,直到所有可能合并的内存块合并完成后才结束循环。
本文完! 小生由于精力和知识有限,如遇分享的文章存在不妥的地方,请多多批评,也可与我一起讨论。哈哈
|