ULK3 based on kenerl 2.6.11 讀書(shū)筆記(3)

第17章 回收頁(yè)框

頁(yè)框回收算法

內(nèi)存及磁盤(pán)高速緩存抓取了那么多的頁(yè)框但從未釋放任何頁(yè)框。因此,遲早所有的空閑內(nèi)存將被分配給進(jìn)程和高速緩存。Linux內(nèi)核的頁(yè)框回收算法(page frame reclaiming algorithm,PFRA)采取從用戶進(jìn)程和內(nèi)核高速緩存“竊取”頁(yè)框的辦法補(bǔ)充伙伴系統(tǒng)的空閑塊列表。

頁(yè)框回收算法的目標(biāo)之一就是保存最少的空閑頁(yè)框池以便內(nèi)核可以安全地從“內(nèi)存緊缺”的情形中恢復(fù)過(guò)來(lái)。

PFRA按照頁(yè)框所含內(nèi)容,以不同的方式處理頁(yè)框:

映射頁(yè):所謂“映射頁(yè)”就是指該頁(yè)映射了一個(gè)文件的某個(gè)部分。映射頁(yè)差不多都是可同步的,為了回收頁(yè)框,內(nèi)核必須檢查頁(yè)是否為臟,而且必要時(shí)將頁(yè)的內(nèi)容寫(xiě)道相應(yīng)的磁盤(pán)文件。

匿名頁(yè):所謂“匿名頁(yè)”就是指它屬于一個(gè)進(jìn)程的某個(gè)線性區(qū)(例如,進(jìn)程的用戶態(tài)堆和堆棧中的所有頁(yè)稱(chēng)為匿名頁(yè))。為了回收頁(yè)框,內(nèi)核必須將頁(yè)中內(nèi)容保存到一個(gè)專(zhuān)門(mén)的磁盤(pán)分區(qū)或磁盤(pán)文件,叫做“交換區(qū)”。所以匿名頁(yè)都是可交換的。

PFRA設(shè)計(jì)

PFRA算法的候選頁(yè):磁盤(pán)高速緩存 頁(yè)高速緩存以及屬于進(jìn)程用戶態(tài)地址空間的頁(yè)。

PFRA算法的原則:

1.首先釋放無(wú)害頁(yè):在進(jìn)程用戶態(tài)地址空間的頁(yè)回收之前,必須縣回收沒(méi)有被任何進(jìn)程使用的磁盤(pán)和內(nèi)存高速緩存的頁(yè)

2.將用戶進(jìn)程的所有頁(yè)定為可回收頁(yè):除了鎖定頁(yè),PFRA必須能夠竊得任何用戶進(jìn)程頁(yè)。這樣,睡眠較長(zhǎng)時(shí)間的進(jìn)程將會(huì)失去所有頁(yè)框

3.同時(shí)取消引用一個(gè)共享頁(yè)框的所有頁(yè)表項(xiàng)的映射,就可以回收該共享頁(yè)框

4.只回收未用頁(yè):使用簡(jiǎn)化的LRU算法,PFRA算法將頁(yè)框分為在用(in_use)和未用(unused)

反向映射

PFRA的目標(biāo)之一是回收共享頁(yè)框。Linux 2.6內(nèi)核能夠快速定位指向同一頁(yè)框的所有頁(yè)表項(xiàng)。這個(gè)過(guò)程稱(chēng)為反向映射(reverse mapping)。

反向映射方法的簡(jiǎn)單解決之道,就是在頁(yè)描述符中引入附加字段,從而將某頁(yè)描述符所確定的頁(yè)框中對(duì)應(yīng)的所有頁(yè)表項(xiàng)聯(lián)接起來(lái)。但是,保持更新這樣的鏈表將會(huì)大大增加系統(tǒng)開(kāi)銷(xiāo)。Linux 2.6采用”面向?qū)ο蟮姆聪蛴成洹钡募夹g(shù)。實(shí)際上,對(duì)任何可回收的用戶態(tài)頁(yè),內(nèi)核保留系統(tǒng)中該頁(yè)所在所有線性區(qū)(“對(duì)象”)的反向鏈接,每個(gè)線性區(qū)描述符存放一個(gè)指針指向一個(gè)內(nèi)存區(qū)描述符,而該內(nèi)存描述符又包含一個(gè)指針 指向一個(gè)頁(yè)全局目錄(Page Global Directory)。因此,這些反向鏈接使得PFRA能夠檢索引用某頁(yè)的所有頁(yè)表項(xiàng)。

