0 CPU頁幀緩存概念
在前一節(jié)中,我們學(xué)習(xí)了buddy伙伴關(guān)系系統(tǒng),它適用于申請連續(xù)的大塊物理內(nèi)存;而有些時候,經(jīng)常需要申請和釋放單個頁幀。但是,如果使用伙伴關(guān)系系統(tǒng),需要查表、進(jìn)行合并等操作,效率不高。為了提高性能,每個內(nèi)存ZONE區(qū)都提供了一個per-CPU變量,CPU頁幀緩存。每個CPU頁幀緩存都包含一些預(yù)分配好的頁幀,滿足本地CPU發(fā)起的單個頁幀請求。
實際上,每個內(nèi)存ZONE區(qū)和每個CPU都有2個緩存:一個是熱緩存,它存儲頁幀,其內(nèi)容可能包含在CPU的硬件緩存中;另一個是冷緩存。
如果內(nèi)核或用戶進(jìn)程在分配后立即寫入頁幀,那么從熱緩存中獲取頁幀將有利于系統(tǒng)性能。實際上,每次訪問頁幀的某個內(nèi)存位置,都會導(dǎo)致硬件Cache中替換其它頁幀的某一行(Cache-line),當(dāng)然,除非硬件Cache已經(jīng)包含剛剛訪問的“熱”頁幀中內(nèi)存位置的一行。
相反,如果要用DMA操作填充頁幀,則從冷緩存中取頁幀是很方便的。在這種情況下,不涉及CPU,也不會修改硬件Cache的任何行。從冷緩存中取頁幀可以為其他類型的內(nèi)存分配請求保留熱頁幀。
CPU頁幀緩存的數(shù)據(jù)結(jié)構(gòu)是per_cpu_pageset類型的數(shù)組,其存儲在內(nèi)存ZONE描述符中的pageset成員中,如下面的代碼所示:
structzone{ /*...*/ structper_cpu_pagesetpageset[NR_CPUS]; /*...*/ }
數(shù)組個數(shù)與CPU個數(shù)相關(guān),其中的每個數(shù)組元素又包含2個per_cpu_pages描述符成員:一個是熱緩存;另一個是冷緩存。而per_cpu_pages數(shù)據(jù)類型的成員如下表所示:
structper_cpu_pages{ intcount;/*緩存中的頁幀數(shù)量*/ intlow;/*閾值下限,用于緩存補(bǔ)充*/ inthigh;/*閾值上限,需要清空緩存*/ intbatch;/*需從緩存中添加或減少的頁幀數(shù)*/ structlist_headlist;/*緩存中頁幀描述符列表,即內(nèi)存頁列表*/ };
內(nèi)核使用兩個閾值(low和high)監(jiān)控冷/熱緩存的大小:如果頁幀數(shù)量低于閾值,則內(nèi)核使用伙伴系統(tǒng)分配一定數(shù)量的單個頁幀(batch);否則,頁幀數(shù)量超過閾值上限,內(nèi)核將緩存中的頁幀釋放到伙伴系統(tǒng)中(batch)。batch、low和high的值,具體依賴于內(nèi)存ZONE區(qū)的頁幀數(shù)量。
1 通過CPU頁幀緩存分配頁幀
buffered_rmqueue()函數(shù)在給定的內(nèi)存ZONE區(qū)中分配頁幀。它利用CPU頁幀緩存來處理單個頁幀請求。
Linux v2.6.11內(nèi)核源碼實現(xiàn)如下所示(文件位置:/mm/page_alloc.c):
staticstructpage* buffered_rmqueue(structzone*zone,intorder,intgfp_flags) { unsignedlongflags; structpage*page=NULL; intcold=!!(gfp_flags&__GFP_COLD); if(order==0){ structper_cpu_pages*pcp; pcp=&zone->pageset[get_cpu()].pcp[cold]; local_irq_save(flags); if(pcp->count<=?pcp->low) pcp->count+=rmqueue_bulk(zone,0, pcp->batch,&pcp->list); if(pcp->count){ page=list_entry(pcp->list.next,structpage,lru); list_del(&page->lru); pcp->count--; } local_irq_restore(flags); put_cpu(); } if(page==NULL){ spin_lock_irqsave(&zone->lock,flags); page=__rmqueue(zone,order); spin_unlock_irqrestore(&zone->lock,flags); } if(page!=NULL){ BUG_ON(bad_range(zone,page)); mod_page_state_zone(zone,pgalloc,1<
輸入?yún)?shù)分別是內(nèi)存ZONE區(qū)的描述符的地址(zone)、內(nèi)存分配請求大小(2^order)和分配標(biāo)志gfp_flags。如果在gfp_flags中設(shè)置了__GFP_COLD標(biāo)志,則應(yīng)從冷緩存中獲取頁幀,否則應(yīng)從熱緩存中獲取頁幀(此標(biāo)志僅對單個頁幀請求有意義)。該函數(shù)基本上執(zhí)行以下操作:
如果order不等于0,則頁幀緩存不能使用,函數(shù)直接跳轉(zhuǎn)到第4步。
檢查由__GFP_COLD標(biāo)志標(biāo)識的內(nèi)存ZONE區(qū)域的CPU緩存是否必須被補(bǔ)充(per_cpu_pages的count ≤ low)。在本例中,它執(zhí)行以下子步驟:
重復(fù)調(diào)用__rmqueue()函數(shù),從伙伴系統(tǒng)中分配batch個頁幀。
將分配的頁幀描述符插入到緩存的列表中。
更新count變量(將新分配的頁幀數(shù)量加上)。
如果count > 0,從緩存列表中取一個頁幀,然后跳轉(zhuǎn)到第5步。(CPU頁幀緩存可能是空的,在第2步的__rmqueue()沒有申請到頁幀時就會發(fā)生)
到這兒,如果內(nèi)存請求沒有被滿足,調(diào)用__rmqueue()申請從伙伴系統(tǒng)中分配所請求頁幀。
如果內(nèi)存請求被滿足,初始化該頁幀(第1個)的頁描述符:清除某些標(biāo)志、設(shè)置private為0,設(shè)置頁幀引用計數(shù)器為1。另外,如果設(shè)置了__GPF_ZERO,將申請的內(nèi)存清零。
返回頁幀(第1個)的描述符,失敗返回NULL。
2 通過CPU頁幀緩存釋放頁幀
從CPU頁幀緩存中釋放頁幀,使用free_hot_page()和free_cold_page()函數(shù)。它們都是free_hot_cold_page()的封裝函數(shù),如下所示(文件位置:/mm/page_alloc.c):
staticvoidfastcallfree_hot_cold_page(structpage*page,intcold) { structzone*zone=page_zone(page); structper_cpu_pages*pcp; unsignedlongflags; arch_free_page(page,0); kernel_map_pages(page,1,0); inc_page_state(pgfree); if(PageAnon(page)) page->mapping=NULL; free_pages_check(__FUNCTION__,page); pcp=&zone->pageset[get_cpu()].pcp[cold]; local_irq_save(flags); if(pcp->count>=pcp->high) pcp->count-=free_pages_bulk(zone,pcp->batch,&pcp->list,0); list_add(&page->lru,&pcp->list); pcp->count++; local_irq_restore(flags); put_cpu(); }
free_hot_cold_page()接受的參數(shù)是待釋放頁幀的描述符地址page,表示熱緩存還是冷緩存的標(biāo)志cold。
執(zhí)行的步驟如下:
根據(jù)頁幀,獲取page->flags標(biāo)志。
根據(jù)cold標(biāo)志獲取對應(yīng)頁幀緩存的描述符per_cpu_pages地址。
檢查緩存是否不足:如果count ≥ high,調(diào)用free_pages_bulk()函數(shù)。該函數(shù)會重復(fù)調(diào)用__free_pages_bulk()函數(shù)釋放指定的頁幀到伙伴系統(tǒng)中。
將該頁幀添加到緩存列表中,增加count計數(shù)。
應(yīng)該注意的是,在Linux v2.6內(nèi)核中,沒有任何頁幀被釋放到冷緩存中:內(nèi)核總是假設(shè)釋放的頁幀相對于硬件緩存來說是熱的。當(dāng)然,這并不意味著冷緩存是空的:當(dāng)達(dá)到低閾值時,緩存由buffered_rmqueue()補(bǔ)充。
3 移除__GFP_COLD
雖然我們前邊分析了基于冷熱緩存的CPU頁幀緩存,但是,從v4.14版本以后的內(nèi)核中已經(jīng)移除,參考patch。Patches 1-4是與移除冷緩存最相關(guān)的部分;Patches 5-8是可選的,因為它們都是刪除無用但也不影響性能的代碼。
free_hot_cold_page的大多數(shù)調(diào)用者用戶都聲稱被釋放的頁是熱緩存的。唯一的例外是頁回收代碼,因為在不久的將來可能會釋放足夠多的頁,因此CPU的本地頁幀緩存列表將被回收,熱緩存信息將丟失。由于沒有人真正關(guān)心被釋放到分配器的頁的熱信息,所以省略該參數(shù)即可。
審核編輯:劉清
-
計數(shù)器
+關(guān)注
關(guān)注
32文章
2281瀏覽量
95790 -
Cache
+關(guān)注
關(guān)注
0文章
129瀏覽量
28789 -
LINUX內(nèi)核
+關(guān)注
關(guān)注
1文章
317瀏覽量
22076
原文標(biāo)題:Linux內(nèi)核8.5-內(nèi)存管理之CPU本地頁幀緩存
文章出處:【微信號:嵌入式ARM和Linux,微信公眾號:嵌入式ARM和Linux】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
Linux的內(nèi)存管理是什么,Linux的內(nèi)存管理詳解

Linux內(nèi)存管理之頁面回收

關(guān)于Linux內(nèi)存管理的詳細(xì)介紹
Linux內(nèi)核內(nèi)存管理之ZONE內(nèi)存分配器
Linux內(nèi)核內(nèi)存管理之內(nèi)核非連續(xù)物理內(nèi)存分配

HVM的緩存控制與內(nèi)存管理
Linux內(nèi)存管理導(dǎo)讀
Linux性能及調(diào)優(yōu)指南:內(nèi)存架構(gòu)
CPU緩存的作用及原理有哪些
如何在 Linux 上查看本地 DNS 緩存

Linux 內(nèi)存管理總結(jié)

什么是CPU緩存?它有哪些作用?
緩存之美——如何選擇合適的本地緩存?

評論