Linux內核伙伴系統中頁面釋放,主函數為free_pages()
一、上層操作
- /*用虛擬地址進行釋放*/
- void free_pages(unsigned long addr, unsigned int order)
- {
- if (addr != 0) {
- VM_BUG_ON(!virt_addr_valid((void *)addr));
- __free_pages(virt_to_page((void *)addr), order);/*具體的釋放函數*/
- }
- }
- /*釋放頁面*/
- void __free_pages(struct page *page, unsigned int order)
- {
- if (put_page_testzero(page)) {/*count值減一為0時釋放*/
- /*調試*/
- trace_mm_page_free_direct(page, order);
- if (order == 0)
- free_hot_page(page);/*釋放單個頁面*/
- else
- __free_pages_ok(page, order);
- }
- }
二、釋放單個頁面
釋放單個頁面free_hot_page()調用free_hot_cold_page()函數
- 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);
- }
- /*x86下為空*/
- arch_free_page(page, 0);
- /*調試用*/
- kernel_map_pages(page, 1, 0);
- /*獲得zone對應cpu的pcp*/
- pcp = &zone_pcp(zone, get_cpu())->pcp;
- /*獲得頁面的migratetype*/
- 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
- */
- if (migratetype >= MIGRATE_PCPTYPES) {
- if (unlikely(migratetype == MIGRATE_ISOLATE)) {
- /*釋放到伙伴系統 */
- free_one_page(zone, page, 0, migratetype);
- goto out;
- }
- migratetype = MIGRATE_MOVABLE;
- }
-
- if (cold)/*加入到pcp鏈表尾部*/
- list_add_tail(&page->lru, &pcp->lists[migratetype]);
- else/*加入到pcp鏈表頭部*/
- list_add(&page->lru, &pcp->lists[migratetype]);
- pcp->count++;/*pcp計數加一*/
- if (pcp->count >= pcp->high) {/*當pcp中頁面數量超過他的最高值時,
- 釋放pcp->batch個頁面到伙伴系統中*/
- free_pcppages_bulk(zone, pcp->batch, pcp);
- pcp->count -= pcp->batch;/*頁面數減去釋放的頁面數量*/
- }
-
- out:
- local_irq_restore(flags);/*回復中斷*/
- put_cpu();
- }
從pcp中釋放頁面到伙伴系統中
free_pcppages_bulk()
- are in same zone, and of same order.
- * count is the number of pages to free.
- *
- * If the zone was previously in an "all pages pinned" state then look to
- * see if this freeing clears that state.
- *
- * And clear the zone's pages_scanned counter, to hold off the "all pages are
- * pinned" detection logic.
- */
- /*從PCP中釋放count個頁面到伙伴系統中*/
- static void free_pcppages_bulk(struct zone *zone, int count,
- struct per_cpu_pages *pcp)
- {
- int migratetype = 0;
- int batch_free = 0;
- /*
- * 雖然管理區可以按照CPU節點分類,但是也可以跨CPU節點進行內存分配,
- * 因此這裡需要用自旋鎖保護管理區
- * 使用每CPU緩存的目的,也是為了減少使用這把鎖。
- */
- spin_lock(&zone->lock);
- /* all_unreclaimable代表了內存緊張程度,釋放內存後,將此標志清除 */
- zone_clear_flag(zone, ZONE_ALL_UNRECLAIMABLE);
- zone->pages_scanned = 0;/* pages_scanned代表最後一次內存緊張以來,頁面回收過程已經掃描的頁數。
- 目前正在釋放內存,將此清0,待回收過程隨後回收時重新計數 */
-
- /*增加管理區空閒頁數*/
- __mod_zone_page_state(zone, NR_FREE_PAGES, count);
- while (count) {
- struct page *page;
- struct list_head *list;
-
- /*
- * Remove pages from lists in a round-robin fashion. A
- * batch_free count is maintained that is incremented when an
- * empty list is encountered. This is so more pages are freed
- * off fuller lists instead of spinning excessively around empty
- * lists
- */
- do {/*從pcp的三類鏈表中找出不空的一個,釋放*/
- batch_free++;/*參看英文注釋*/
- if (++migratetype == MIGRATE_PCPTYPES)
- migratetype = 0;
- list = &pcp->lists[migratetype];
- } while (list_empty(list));
-
- do {
- page = list_entry(list->prev, struct page, lru);
- /* must delete as __free_one_page list manipulates */
- list_del(&page->lru);
- /*釋放單個頁面到伙伴系統,注意這裡的分類回收*/
- __free_one_page(page, zone, 0, migratetype);
- trace_mm_page_pcpu_drain(page, 0, migratetype);
- } while (--count && --batch_free && !list_empty(list));
- }
- spin_unlock(&zone->lock);
- }