為確定頁(yè)框是共享還是非共享的,匿名還是非匿名,內(nèi)核要查看頁(yè)描述符的兩個(gè)字段:_mapcount和mapping。

_mapcount:引用頁(yè)框的頁(yè)表項(xiàng)數(shù)目。

? ? ? ? ? ? ? ? ? 為-1,表示沒(méi)有頁(yè)表項(xiàng)引用該頁(yè)框

? ? ? ? ? ? ? ? ? 為0,表示是非共享頁(yè)框,即只有一個(gè)頁(yè)表項(xiàng)引用該頁(yè)框大于0,表示頁(yè)框是共享的

? ? ? ? ? ? ? ? ? page_mapcount函數(shù)接受一個(gè)頁(yè)描述符地址,返回值為map_count + 1

mapping:用于確定頁(yè)框是映射頁(yè)還是匿名頁(yè)如果mapping

? ? ? ? ? ? ? ? ?字段為空,則該頁(yè)屬于交換頁(yè)如果mapping

? ? ? ? ? ? ? ? ?字段非空,且最低位為1,則為匿名頁(yè),同時(shí)mapping字段中存放的是指向anon_vma描述符的指針

? ? ? ? ? ? ? ? ? 如果mapping字段非空,且最低位為0,則為映射頁(yè),同時(shí)mapping字段中存放的是指向?qū)?yīng)文件的address_space對(duì)象。

PageAnon函數(shù)接受頁(yè)描述符地址作為參數(shù),如果mapping字段最低位置位(最低位為1),則返回1,否則返回0。

匿名頁(yè)的反向映射

線性區(qū):進(jìn)程使用的線性地址區(qū)間

將引用同一頁(yè)框的所有匿名頁(yè)鏈接起來(lái)的策略非常簡(jiǎn)單,即將該頁(yè)框所在的匿名線性區(qū)放在一個(gè)雙向循環(huán)鏈表中。注意:即使一個(gè)匿名線性區(qū)存有不同的頁(yè),頁(yè)始終只有一個(gè)反向映射鏈表用于該區(qū)域中的所有頁(yè)框。

當(dāng)為一個(gè)匿名區(qū)分配第一頁(yè)時(shí),內(nèi)核創(chuàng)建一個(gè)新的anon_vma數(shù)據(jù)結(jié)構(gòu),它只包含兩個(gè)字段:lock和head。lock字段時(shí)競(jìng)爭(zhēng)條件下保護(hù)鏈表的自旋鎖,head字段時(shí)指向線性區(qū)描述符雙向循環(huán)鏈表的頭部。然后,內(nèi)核將匿名線性區(qū)的vm_area_struct描述符插入到雙向循環(huán)鏈表中。vm_area_struct包含兩個(gè)字段:anon_vma_node和anon_vma。其中anon_vma_node字段存放指向鏈表中前一個(gè)和后一個(gè)元素的指針,而anon_vma字段指向anon_vma數(shù)據(jù)結(jié)構(gòu)。最后,把指向anon_vma數(shù)據(jù)結(jié)構(gòu)的指針指向頁(yè)框描述符的mapping字段。如下圖:

映射區(qū)頁(yè)的反向映射與匿名頁(yè)相反,映射頁(yè)經(jīng)常時(shí)共享的,這是因?yàn)椴煌倪M(jìn)程經(jīng)常會(huì)共享同一程序代碼。Linux 2.6依靠叫做“優(yōu)先搜索樹(shù)(priority search tree)”的結(jié)構(gòu)來(lái)快速定位引用同一頁(yè)框的所有線性區(qū)。如下圖:

PFRA實(shí)現(xiàn)

頁(yè)框回收算法必須處理多種屬于用戶態(tài)進(jìn)程 磁盤(pán)高速緩存和頁(yè)高速緩存的頁(yè)

頁(yè)框回收算法執(zhí)行的三個(gè)路口:

1.內(nèi)存緊缺回收:內(nèi)核發(fā)現(xiàn)內(nèi)存緊缺

2.睡眠回收:在進(jìn)入suspend-to-disk狀態(tài)時(shí),內(nèi)核必須釋放內(nèi)存

3.周期回收:必要時(shí),周期性激活內(nèi)核線程執(zhí)行內(nèi)存回收算法

最近最少使用(LRU)鏈表

