一文玩轉(zhuǎn)linux內(nèi)核--頁表緩存于巨型頁(學(xué)習(xí)筆記~)
一、頁表緩存(TLB)
處理器廠商在內(nèi)存管理單元(MMU)里增加一個TLB(Translation Lookaside Buffer)的高速緩存,TLB直譯為轉(zhuǎn)譯后備緩沖器,也被翻譯為頁表緩存。
TLB為CPU的一種緩存,由存儲器管理單元用于改進(jìn)虛擬地址到物理地址的轉(zhuǎn)譯速度。
TLB 用于緩存一部分標(biāo)簽頁表條目。TLB可介于 CPU 和CPU緩存之間,或在 CPU緩存和主存之間,這取決于緩存使用的是物理尋址或是虛擬尋址。
1.地址空間標(biāo)識符區(qū)分不同進(jìn)程的頁表項,虛擬機(jī)標(biāo)識符區(qū)分不同虛擬機(jī)的頁表項
地址空間標(biāo)識符:為了減少在進(jìn)程切換時清空頁表緩存的需要,ARM64處理器的頁表緩存使用非全局(not global, nG)位區(qū)分內(nèi)核和進(jìn)程的頁表項,使用地址空間標(biāo)識符(Address Space Identifer,ASID)區(qū)分不同進(jìn)程的頁表項。
ARM64處理器ASID長度是由具體實現(xiàn)定義的,可以選擇8位或者16位,寄存器ID_AA64MMFRO_EL1(AArch64內(nèi)存模型特性寄存器0,AArch64 Memory Model Feature Register 0)的字段ASIDBits存放處理器支持的ASID長度。
虛擬機(jī)標(biāo)識符:虛擬機(jī)里面運行的客戶操作系統(tǒng)的虛擬地址換成物理地址分兩個階段:第1階段把虛擬地址轉(zhuǎn)換成中間物理地址,第2階段把中間物理地址轉(zhuǎn)換成物理地址。第1階段轉(zhuǎn)換由客戶操作系統(tǒng)的內(nèi)存控制,和非虛擬化的轉(zhuǎn)換過程相同。第2階段轉(zhuǎn)換由虛擬機(jī)監(jiān)控器控制,虛擬機(jī)監(jiān)控器為每個虛擬機(jī)維護(hù)一個轉(zhuǎn)換表,分配一個虛擬機(jī)標(biāo)識符(Virtual Machine Identifier,VMID),寄存器VTTBR_EL2(虛擬化轉(zhuǎn)換表基準(zhǔn)寄存器,Virtualization Translation Table Base Register)存放當(dāng)前虛擬機(jī)的階段2轉(zhuǎn)換表的物理地址。
2.頁表改變后沖刷TLB的函數(shù):


使指定用戶地址空間的所有TLB表項失效,參數(shù)mm是進(jìn)程的內(nèi)存描述符。

使指定用戶地址空間的某個范圍TLB表項失效,參數(shù)vma虛擬內(nèi)存區(qū)域,start是起始地址,end是結(jié)束地址。

使指定用戶地址空間里面的指定虛擬頁的TLB表項失效,參數(shù)vma是虛擬內(nèi)存區(qū)域,uaddr是一個虛擬頁中的任意虛擬地址。

使內(nèi)核的某個虛擬地址范圍的TLB表項失效,參數(shù)start是起始地址,end是結(jié)束地址。
【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實戰(zhàn)項目及代碼)? ? ? ??


3.當(dāng)TLB沒有命中時候ARM64處理器的內(nèi)存管理單元自動遍歷內(nèi)存中的頁表,把頁表復(fù)制到TLB,要軟件把頁表寫道TLB,所以ARM64架構(gòu)沒有提供寫TLB的指令。
ARM64架構(gòu)提供一條TLB失效指令
字段常見選項:(ALL:所有表項。 VMALL:當(dāng)前虛擬機(jī)的階段1的所有表項,即表項的VMID是當(dāng)前虛擬機(jī)的VMID,虛擬機(jī)里面運行客戶操作系統(tǒng)的虛擬地址轉(zhuǎn)換成物理地址分為兩個階段,第一階段把虛擬地址轉(zhuǎn)換成物理地址,第二階段把中間物理地址轉(zhuǎn)換成物理地址。ASID:匹配寄存器Xt指定的ASID的表項。VA:匹配寄存器Xt指定的虛擬地址和ASID的表項。VAA:匹配寄存器Xt指定的虛擬地址并且ASID可以是任意值的表項。)
字段指定異常級別:(E1:異常級別1。 E2:異常級別2。 E3:異常級別3。)
字段表示內(nèi)部共享,即多個核共享:如果不使用字段IS,表示非共享,只被一個核使用。
字段Xt是X0-X31中的任何一個寄存器。


