什么是冷熱頁(yè)?
在Linux Kernel的物理內(nèi)存管理的Buddy System中,引入了冷熱頁(yè)的概念。冷頁(yè)表示該空閑頁(yè)已經(jīng)不再高速緩存中了(一般是指L2 Cache),熱頁(yè)表示該空閑頁(yè)仍然在高速緩存中。冷熱頁(yè)是針對(duì)于每CPU的,每個(gè)zone中,都會(huì)針對(duì)于所有的CPU初始化一個(gè)冷熱頁(yè)的per-cpu-pageset.
為什么要有冷熱頁(yè)?
作用有3點(diǎn):
Buddy Allocator在分配order為0的空閑頁(yè)的時(shí)候,如果分配一個(gè)熱頁(yè),那么由于該頁(yè)已經(jīng)存在于L2 Cache中了。CPU寫(xiě)訪問(wèn)的時(shí)候,不需要先把內(nèi)存中的內(nèi)容讀到Cache中,然后再寫(xiě)。如果分配一個(gè)冷頁(yè),說(shuō)明該頁(yè)不在L2 Cache中。一般情況下,盡可能用熱頁(yè),是容易理解的。什么時(shí)候用冷頁(yè)呢?While allocating a physical page frame, there is a bit specifying whether we would like a hot or a cold page (that is, a page likely to be in the CPU cache, or a page not likely to be there). If the page will be used by the CPU, a hot page will be faster. If the page will be used for device DMA the CPU cache would be invalidated anyway, and a cold page does not waste precious cache contents.
簡(jiǎn)單翻譯一下:當(dāng)內(nèi)核分配一個(gè)物理頁(yè)框時(shí),有一些規(guī)范來(lái)約束我們是分配熱頁(yè)還是冷頁(yè)。當(dāng)頁(yè)框是CPU使用的,則分配熱頁(yè)。當(dāng)頁(yè)框是DMA設(shè)備使用的,則分配冷頁(yè)。因?yàn)镈MA設(shè)備不會(huì)用到CPU高速緩存,所以沒(méi)必要使用熱頁(yè)。
Buddy System在給某個(gè)進(jìn)程分配某個(gè)zone中空閑頁(yè)的時(shí)候,首先需要用自旋鎖鎖住該zone,然后分配頁(yè)。這樣,如果多個(gè)CPU上的進(jìn)程同時(shí)進(jìn)行分配頁(yè),便會(huì)競(jìng)爭(zhēng)。引入了per-cpu-set后,當(dāng)多個(gè)CPU上的進(jìn)程同時(shí)分配頁(yè)的時(shí)候,競(jìng)爭(zhēng)便不會(huì)發(fā)生,提高了效率。另外當(dāng)釋放單個(gè)頁(yè)面時(shí),空閑頁(yè)面首先放回到per-cpu-pageset中,以減少zone中自旋鎖的使用。當(dāng)頁(yè)面緩存中的頁(yè)面數(shù)量超過(guò)閥值時(shí),再將頁(yè)面放回到伙伴系統(tǒng)中。
使用每CPU冷熱頁(yè)還有一個(gè)好處是,能保證某個(gè)頁(yè)一直黏在1個(gè)CPU上,這有助于提高Cache的命中率。
冷熱頁(yè)的數(shù)據(jù)結(jié)構(gòu)
struct per_cpu_pages { int count; // number of pages in the list int high; // high watermark, emptying needed int batch; // chunk size for buddy add/remove // Lists of pages, one per migrate type stored on the pcp-lists 每個(gè)CPU在每個(gè)zone上都有MIGRATE_PCPTYPES個(gè)冷熱頁(yè)鏈表(根據(jù)遷移類(lèi)型劃分) struct list_head lists[MIGRATE_PCPTYPES]; }; 在Linux中,對(duì)于UMA的架構(gòu),冷熱頁(yè)是在一條鏈表上進(jìn)行管理。熱頁(yè)在前,冷頁(yè)在后。CPU每釋放一個(gè)order為0的頁(yè),如果per-cpu-pageset中的頁(yè)數(shù)少于其指定的閾值,便會(huì)將釋放的頁(yè)插入到冷熱頁(yè)鏈表的開(kāi)始處。這樣,之前插入的熱頁(yè)便會(huì)隨著其后熱頁(yè)源源不斷的插入向后移動(dòng),其頁(yè)由熱變冷的幾率便大大增加。
怎樣分配冷熱頁(yè)
在分配order為0頁(yè)的時(shí)候(冷熱頁(yè)機(jī)制只處理單頁(yè)分配的情況),先找到合適的zone,然后根據(jù)需要的migratetype類(lèi)型定位冷熱頁(yè)鏈表(每個(gè)zone,對(duì)于每個(gè)cpu,有3條冷熱頁(yè)鏈表,對(duì)應(yīng)于:MIGRATE_UNMOVABLE、MIGRATE_RECLAIMABLE、MIGRATE_MOVABLE)。若需要熱頁(yè),則從鏈表頭取下一頁(yè)(此頁(yè)最“熱”);若需要冷頁(yè),則從鏈表尾取下一頁(yè)(此頁(yè)最“冷”)。
分配函數(shù)(關(guān)鍵部分已添加注釋?zhuān)?/p>
/* * Really, prep_compound_page() should be called from __rmqueue_bulk(). But * we cheat by calling it from here, in the order > 0 path. Saves a branch * or two. */static inlinestruct page *buffered_rmqueue(struct zone *preferred_zone, struct zone *zone, int order, gfp_t gfp_flags, int migratetype){ unsigned long flags; struct page *page; //分配標(biāo)志是__GFP_COLD才分配冷頁(yè) int cold = !!(gfp_flags & __GFP_COLD);again: if (likely(order == 0)) { struct per_cpu_pages *pcp; struct list_head *list; local_irq_save(flags); pcp = &this_cpu_ptr(zone->pageset)->pcp; list = &pcp->lists[migratetype]; if (list_empty(list)) { //如果缺少頁(yè),則從Buddy System中分配。 pcp->count += rmqueue_bulk(zone, 0, pcp->batch, list, migratetype, cold); if (unlikely(list_empty(list))) goto failed; } if (cold) //分配冷頁(yè)時(shí),從鏈表尾部分配,list為鏈表頭,list->prev表示鏈表尾 page = list_entry(list->prev, struct page, lru); else //分配熱頁(yè)時(shí),從鏈表頭分配 page = list_entry(list->next, struct page, lru); //分配完一個(gè)頁(yè)框后從冷熱頁(yè)鏈表中刪去該頁(yè) list_del(&page->lru); pcp->count--; } else {//如果order!=0(頁(yè)框數(shù)>1),則不從冷熱頁(yè)鏈表中分配 if (unlikely(gfp_flags & __GFP_NOFAIL)) { /* * __GFP_NOFAIL is not to be used in new code. * * All __GFP_NOFAIL callers should be fixed so that they * properly detect and handle allocation failures. * * We most definitely don't want callers attempting to * allocate greater than order-1 page units with * __GFP_NOFAIL. */ WARN_ON_ONCE(order > 1); } spin_lock_irqsave(&zone->lock, flags); page = __rmqueue(zone, order, migratetype); spin_unlock(&zone->lock); if (!page) goto failed; __mod_zone_page_state(zone, NR_FREE_PAGES, -(1 << order)); } __count_zone_vm_events(PGALLOC, zone, 1 << order); zone_statistics(preferred_zone, zone, gfp_flags); local_irq_restore(flags); VM_BUG_ON(bad_range(zone, page)); if (prep_new_page(page, order, gfp_flags)) goto again; return page;failed: local_irq_restore(flags); return NULL;}以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持VEVB武林網(wǎng)。
新聞熱點(diǎn)
疑難解答
圖片精選