屬于進(jìn)程用戶態(tài)的地址空間或者頁(yè)高速緩存中的所有頁(yè)被分成兩組:活動(dòng)鏈表和非活動(dòng)鏈表。它們統(tǒng)稱(chēng)為L(zhǎng)RU鏈表。前面一個(gè)鏈表用于存放最近被訪問(wèn)過(guò)的頁(yè);后面的則是存放由一段時(shí)間沒(méi)有被訪問(wèn)過(guò)的頁(yè)。顯然,頁(yè)必須從非活動(dòng)鏈表中取。

這兩個(gè)雙向鏈表的頭分別存放在每個(gè)zone描述符的active_list和inactive_list字段,而nr_active和nr_inactive字段表示存放在兩個(gè)鏈表中的頁(yè)數(shù)。最后,lru_lock字段是一個(gè)自旋鎖,保護(hù)兩個(gè)鏈表免受SMP系統(tǒng)上的并發(fā)訪問(wèn)。

如果屬于LRU鏈表,則設(shè)置頁(yè)描述符中的PG_lru標(biāo)志。此外,如果頁(yè)表屬于活動(dòng)鏈表,則設(shè)置PG_active標(biāo)志,如果屬于非活動(dòng)鏈表,則清PG_active標(biāo)志。頁(yè)描述符的lru字段存放指向lru鏈表中下一個(gè)元素和前一個(gè)元素。

在LRU鏈表之間移動(dòng)頁(yè)

在頁(yè)描述符中的PG_referenced標(biāo)志用來(lái)把一個(gè)頁(yè)從非活動(dòng)鏈表移動(dòng)到活動(dòng)鏈表所需的訪問(wèn)次數(shù)加倍。例如,假如在非活動(dòng)鏈表中一個(gè)頁(yè)其PG_referenced標(biāo)志置為0。第一次訪問(wèn)把這個(gè)標(biāo)志置為1,但是這一頁(yè)仍然留在非活動(dòng)鏈表中。第二次堆該頁(yè)訪問(wèn)時(shí)發(fā)現(xiàn)這一標(biāo)志被設(shè)置。因此,把頁(yè)移動(dòng)到活動(dòng)鏈表。但是,如果第一次訪問(wèn)之后在給定的時(shí)間間隔內(nèi)第二次訪問(wèn)沒(méi)有發(fā)生,那么頁(yè)框回收算法就可能重置PG_referencd標(biāo)志。

PFRA使用mark_page_accessed() ?page_referenced() refill_inactive_zone()函數(shù)在LRU鏈表之間移動(dòng)頁(yè)。

mark_page_accessed()函數(shù)

當(dāng)內(nèi)核必須把一個(gè)頁(yè)標(biāo)記為訪問(wèn)過(guò)時(shí),就調(diào)用mark_page_accessed()函數(shù)。

page_referenced()函數(shù)

PFRA掃描一頁(yè)調(diào)用一次page_referenced()函數(shù),如果PG_referenced標(biāo)志或頁(yè)表項(xiàng)中的某些Accessed標(biāo)志位置位,則函數(shù)返回1;否則返回0。該函數(shù)首先檢查頁(yè)描述符的PG_referencd標(biāo)志。如果標(biāo)志置為則清0.然后使用面向?qū)ο蟮姆聪蛴成浞椒?,?duì)引用該頁(yè)的所有用戶態(tài)頁(yè)表項(xiàng)中的Accessed標(biāo)志位進(jìn)行檢查并清0.

refill_inactive_zone()函數(shù)

refill_inactive_zone()函數(shù)由shrink_zone()函數(shù)調(diào)用,而shrink_zone()函數(shù)對(duì)頁(yè)高速緩存和用戶態(tài)地址空間地址進(jìn)行頁(yè)回收。此函數(shù)有兩個(gè)參數(shù):zone 和 sc。

zone:指針zone指向一個(gè)內(nèi)存管理區(qū)的描述符

sc:指針sc指向一個(gè)scan_control結(jié)構(gòu)

scan_control結(jié)構(gòu)字段如下圖:


? ? ?PFRA傾向于壓縮頁(yè)高速緩存,而將用戶態(tài)進(jìn)程的頁(yè)留在RAM中。該函數(shù)按下列公式計(jì)算交換傾向值:

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 交換傾向值=映射比率/2 + 負(fù)荷值 + 交換值

映射比率:用戶態(tài)地址空間引用的頁(yè)數(shù)占所有可分配頁(yè)框的百分比