此函數(shù)核flush_tlb_all區(qū)別在于:
指令dsb中字段ish換成nsh,nsh使非共享(non-shareable),表示數(shù)據(jù)同步屏障指令僅僅在當(dāng)前核起作用;
指令tlbi沒有攜帶字段is,表示僅僅使當(dāng)前核的TLB表項失效。
4.平時為了方便描述,假設(shè)ASID長度是8位,ASID只有256個值,其中0是保留值,可分配ASID范圍是1-255,進(jìn)程數(shù)量可能超過255個,兩個進(jìn)程ASID可能相同,如何解決這個問題,內(nèi)核引入ASID版本號:
每個進(jìn)程有一個64位的軟件ASID,低8位存放硬件ASID,高56位存放ASID版本號;
64位全局變量的高56位保存全局ASID版本號;
當(dāng)進(jìn)程被調(diào)用的時候,比較進(jìn)程的ASID版本號核全局ASID版本號,如果版本號相同,直接使用上次分配的硬件ASID。否則需要給進(jìn)程重新分配硬件ASID。
引入ASID版本號的好處:避免每次進(jìn)程切換都需要清空頁表緩存,只需在硬件ASID回繞時把處理器的頁表緩存清空。
內(nèi)存描述符成員context存放結(jié)構(gòu)特定的內(nèi)存管理上下文,數(shù)據(jù)類型時結(jié)構(gòu)體nn_context_t。ARM64架構(gòu)定義結(jié)構(gòu)體類型如下:

當(dāng)全局ASIDe版本號+1時,每個處理器需要清空頁表緩存,位圖tlb_flush_pending保存需要清空頁表緩存的處理器集合。

5.虛擬機(jī)標(biāo)識符
每個虛擬機(jī)有獨立的ASID空間,頁表緩存使用虛擬機(jī)標(biāo)識符區(qū)分不同虛擬機(jī)的轉(zhuǎn)換表項,可以避免每次虛擬機(jī)切換都需要清空頁表緩存,只需要在虛擬機(jī)標(biāo)識符回繞時把處理器的頁表緩存清空。
二、巨型頁
當(dāng)運行內(nèi)存需求量較大的應(yīng)用程序時,如果使用長度為4KB的頁,將會產(chǎn)生較多的TLB未命中和缺頁異常,嚴(yán)重影響應(yīng)用程序的性能。如果使用長度為2MB甚至更大的巨型頁,可以大幅減少TLB未命中和缺頁異常的數(shù)量,大幅提高應(yīng)用程序的性能。這才是內(nèi)核引入巨型頁(Huge Page)的真正原因。
巨型頁首先需要處理器能夠支持,然后需要內(nèi)核支持,內(nèi)核有兩種實現(xiàn)方式:
使用hugetlbfs偽文件系統(tǒng)實現(xiàn)巨型頁;
透明巨型頁。
hugetlbfs文件系統(tǒng)時一個假的文件系統(tǒng),只是利用了文件系統(tǒng)的編程接口。使用hugetlbfs文件系統(tǒng)實現(xiàn)的巨型頁稱為傳統(tǒng)巨型頁。
透明巨型頁,標(biāo)準(zhǔn)巨型頁的優(yōu)點是預(yù)先分配巨型頁到巨型頁池,進(jìn)程申請巨型頁的時候從巨型池取出,成功的概率很高,缺點時應(yīng)用程序需要使用文件系統(tǒng)的編程接口。透明巨型頁的優(yōu)點是對應(yīng)用程序透明,缺點是動態(tài)分配,在內(nèi)存碎片化的時候分配成功的概率很低。
1.處理器對巨型頁的支持
ARM64處理器支持巨型頁的方式有兩種:
通過塊描述符支持巨型頁;
通過頁/塊描述符的連續(xù)位支持巨型頁。
通過塊描述符支持巨型頁:
假如:頁長度4kb,那么使用4級轉(zhuǎn)換表,0級轉(zhuǎn)換表不能使用塊描述符,1級轉(zhuǎn)換表的塊描述符指向1GB巨型頁,2級轉(zhuǎn)換表的塊描述符指向2MB巨型頁。

