0 CPU頁(yè)幀緩存概念
在前一節(jié)中,我們學(xué)習(xí)了buddy伙伴關(guān)系系統(tǒng),它適用于申請(qǐng)連續(xù)的大塊物理內(nèi)存;而有些時(shí)候,經(jīng)常需要申請(qǐng)和釋放單個(gè)頁(yè)幀。但是,如果使用伙伴關(guān)系系統(tǒng),需要查表、進(jìn)行合并等操作,效率不高。為了提高性能,每個(gè)內(nèi)存ZONE區(qū)都提供了一個(gè)per-CPU變量,CPU頁(yè)幀緩存。每個(gè)CPU頁(yè)幀緩存都包含一些預(yù)分配好的頁(yè)幀,滿足本地CPU發(fā)起的單個(gè)頁(yè)幀請(qǐng)求。
實(shí)際上,每個(gè)內(nèi)存ZONE區(qū)和每個(gè)CPU都有2個(gè)緩存:一個(gè)是熱緩存,它存儲(chǔ)頁(yè)幀,其內(nèi)容可能包含在CPU的硬件緩存中;另一個(gè)是冷緩存。
如果內(nèi)核或用戶進(jìn)程在分配后立即寫入頁(yè)幀,那么從熱緩存中獲取頁(yè)幀將有利于系統(tǒng)性能。實(shí)際上,每次訪問(wèn)頁(yè)幀的某個(gè)內(nèi)存位置,都會(huì)導(dǎo)致硬件Cache中替換其它頁(yè)幀的某一行(Cache-line),當(dāng)然,除非硬件Cache已經(jīng)包含剛剛訪問(wèn)的“熱”頁(yè)幀中內(nèi)存位置的一行。
相反,如果要用DMA操作填充頁(yè)幀,則從冷緩存中取頁(yè)幀是很方便的。在這種情況下,不涉及CPU,也不會(huì)修改硬件Cache的任何行。從冷緩存中取頁(yè)幀可以為其他類型的內(nèi)存分配請(qǐng)求保留熱頁(yè)幀。
CPU頁(yè)幀緩存的數(shù)據(jù)結(jié)構(gòu)是per_cpu_pageset類型的數(shù)組,其存儲(chǔ)在內(nèi)存ZONE描述符中的pageset成員中,如下面的代碼所示:
structzone{ /*...*/ structper_cpu_pagesetpageset[NR_CPUS]; /*...*/ }
數(shù)組個(gè)數(shù)與CPU個(gè)數(shù)相關(guān),其中的每個(gè)數(shù)組元素又包含2個(gè)per_cpu_pages描述符成員:一個(gè)是熱緩存;另一個(gè)是冷緩存。而per_cpu_pages數(shù)據(jù)類型的成員如下表所示:
structper_cpu_pages{ intcount;/*緩存中的頁(yè)幀數(shù)量*/ intlow;/*閾值下限,用于緩存補(bǔ)充*/ inthigh;/*閾值上限,需要清空緩存*/ intbatch;/*需從緩存中添加或減少的頁(yè)幀數(shù)*/ structlist_headlist;/*緩存中頁(yè)幀描述符列表,即內(nèi)存頁(yè)列表*/ };
內(nèi)核使用兩個(gè)閾值(low和high)監(jiān)控冷/熱緩存的大小:如果頁(yè)幀數(shù)量低于閾值,則內(nèi)核使用伙伴系統(tǒng)分配一定數(shù)量的單個(gè)頁(yè)幀(batch);否則,頁(yè)幀數(shù)量超過(guò)閾值上限,內(nèi)核將緩存中的頁(yè)幀釋放到伙伴系統(tǒng)中(batch)。batch、low和high的值,具體依賴于內(nèi)存ZONE區(qū)的頁(yè)幀數(shù)量。
1 通過(guò)CPU頁(yè)幀緩存分配頁(yè)幀
buffered_rmqueue()函數(shù)在給定的內(nèi)存ZONE區(qū)中分配頁(yè)幀。它利用CPU頁(yè)幀緩存來(lái)處理單個(gè)頁(yè)幀請(qǐng)求。
Linux v2.6.11內(nèi)核源碼實(shí)現(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<
輸入參數(shù)分別是內(nèi)存ZONE區(qū)的描述符的地址(zone)、內(nèi)存分配請(qǐng)求大小(2^order)和分配標(biāo)志gfp_flags。如果在gfp_flags中設(shè)置了__GFP_COLD標(biāo)志,則應(yīng)從冷緩存中獲取頁(yè)幀,否則應(yīng)從熱緩存中獲取頁(yè)幀(此標(biāo)志僅對(duì)單個(gè)頁(yè)幀請(qǐng)求有意義)。該函數(shù)基本上執(zhí)行以下操作:
如果order不等于0,則頁(yè)幀緩存不能使用,函數(shù)直接跳轉(zhuǎn)到第4步。
檢查由__GFP_COLD標(biāo)志標(biāo)識(shí)的內(nèi)存ZONE區(qū)域的CPU緩存是否必須被補(bǔ)充(per_cpu_pages的count ≤ low)。在本例中,它執(zhí)行以下子步驟:
重復(fù)調(diào)用__rmqueue()函數(shù),從伙伴系統(tǒng)中分配batch個(gè)頁(yè)幀。
將分配的頁(yè)幀描述符插入到緩存的列表中。
更新count變量(將新分配的頁(yè)幀數(shù)量加上)。
如果count > 0,從緩存列表中取一個(gè)頁(yè)幀,然后跳轉(zhuǎn)到第5步。(CPU頁(yè)幀緩存可能是空的,在第2步的__rmqueue()沒有申請(qǐng)到頁(yè)幀時(shí)就會(huì)發(fā)生)
到這兒,如果內(nèi)存請(qǐng)求沒有被滿足,調(diào)用__rmqueue()申請(qǐng)從伙伴系統(tǒng)中分配所請(qǐng)求頁(yè)幀。
如果內(nèi)存請(qǐng)求被滿足,初始化該頁(yè)幀(第1個(gè))的頁(yè)描述符:清除某些標(biāo)志、設(shè)置private為0,設(shè)置頁(yè)幀引用計(jì)數(shù)器為1。另外,如果設(shè)置了__GPF_ZERO,將申請(qǐng)的內(nèi)存清零。
返回頁(yè)幀(第1個(gè))的描述符,失敗返回NULL。
2 通過(guò)CPU頁(yè)幀緩存釋放頁(yè)幀
從CPU頁(yè)幀緩存中釋放頁(yè)幀,使用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ù)是待釋放頁(yè)幀的描述符地址page,表示熱緩存還是冷緩存的標(biāo)志cold。
執(zhí)行的步驟如下:
根據(jù)頁(yè)幀,獲取page->flags標(biāo)志。
根據(jù)cold標(biāo)志獲取對(duì)應(yīng)頁(yè)幀緩存的描述符per_cpu_pages地址。
檢查緩存是否不足:如果count ≥ high,調(diào)用free_pages_bulk()函數(shù)。該函數(shù)會(huì)重復(fù)調(diào)用__free_pages_bulk()函數(shù)釋放指定的頁(yè)幀到伙伴系統(tǒng)中。
將該頁(yè)幀添加到緩存列表中,增加count計(jì)數(shù)。
應(yīng)該注意的是,在Linux v2.6內(nèi)核中,沒有任何頁(yè)幀被釋放到冷緩存中:內(nèi)核總是假設(shè)釋放的頁(yè)幀相對(duì)于硬件緩存來(lái)說(shuō)是熱的。當(dāng)然,這并不意味著冷緩存是空的:當(dāng)達(dá)到低閾值時(shí),緩存由buffered_rmqueue()補(bǔ)充。
3 移除__GFP_COLD
雖然我們前邊分析了基于冷熱緩存的CPU頁(yè)幀緩存,但是,從v4.14版本以后的內(nèi)核中已經(jīng)移除,參考patch。Patches 1-4是與移除冷緩存最相關(guān)的部分;Patches 5-8是可選的,因?yàn)樗鼈兌际莿h除無(wú)用但也不影響性能的代碼。
free_hot_cold_page的大多數(shù)調(diào)用者用戶都聲稱被釋放的頁(yè)是熱緩存的。唯一的例外是頁(yè)回收代碼,因?yàn)樵诓痪玫膶?lái)可能會(huì)釋放足夠多的頁(yè),因此CPU的本地頁(yè)幀緩存列表將被回收,熱緩存信息將丟失。由于沒有人真正關(guān)心被釋放到分配器的頁(yè)的熱信息,所以省略該參數(shù)即可。
審核編輯:劉清
-
計(jì)數(shù)器
+關(guān)注
關(guān)注
32文章
2261瀏覽量
94994 -
Cache
+關(guān)注
關(guān)注
0文章
129瀏覽量
28435 -
LINUX內(nèi)核
+關(guān)注
關(guān)注
1文章
316瀏覽量
21744
原文標(biāo)題:Linux內(nèi)核8.5-內(nèi)存管理之CPU本地頁(yè)幀緩存
文章出處:【微信號(hào):嵌入式ARM和Linux,微信公眾號(hào):嵌入式ARM和Linux】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
Linux的內(nèi)存管理是什么,Linux的內(nèi)存管理詳解
![<b class='flag-5'>Linux</b>的<b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理</b>是什么,<b class='flag-5'>Linux</b>的<b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理</b>詳解](https://file.elecfans.com//web2/M00/43/01/pYYBAGJ7h8qAfJe-AAA3U8e_SDc509.jpg)
Linux內(nèi)存管理之頁(yè)面回收
![<b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理</b><b class='flag-5'>之</b>頁(yè)面回收](https://file.elecfans.com//web2/M00/44/8E/poYBAGKF3tWAK9WxAABA4cDnZ5U957.png)
關(guān)于Linux內(nèi)存管理的詳細(xì)介紹
Linux內(nèi)核內(nèi)存管理之ZONE內(nèi)存分配器
Linux內(nèi)核內(nèi)存管理之內(nèi)核非連續(xù)物理內(nèi)存分配
![<b class='flag-5'>Linux</b>內(nèi)核<b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理</b>之內(nèi)核非連續(xù)物理<b class='flag-5'>內(nèi)存</b>分配](https://file1.elecfans.com/web2/M00/C1/9D/wKgaomXX-KCADAsrAAAaZXMwKKg445.png)
HVM的緩存控制與內(nèi)存管理
Linux內(nèi)存管理導(dǎo)讀
Linux性能及調(diào)優(yōu)指南:內(nèi)存架構(gòu)
CPU緩存的作用及原理有哪些
如何在 Linux 上查看本地 DNS 緩存
![如何在 <b class='flag-5'>Linux</b> 上查看<b class='flag-5'>本地</b> DNS <b class='flag-5'>緩存</b>](https://file1.elecfans.com/web2/M00/8B/6B/wKgZomSY_YiAfM9KAAAfka7CouE603.png)
Linux 內(nèi)存管理總結(jié)
![<b class='flag-5'>Linux</b> <b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理</b>總結(jié)](https://file1.elecfans.com/web2/M00/AF/48/wKgZomVN1IOATlnhAACeh1R18WE061.jpg)
什么是CPU緩存?它有哪些作用?
緩存之美——如何選擇合適的本地緩存?
![<b class='flag-5'>緩存</b><b class='flag-5'>之</b>美——如何選擇合適的<b class='flag-5'>本地</b><b class='flag-5'>緩存</b>?](https://file1.elecfans.com//web2/M00/0C/47/wKgaomcyxP6Adt0qAABfE5AgeRg699.png)
評(píng)論