負(fù)荷值:依據(jù)是前一次PFRA運(yùn)行時(shí)管理區(qū)的掃描優(yōu)先級(jí),這個(gè)優(yōu)先級(jí)存放在管理區(qū)描述符的prev_priority字段。

負(fù)荷值與管理區(qū)前一次優(yōu)先級(jí)的對(duì)應(yīng)關(guān)系: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 管理區(qū)前一次優(yōu)先級(jí) ? ? ? ? ?12...7 ? ? ? ? 6 ? ? ? ? 5 ? ? ? ?4 ? ? ? ?3 ? ? ? ? 2 ? ? ? ? 1 ? ? ? 0 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?負(fù)荷值 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?0 ? ? ? ? ? ?1 ? ? ? ? 3 ? ? ? ?6 ? ? ? ?12 ? ? ? ?25 ? ? ? 50 ? ? 100

交換值:是一個(gè)自定義的常數(shù),通常為60。系統(tǒng)管理員可以在/proc/sys/vm/swappiness文件內(nèi)修改這個(gè)值,或用相應(yīng)的sysctl()函數(shù)調(diào)用調(diào)整這個(gè)值。

2015.10.25:23:03

內(nèi)存緊缺回收

當(dāng)內(nèi)存分配失敗時(shí)激活內(nèi)存精確回收。 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 在分配VFS緩沖區(qū)或者緩沖區(qū)首部時(shí),內(nèi)核調(diào)用free_more_memory() ?函數(shù) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 而當(dāng)從伙伴系統(tǒng)分配一個(gè)或多個(gè)頁(yè)框時(shí),調(diào)用try_to_free_pages()函數(shù)?

free_more_memory()函數(shù)

free_more_memory()函數(shù)執(zhí)行如下操作:

1.調(diào)用wakeup_bdflush()喚醒一個(gè)pdflush內(nèi)核線程,并觸發(fā)頁(yè)高速緩存中1024個(gè)臟頁(yè)的寫(xiě)操作。寫(xiě)臟頁(yè)到磁盤(pán)的操作將最終使包含緩沖區(qū) 緩沖區(qū)首部和其他VFS數(shù)據(jù)結(jié)構(gòu)的頁(yè)框稱(chēng)為可釋放的。

2.調(diào)用sched_yield()系統(tǒng)調(diào)用的服務(wù)例程,為pdflush內(nèi)核線程提供指向機(jī)會(huì)。

3.對(duì)系統(tǒng)的所有內(nèi)存節(jié)點(diǎn),啟動(dòng)一個(gè)循環(huán)。對(duì)每一個(gè)節(jié)點(diǎn),調(diào)用try_to_free_pages()函數(shù),傳給它的參數(shù)時(shí)一個(gè)“緊缺”內(nèi)存管理區(qū)鏈表(在80x86結(jié)構(gòu)中是ZONE_DMA和ZONE_NORMAL)

try_to_free_pages()函數(shù)

try_to_free_pages()函數(shù)接受如下三個(gè)參數(shù):

zones:要回收的頁(yè)所在的內(nèi)存管理區(qū)鏈表

gfp_mask:用于失敗的內(nèi)存分配的一組分配標(biāo)志

order:沒(méi)有使用

該函數(shù)的目標(biāo)就是通過(guò)重復(fù)調(diào)用shrink_caches()和shrink_slab()函數(shù)時(shí)方至少32個(gè)頁(yè)框,每次調(diào)用后優(yōu)先級(jí)會(huì)比前一次提高。有關(guān)的輔助函數(shù)可以獲得scan_control類(lèi)型描述符中的優(yōu)先級(jí),以及正在進(jìn)行的掃描操作的其他參數(shù)。最低的 也是初始的優(yōu)先級(jí)是12,而最高的 也是最終的優(yōu)先級(jí)是0。如果try_to_free_pages()每能在某次(共13次)調(diào)用shrink_caches()和shrink_slab()函數(shù)時(shí)成功回收32個(gè)頁(yè)框,PFRA就要黔驢技窮了。最后一招:刪除一個(gè)進(jìn)程,釋放它的所有頁(yè)框。這一操作由out_of_memory()函數(shù)執(zhí)行。

shrink_caches()函數(shù)

shrink_caches()函數(shù)由try_to_free_pages()函數(shù)調(diào)用,它有兩個(gè)參數(shù):內(nèi)存管理區(qū)鏈表zones和scan_control描述符地址sc。