通過頁/塊描述符的連續(xù)位支持巨型頁:
頁/塊描述符中的連續(xù)位指示表項是一個連續(xù)表項集合中的一條表項,一個連續(xù)表項集合可以被緩存在一條TLB表項里面。通常所說,進(jìn)程申請了n頁的虛擬內(nèi)存區(qū)域,然后申請了n頁的物理內(nèi)存區(qū)域,使用n個連續(xù)的頁表把每個虛擬頁映射到物理頁,每個頁表項設(shè)置了連續(xù)標(biāo)志位,當(dāng)處理器的內(nèi)存管理單元遍歷內(nèi)存的頁表時,訪問到n個頁表中的任何一個頁表項。發(fā)現(xiàn)頁表項設(shè)置了連續(xù)標(biāo)志位,就會把n個頁表項合并以后填充到TLB表項。
假設(shè):頁長度時4KB,那么使用4級轉(zhuǎn)換表,1級轉(zhuǎn)換表的塊描述符不能使用連續(xù)位;2級轉(zhuǎn)換表的塊描述符支撐16個連續(xù)塊,即支持(162MB=32MB)巨型頁,3級轉(zhuǎn)換表的頁描述符支撐16個連續(xù)頁,即支持(164KB=64KB)巨型頁。
假設(shè):頁長度時16KB,那么使用4級轉(zhuǎn)換表,2級轉(zhuǎn)換表的塊描述符支持32個連續(xù)塊,即支持(3232MB=1GB)巨型頁,3級轉(zhuǎn)換表的頁描述符支撐128個連續(xù)頁,即支持(12816KB=2MB)巨型頁。
假設(shè):頁長度時64KB,那么使用3級轉(zhuǎn)換表,2級轉(zhuǎn)換表的塊描述符不能使用連續(xù)位,3級轉(zhuǎn)換表的頁描述符支撐32個連續(xù)頁,即支持(32*64KB=2MB)巨型頁。

2.巨型頁池
內(nèi)核使用巨型頁池管理巨型頁。有的處理器架構(gòu)支持多種巨型頁長度,每種巨型頁長度對應(yīng)一個巨型頁池,有一個默認(rèn)的巨型頁長度,默認(rèn)只創(chuàng)建巨型頁長度是默認(rèn)長度的巨型頁池。比如ARM64架構(gòu)在頁長度位4kb的時候支持巨型頁長度是1GB 32MB 2MB 64KB,默認(rèn)的巨型頁查高度是2MB,默認(rèn)只有創(chuàng)建巨型頁長度是2MB的巨型頁池。

巨型頁池中的巨型頁可以分為兩種:
永久巨型頁:是保留的,不能有其他用途,被預(yù)先分配到巨型頁池,當(dāng)進(jìn)程釋放永久巨型頁的時候,永久巨型頁被歸還到巨型頁池。
臨時巨型頁:也稱為多余的巨型頁,當(dāng)永久巨型頁用完之后,可以從頁分配器分配臨時巨型頁;進(jìn)程釋放臨時巨型頁的時候,直接釋放到頁分配器。當(dāng)設(shè)備長時間運行之后,內(nèi)存可能碎片化,分配臨時巨型頁可能會失敗。
hugetlb相當(dāng)于hugepages頁面管理者,頁面的分配及釋放,都是由此模塊負(fù)責(zé)。
Hugetlbfs則用于向用戶提供一套基于文件系統(tǒng)的巨型頁使用界面,下層功能的實現(xiàn),主要依賴hugetlb。
