1. 頁表
1.1 統(tǒng)一的頁表框架
頁表用來把虛擬頁映射到物理頁,并且存放頁的保護(hù)位(即訪問權(quán)限)。
在Linux4.11版本以前抛姑,Linux內(nèi)核把頁表分為4級(jí):
頁全局目錄表(PGD)、頁上層目錄(PUD)、頁中間目錄(PMD)收奔、直接頁表(PT)。
4.11版本把頁表擴(kuò)展到5級(jí)滓玖,在頁全局目錄和頁上層目錄之間增加了頁四級(jí)目錄(P4D)坪哄。
各處處理器架構(gòu)可以選擇使用5級(jí),4級(jí)势篡,3級(jí)或者2級(jí)頁表翩肌,同一種處理器在頁長(zhǎng)度不同的情況可能選擇不同的頁表級(jí)數(shù)〗疲可以使用配置宏CONFIG_PGTABLE_LEVELS配置頁表的級(jí)數(shù)摧阅,一般使用默認(rèn)值。
如果選擇4級(jí)頁表绷蹲,那么使用PGD棒卷,PUD,PMD祝钢,PT比规;如果使用3級(jí)頁表,那么使用PGD拦英,PMD蜒什,PT;如果選擇2級(jí)頁表疤估,那么使用PGD和PT灾常。如果不使用頁中間目錄,那么內(nèi)核模擬頁中間目錄铃拇,調(diào)用函數(shù)pmd_offset根據(jù)頁上層目錄表項(xiàng)和虛擬地址獲取頁中間目錄表項(xiàng)時(shí)钞瀑,直接把頁上層目錄表項(xiàng)指針強(qiáng)制轉(zhuǎn)換成頁中間目錄表項(xiàng)。
static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr)
{
return (pmd_t *)pud_page_vaddr(*pud) + pmd_index(addr);
}
每個(gè)進(jìn)程有獨(dú)立的頁表慷荔,進(jìn)程的mm_struct實(shí)例的成員pgd指向頁全局目錄,前面四級(jí)頁表的表項(xiàng)存放下一級(jí)頁表的起始地址雕什,直接頁表的頁表項(xiàng)存放頁幀號(hào)(PFN)。
內(nèi)核也有一個(gè)頁表,0號(hào)內(nèi)核線程的進(jìn)程描述符init_task的成員active_mm指向內(nèi)存描述符init_mm贷岸,內(nèi)存描述符init_mm的成員pgd指向內(nèi)核的頁全局目錄swapper_pg_dir壹士。
1.2 ARM64處理器的頁表
ARM64處理器把頁表稱為轉(zhuǎn)換表,最多4級(jí)偿警。ARM64處理器支持三種頁長(zhǎng)度:4KB躏救,16KB,64KB螟蒸。頁長(zhǎng)度和虛擬地址的寬度決定了轉(zhuǎn)換表的級(jí)數(shù)盒使,在虛擬地址的寬度為48位的條件下,頁長(zhǎng)度和轉(zhuǎn)換表級(jí)數(shù)的關(guān)系如下所示:
頁長(zhǎng)度 | 轉(zhuǎn)換表級(jí)數(shù) |
---|---|
4KB | 4級(jí)頁表 |
16KB | 4級(jí)頁表 |
64KB | 3級(jí)頁表 |
ARM64處理器把表項(xiàng)稱為描述符尿庐,使用64位的長(zhǎng)描述符格式忠怖。描述符的0bit指示描述符是不是有效的:0表示無效,1表示有效抄瑟。第1位指定描述符類型凡泣。
在塊描述符和頁描述符中,內(nèi)存屬性被拆分為一個(gè)高屬性和一個(gè)低屬性塊皮假。
(1)[59,63):基于頁的硬件屬性鞋拟。
(2)[55,59):保留給軟件使用。
(3)54:在異常級(jí)別0惹资,表示UXN贺纲,即不運(yùn)行異常級(jí)別0執(zhí)行內(nèi)核代碼;在其他異常級(jí)別褪测,表示XN猴誊,不允許執(zhí)行。
(4)53:PXN不運(yùn)行在特權(quán)級(jí)別(el1,el2,el3)執(zhí)行侮措。
(5)52:連續(xù)懈叹,指示這條轉(zhuǎn)換表項(xiàng)屬于一個(gè)連續(xù)表項(xiàng)集合,一個(gè)連續(xù)表項(xiàng)集合可以被換存在一條TLB表項(xiàng)里面分扎。
(6)51:臟位修飾符(DBM)澄成,指示頁或內(nèi)存塊是否被修改過。
(7)11:非全局(nG)畏吓。表示不是全局墨状,是進(jìn)程私有的,有一個(gè)關(guān)聯(lián)的ASID菲饼;nG位是0肾砂,表示轉(zhuǎn)換是全局的,是所有進(jìn)程共享的巴粪,內(nèi)核的頁或內(nèi)存塊是所有進(jìn)程共享的通今。
(8)10:訪問標(biāo)志粥谬,指示頁或內(nèi)存塊自從相應(yīng)的轉(zhuǎn)換表描述符中的訪問標(biāo)志被設(shè)置為0后是否被訪問過肛根。
(9)[8,10):可共享性辫塌,00表示不共享,01保留值派哲,10表示外部共享臼氨,11表示內(nèi)部共享。
(10)[6,8): AP[2:1](數(shù)據(jù)訪問權(quán)限)芭届。
(11)5:非安全储矩。對(duì)于安全狀態(tài)的內(nèi)存訪問,指定輸出地址在安全地址映射還是非安全地址映射褂乍。
(12)[2,5):內(nèi)存屬性索引持隧,指定寄存器MAIR_ELx中內(nèi)存屬性字段的索引,內(nèi)存屬性間接寄存器有8個(gè)8位內(nèi)存屬性字段:Attr<n>逃片,n等于0~7屡拨。
2. TLB
處理器的MMU負(fù)責(zé)把虛擬地址轉(zhuǎn)換成物理地址,為了改進(jìn)虛擬地址到物理地址的轉(zhuǎn)換速度褥实,避免每次轉(zhuǎn)換都需要查詢內(nèi)存中的頁表呀狼,處理器廠商在管理單元里加了稱為TLB的高速緩存,TLB直譯為轉(zhuǎn)換后備緩沖區(qū)损离,意譯為頁表緩存哥艇。
頁表緩存用來緩存最近使用過的頁表項(xiàng),有些處理器使用兩級(jí)頁表緩存:第一級(jí)TLB分為指令TLB和數(shù)據(jù)TLB僻澎,好處是取指令和取數(shù)據(jù)可以并行貌踏;第二級(jí)TLB是統(tǒng)一TLB,即指令和數(shù)據(jù)共用的TLB窟勃。
2.1TLB表項(xiàng)格式
不同處理器架構(gòu)的TLB表項(xiàng)的格式不同祖乳。ARM64處理器的每條TLB表項(xiàng)不僅包含虛擬地址和物理地址,也包含屬性:內(nèi)存類型拳恋、緩存策略凡资、訪問權(quán)限、地址空間標(biāo)識(shí)符(ASID)和虛擬機(jī)標(biāo)識(shí)符(VMID)谬运。地址空間標(biāo)識(shí)符區(qū)分不同進(jìn)程的頁表項(xiàng)隙赁,虛擬機(jī)標(biāo)識(shí)符區(qū)分不同虛擬機(jī)的頁表項(xiàng)。
2.2 TLB管理
如果內(nèi)核修改了可能緩存在TLB里面的頁表項(xiàng)梆暖,那么內(nèi)核必須負(fù)責(zé)使舊的TLB表項(xiàng)失效伞访,內(nèi)核定義了每種處理器架構(gòu)必須實(shí)現(xiàn)的函數(shù)。
函數(shù) | 說明 |
---|---|
void flush_tlb_all(void); | 使所有TLB表項(xiàng)失效 |
void flush_tlb_mm(struct mm_struct *mm); | 使指定用戶地址空間的所有TLB表項(xiàng)失效轰驳,參數(shù)mm是進(jìn)程的內(nèi)存描述符 |
void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,unsigned long end); | 使指定用戶地址空間的指定虛擬頁的TLB表項(xiàng)失效,參數(shù)vma是虛擬內(nèi)存區(qū)域厚掷,uaddr是一個(gè)虛擬頁中的任意虛擬地址 |
void flush_tlb_kernel_range(unsigned long start, unsigned long end); | 使內(nèi)核的某個(gè)虛擬地址范圍的TLB表項(xiàng)失效弟灼,參數(shù)start是起始地址,end是結(jié)束地址 |
當(dāng)TLB沒有命中的時(shí)候冒黑,ARM64處理器的MMU自動(dòng)遍歷內(nèi)存中的頁表田绑,把頁表項(xiàng)復(fù)制到TLB,不需要軟件把頁表項(xiàng)寫到TLB抡爹,所以ARM64架構(gòu)沒有提供寫TLB的指令掩驱。
2.3 地址空間標(biāo)識(shí)符
為了減少在進(jìn)程切換時(shí)清空頁表緩存的需要,ARM64處理器的頁表緩存使用非全局位區(qū)分內(nèi)核和進(jìn)程的頁表項(xiàng)(nG位為0表示內(nèi)核的頁表項(xiàng))冬竟,使用地址空間標(biāo)識(shí)符(ASID)區(qū)分不同進(jìn)程的頁表項(xiàng)欧穴。
ARM64處理器的ASID長(zhǎng)度是由具體實(shí)現(xiàn)定義的,可以選擇8位或者16位泵殴。寄存器TTBR0_EL1或者TTBR1_EL1都可以用來存放當(dāng)前進(jìn)程的ASID涮帘,通常使用寄存器TCR_EL1的A1位決定使用哪個(gè)寄存器存放當(dāng)前進(jìn)程的ASID,通常使用寄存器TTBR0_EL1笑诅。寄存器TTBR0_EL1的位[63:48]或者[63:56]存放當(dāng)前進(jìn)程的ASID调缨,位[47:1]存放當(dāng)前進(jìn)程的頁全局目錄的物理地址。
在SMP系統(tǒng)中苟鸯,ARM64架構(gòu)要求ASID在處理器的所有核是唯一的同蜻。假設(shè)ASID為8位,ASID只有256個(gè)值早处,其中0是保留值湾蔓,可分配的ASID范圍1~255,進(jìn)程的數(shù)量可能超過255砌梆,兩個(gè)進(jìn)程的ASID可能相同默责,內(nèi)核引入ASID版本號(hào)解決這個(gè)問題。
(1)每個(gè)進(jìn)程有一個(gè)64位的軟件ASID咸包,低8位存放硬件ASID桃序,高56位存放ASID版本號(hào)。
(2)64位全局變量asid_generation的高56位保存全局ASID版本號(hào)烂瘫。
(3)當(dāng)進(jìn)程被調(diào)度時(shí)媒熊,比較進(jìn)程的ASID版本號(hào)和全局版本號(hào)。如果版本號(hào)相同坟比,那么直接使用上次分配的ASID芦鳍,否則需要給進(jìn)程重新分配硬件ASID。
存在空閑ASID葛账,那么選擇一個(gè)分配給進(jìn)程柠衅。不存在空閑ASID時(shí),把全局ASID版本號(hào)加1籍琳,重新從1開始分配硬件ASID菲宴,即硬件ASID從255回繞到1贷祈。因?yàn)閯偡峙涞挠布嗀SID可能和某個(gè)進(jìn)程的ASID相同,只是ASID版本號(hào)不同喝峦,頁表緩存可能包含了這個(gè)進(jìn)程的頁表項(xiàng)势誊,所以必須把所有處理器的頁表緩存清空。
引入ASID版本號(hào)的好處是:避免每次進(jìn)程切換都需要清空頁表緩存愈犹,只需要在硬件ASID回環(huán)時(shí)把處理器的頁表緩存清空键科。
2.4 虛擬機(jī)標(biāo)識(shí)符
虛擬機(jī)里面運(yùn)行的客戶操作系統(tǒng)的虛擬地址轉(zhuǎn)物理地址分兩個(gè)階段:
(1)把虛擬地址轉(zhuǎn)換成中間物理地址闻丑,由客戶操作系統(tǒng)的內(nèi)核控制漩怎,和非虛擬化的轉(zhuǎn)換過程相同。
(2)把中間物理地址轉(zhuǎn)換成物理地址嗦嗡,由虛擬機(jī)監(jiān)控器控制勋锤,虛擬機(jī)監(jiān)控器為每個(gè)虛擬機(jī)維護(hù)一個(gè)轉(zhuǎn)換表,分配一個(gè)虛擬機(jī)標(biāo)識(shí)符侥祭,寄存器VTTBR_EL2存放當(dāng)前虛擬機(jī)的階段2轉(zhuǎn)換表的物理地址叁执。
每個(gè)虛擬機(jī)有獨(dú)立的ASID空間,頁表緩存使用虛擬機(jī)標(biāo)識(shí)符區(qū)分不同虛擬機(jī)的轉(zhuǎn)換表項(xiàng)矮冬,避免每次虛擬機(jī)切換都要清空頁表緩存谈宛,在虛擬機(jī)標(biāo)識(shí)符回繞時(shí)把處理器的頁表緩存清空。