最底层接口:
__rmqueue(struct zone *zone, unsigned int order) //申请页框
=>for (current_order = order; current_order < MAX_ORDER; ++current_order) //从当前order开始,小内存小order不够塞牙缝的
area = zone->free_area + current_order; //瞄准,找到order配套的的area
if (list_empty(&area->free_list))//当前order为空则不要浪费时间,找更大的大树傍
continue;
page = list_entry(area->free_list.next, struct page, lru); //定位到了,每个page是通过lru链接起来的,每次都是用链表头部的page,最热乎的
rmv_page_order(page);//撒泡尿,这是我的地盘
area->nr_free--;
__mod_zone_page_state(zone, NR_FREE_PAGES, - (1UL << order));
expand(zone, page, order, current_order, area);
=>while (high > low) {//插入到不同的order链表里面
area--;
high--;
size >>= 1;
VM_BUG_ON(bad_range(zone, &page[size]));
list_add(&page[size].lru, &area->free_list);
area->nr_free++;
set_page_order(&page[size], high);
}
free_pages_bulk(struct zone *zone, int count, struct list_head *list, int order)//释放count个数目的内存
=>while (count--)
page = list_entry(list->prev, struct page, lru);//从屁股开始数,往前释放
__free_one_page(page, zone, order);
=>page_idx = page_to_pfn(page) & ((1 << MAX_ORDER) - 1);
=>while (order < MAX_ORDER-1) {
unsigned long combined_idx;
struct free_area *area;
struct page *buddy;
buddy = __page_find_buddy(page, page_idx, order);
if (!page_is_buddy(page, buddy, order))
break; /* Move the buddy up one level. */
list_del(&buddy->lru);
area = zone->free_area + order;
area->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++;
}
=>set_page_order(page, order);//擦干尿,拍拍屁股走人
list_add(&page->lru, &zone->free_area[order].free_list);
zone->free_area[order].nr_free++;
中间层接口
buffered_rmqueue //在指定的内存管理区分配页框
=>if (likely(order == 0))//页高速缓存只针对于order为0的场景
pcp->count = rmqueue_bulk(zone, 0, pcp->batch, &pcp->list);
=>else
page = __rmqueue(zone, order);
free_hot_cold_page(struct page *page, int cold)//释放页框到每个CPU页框高速缓存
=>if (pcp->count >= pcp->high)
free_pages_bulk(zone, pcp->batch, &pcp->list, 0);
最上层接口:伙伴系统的对外接口
__alloc_pages(gfp_t gfp_mask, unsigned int order, struct zonelist *zonelist)
=>page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, order, zonelist, ALLOC_WMARK_LOW|ALLOC_CPUSET);
=>page = buffered_rmqueue(zonelist, zone, order, gfp_mask);
=>wakeup_kswapd(*z, order); //如果page为0,则kswapd想办法回收页框
=>page = get_page_from_freelist(gfp_mask, order, zonelist, alloc_flags);//再申请
/* go through the zonelist yet again, ignoring mins */ //再申请不到的话忽略最低阈值申请
=>page = get_page_from_freelist(gfp_mask, order, zonelist, ALLOC_NO_WATERMARKS);
//杀死进程再看看是否有
=>page = get_page_from_freelist(gfp_mask, order, zonelist, alloc_flags);
void __free_pages(struct page *page, unsigned int order)
=>if (order == 0)
free_hot_page(page);
=>free_hot_cold_page(page, 0);
=>else
__free_pages_ok(page, order);
=>free_one_page(page_zone(page), page, order);
=>__free_one_page(page, zone, order);
slab通关调到伙伴系统
kmem_cache_alloc
=>__cache_alloc(cachep, flags, __builtin_return_address(0))
=>objp = __do_cache_alloc(cachep, flags);
=>____cache_alloc(cachep, flags);
=>ac = cpu_cache_get(cachep);
=>objp = cache_alloc_refill(cachep, flags);
=>x = cache_grow(cachep, flags | GFP_THISNODE, node, NULL);//如果没有空闲的slab节点,求助buddy伙伴系统
=>objp = kmem_getpages(cachep, local_flags, nodeid); //slab与伙伴系统的接口,与之对应的是kmem_freepages
=> page = alloc_pages_node(nodeid, flags, cachep->gfporder);//page是物理地址
=>__alloc_pages(gfp_mask, order, NODE_DATA(nid)->node_zonelists + gfp_zone(gfp_mask));
=>for (i = 0; i < nr_pages; i++)
__SetPageSlab(page + i); //设置page的slab标记位
=>return page_address(page);//返回虚拟地址
=>if (!PageHighMem(page))
return lowmem_page_address(page) //直接映射地址
=>return __va(page_to_pfn(page) << PAGE_SHIFT);
=>#define __page_to_pfn(page) ((unsigned long)((page) - mem_map) + ARCH_PFN_OFFSET)
=>#define __va(x) ((void *)((unsigned long)(x) + PAGE_OFFSET))
#define PAGE_OFFSET ASM_CONST(CONFIG_KERNEL_START)
=>pas = page_slot(page);
=>return &page_address_htable[hash_ptr(page, PA_HASH_ORDER)]; //高端内存的页通过哈希表查找
=>if (!list_empty(&pas->lh)) {
struct page_address_map *pam;
list_for_each_entry(pam, &pas->lh, list) { //冲突检测
if (pam->page == page) {
ret = pam->virtual;
goto done;
}
}
}
=>slabp = alloc_slabmgmt(cachep, objp, offset, local_flags & ~GFP_THISNODE, nodeid);
=>slab_map_pages(cachep, slabp, objp);//**建立page和slab的关联,使用lru指针**
=>page = virt_to_page(addr);
nr_pages = 1;
if (likely(!PageCompound(page)))
nr_pages <<= cache->gfporder;
do {
page_set_cache(page, cache);
=>page->lru.next = (struct list_head *)cache;
page_set_slab(page, slab);
=>page->lru.prev = (struct list_head *)slab;
page++; //page的lru指针加入slab
} while (--nr_pages);
=>cache_init_objs(cachep, slabp);
=>list_add_tail(&slabp->list, &(l3->slabs_free));
=>l3->free_objects += cachep->num;
kmem_cache_destroy
=>__kmem_cache_destroy(cachep);
=>kmem_cache_free(&cache_cache, cachep);
=>__cache_free(cachep, objp);
=>cache_flusharray(cachep, ac);
=>free_block(cachep, ac->entry, batchcount, node);
=>slab_destroy(cachep, slabp);
=>kmem_freepages(cachep, addr);
=>free_pages((unsigned long)addr, cachep->gfporder);
=>if (order == 0)
free_hot_page(page);
=>free_hot_cold_page(page, 0);
else
__free_pages_ok(page, order);
=>__free_pages_ok(page, order);
Linux中的内存分配和释放之__alloc_pages()函数分析
https://blog.csdn.net/satanwxd/article/details/5357691