該函數(shù)的目的值時(shí)對(duì)zones鏈表中的每個(gè)管理區(qū)調(diào)用shrink_zone函數(shù)。但對(duì)給定管理區(qū)調(diào)用shrink_zone()之前,shrink_caches()函數(shù)用sc->priority字段的值更新管理區(qū)描述符的temp_priority字段,這就是掃描操作的當(dāng)前優(yōu)先級(jí)。而且如果PFAR的上一次調(diào)用優(yōu)先級(jí)高于當(dāng)前優(yōu)先級(jí),即這個(gè)管理區(qū)進(jìn)行頁(yè)框回收變得很難了,那么shrink_caches()函數(shù)把當(dāng)前優(yōu)先級(jí)拷貝到管理區(qū)描述符的pre_priority。最后,如果管理區(qū)描述符中的all_unreclaimable標(biāo)志置位,且當(dāng)前優(yōu)先級(jí)小于12,則shrink_caches()不掉用shrink_zone(),也就是說(shuō),在try_to_free_pages()的第一次迭代中不調(diào)用shrink_caches()。當(dāng)PFRA確定一個(gè)管理區(qū)都是不可回收頁(yè),掃描該管理區(qū)的頁(yè)純粹時(shí)浪費(fèi)時(shí)間,則將all_unreclaimable標(biāo)志置位。

shrink_zone()函數(shù)

shrink_zone()函數(shù)有兩個(gè)參數(shù):zone和sc。zone是指向struct_zone描述符的指針;sc是指向scan_control描述符的指針。該函數(shù)的目標(biāo)是從管理區(qū)非活動(dòng)鏈表回收32頁(yè)。它每次在更大的一段管理區(qū)非活動(dòng)鏈表上重復(fù)調(diào)用輔助函數(shù)shrink_cache(),以期達(dá)到目標(biāo)。而且shrink_zone()重復(fù)調(diào)用refill_inactive_zone()函數(shù)來(lái)補(bǔ)充管理區(qū)非活動(dòng)鏈表。 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

管理區(qū)描述符的nr_scan_active和nr_scan_inactive字段在這里起到很重要的作用。為了提高效率,函數(shù)每批處理32頁(yè)。因此如果函數(shù)在低優(yōu)先級(jí)運(yùn)行(對(duì)應(yīng)sc->priority的高值),且某個(gè)lru鏈表中沒(méi)有足夠的頁(yè),函數(shù)就跳過(guò)對(duì)這個(gè)鏈表的掃描。但因此跳過(guò)的活動(dòng)或不活動(dòng)頁(yè)數(shù)就分別存放在nr_scan_active和nr_scan_inactive字段中,這樣函數(shù)下次執(zhí)行時(shí)再處理這些跳過(guò)的頁(yè)。 ? ? ? ? ? ? ? ? ? ? ??

shrink_cache()函數(shù)

shrink_cache()函數(shù)又是一個(gè)輔助函數(shù),它的主要目的就是從管理區(qū)非活動(dòng)鏈表取出一組頁(yè)把它們放入一個(gè)臨時(shí)鏈表,然后調(diào)用shrink_list()函數(shù)對(duì)這個(gè)鏈表中的每一個(gè)頁(yè)進(jìn)行有效的頁(yè)框回收操作。shrink_cache()函數(shù)的參數(shù)與shrink_zones()一樣,都是zone和s c。

shrink_list()函數(shù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 第八章 內(nèi)存管理 本章通過(guò)三部分內(nèi)容描述內(nèi)核給自己動(dòng)態(tài)分配內(nèi)存: ...
    rlkbk閱讀 524評(píng)論 0 1
  • 第二章 內(nèi)存尋址 內(nèi)存地址 內(nèi)存地址分為三種:邏輯地址(logical address)(段+偏移量) ...
    rlkbk閱讀 486評(píng)論 0 1
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,058評(píng)論 25 709
  • 把期待留給你認(rèn)真做的事上,再喜歡再親近的人,也不要有期待。我做的事情,都是我愿意做并且做了開(kāi)心的事情。
    Arwen_閱讀 228評(píng)論 0 0
  • 靜,如空氣流動(dòng),只是空穴里的大風(fēng) 我安靜地愛(ài)你愛(ài)你的無(wú)聲無(wú)色無(wú)味愛(ài)你,把咆哮的海水置入,一聲不吭的懷里 我安靜地愛(ài)...
    郭安安閱讀 308評(píng)論 11 26

友情鏈接更多精彩內(nèi)容