Linux從頭學(xué)15:【頁(yè)目錄和頁(yè)表】-理論 實(shí)例 圖文的最完全、最接地氣詳解
掃描二維碼
隨時(shí)隨地手機(jī)看文章
作 者:道哥,10 年嵌入式開(kāi)發(fā)老兵,專(zhuān)注于:C/C 、嵌入式、Linux。關(guān)注下方公眾號(hào),回復(fù)【書(shū)籍】,獲取 Linux、嵌入式領(lǐng)域經(jīng)典書(shū)籍;回復(fù)【PDF】,獲取所有原創(chuàng)文章( PDF 格式)。
-
頁(yè)表的拆分過(guò)程
-
頁(yè)目錄結(jié)構(gòu)
-
幾個(gè)相關(guān)的寄存器
-
加載用戶程序時(shí): 頁(yè)目錄、頁(yè)表的分配和填充過(guò)程
-
線性地址到物理地址的查找、計(jì)算實(shí)例
頁(yè)表
在一個(gè)32位的系統(tǒng)中,物理內(nèi)存的最大可表示空間就是0xFFFF_FFFF,也就是4GB。

一個(gè)用戶程序中定義的幾個(gè)段,可能實(shí)際上只使用了很小的空間,完全用不到 4 GB。為了解決這個(gè)問(wèn)題,可以把這個(gè)單一映射表拆分成1024個(gè)體積更小的映射表:但是仍然需要為它分配多達(dá) 4MB 的物理內(nèi)存空間來(lái)保存這個(gè)映射表,很浪費(fèi)。
- 每一個(gè)映射表中,只有 1024 個(gè)表項(xiàng),每一個(gè)表項(xiàng)仍然指向一個(gè)物理頁(yè)的起始地址;
- 一共使用 1024 個(gè)這樣的映射表;

計(jì)算過(guò)程:每一個(gè)頁(yè)表項(xiàng)指向一個(gè) 4KB 的物理頁(yè),那么一個(gè)頁(yè)表中 1024 個(gè)頁(yè)表項(xiàng),一共能覆蓋 4MB 的物理內(nèi)存;
那么 10MB 的程序,向上對(duì)齊取整之后(4MB 的倍數(shù),就是 12 MB),就需要 3 個(gè)頁(yè)表就可以了。


P(Present): 存在位。1 - 物理頁(yè)存在; 0 - 物理頁(yè)不存在;RW(Read/Write): 讀/寫(xiě)位。1 - 這個(gè)物理頁(yè)可讀可寫(xiě); 0 - 這個(gè)物理頁(yè)只可讀;
D(Dirty): 臟位。表示這個(gè)物理頁(yè)中的數(shù)據(jù)是否被寫(xiě)過(guò);
頁(yè)目錄
現(xiàn)在,每一個(gè)物理頁(yè),都被一個(gè)頁(yè)表中的一個(gè)表項(xiàng)來(lái)指向了,那么這1024個(gè)頁(yè)表的地址,應(yīng)該怎么來(lái)管理呢?
操作系統(tǒng)在加載用戶程序的時(shí)候,不僅僅需要分配物理內(nèi)存,來(lái)存放程序的內(nèi)容;而且還需要分配物理內(nèi)存,用來(lái)保存程序的頁(yè)目錄和頁(yè)表。


相關(guān)寄存器
現(xiàn)在,所有頁(yè)表的物理地址被頁(yè)目錄表項(xiàng)指向了,那么頁(yè)目錄的物理地址,處理器是怎么知道的呢?
當(dāng)然,處理器中還有一個(gè)快表,用來(lái)加快從線性地址到物理地址的轉(zhuǎn)換過(guò)程。CR3寄存器的格式如下:


加載用戶程序時(shí): 物理頁(yè)分配過(guò)程
在之前的文章中,介紹過(guò)一個(gè)用戶程序被操作系統(tǒng)加載的全過(guò)程,簡(jiǎn)述如下:
這里主要聊一下第3步,假設(shè)用戶程序文件在硬盤(pán)上的長(zhǎng)度是20 MB,電腦中實(shí)際安裝的物理內(nèi)存是1 GB。
- 讀取程序 header 信息,解析出程序的總長(zhǎng)度,從任務(wù)自己的虛擬內(nèi)存中分配一塊足夠的連續(xù)空間;
- 分配一個(gè)空閑物理頁(yè),用作程序的頁(yè)目錄,頁(yè)目錄的地址會(huì)記錄在稍后創(chuàng)建的 TSS 段中;
- 使用虛擬內(nèi)存中的線性地址,分配一個(gè)物理頁(yè)(4 KB),登記到頁(yè)目錄和頁(yè)表中;
- 從硬盤(pán)上讀取 8 個(gè)扇區(qū)的數(shù)據(jù)(每個(gè)扇區(qū) 512 字節(jié)),存放到剛才分配的物理頁(yè)中;
- 檢查程序內(nèi)容是否讀取完畢:是-進(jìn)入第 6 步;否-返回到第 3 步;
- 為用戶程序創(chuàng)建一些必要的內(nèi)核數(shù)據(jù)結(jié)構(gòu),比如:TSS、TCB/PCB 等等;
- 為用戶程序創(chuàng)建 LDT,并且在其中創(chuàng)建每一個(gè)段描述符;
- 把操作系統(tǒng)的頁(yè)目錄中高端地址部分的表項(xiàng),復(fù)制給用戶程序的頁(yè)目錄表。
這樣的話,所有用戶程序的頁(yè)目錄中,高端地址的表項(xiàng)都指向相同的頁(yè)表地址,就達(dá)到了共享“操作系統(tǒng)空間”的目的。
可以先計(jì)算一下:頁(yè)目錄中,每一個(gè)表項(xiàng)覆蓋的空間是 4 MB,那么 20 MB的數(shù)據(jù),需要 5 個(gè)表項(xiàng)就可以了。在初始狀態(tài),頁(yè)目錄中的所有表項(xiàng)都是空的,其中的P位都是為0,表示頁(yè)表不存在。
注意:在“平坦”型分段模型下,線性地址等于虛擬地址。0x4000_0000 = 0100_0000_0000_0000___0000_0000_0000_0000



一個(gè)物理頁(yè)的地址一定是4KB對(duì)齊的(最后的12位全部為0),所以只需要記錄物理頁(yè)地址的高 20 位即可。用于存儲(chǔ)程序文件內(nèi)容的物理頁(yè)分配好了,下面就開(kāi)始從硬盤(pán)中讀取程序文件的內(nèi)容了。
剛才已經(jīng)假設(shè):用戶程序文件在硬盤(pán)上的長(zhǎng)度是20 MB。當(dāng)讀取了一個(gè)物理頁(yè)的內(nèi)容后,通過(guò)計(jì)算發(fā)現(xiàn)用戶程序內(nèi)容還沒(méi)有讀取完,于是繼續(xù)重復(fù)以上流程。
- 線性地址增加 4KB:0x4000_1000 = 0100_0000_0000_0000___0001_0000_0000_0000;
- 前 10 位沒(méi)有變,仍然是頁(yè)目錄中的第 256 個(gè)表項(xiàng),發(fā)現(xiàn)這個(gè)表項(xiàng)指向的頁(yè)表已經(jīng)存在了,于是就不用再分配物理頁(yè)用作頁(yè)表了;
- 分配一個(gè)空閑物理頁(yè),用于存放程序內(nèi)容,假設(shè)在 0x0100_4000處找到一個(gè),把這個(gè)地址登記在頁(yè)表中;
此時(shí),線性地址的中間 10 位的索引值是 1,所以登記在頁(yè)表中的第 1 個(gè)表項(xiàng)。
- 從硬盤(pán)上讀取 8 個(gè)扇區(qū)的數(shù)據(jù),寫(xiě)入這個(gè)物理頁(yè);

后面的過(guò)程就不再嘮叨了,一樣一樣的~~
- 確定頁(yè)目錄表項(xiàng):
0x4040_0000 = 0100_0000_0100_0000___0000_0000_0000_0000,前 10 位的索引值是 257;
- 發(fā)現(xiàn) 257 這個(gè)表項(xiàng)為空,于是分配一個(gè)空閑的物理頁(yè),用作頁(yè)表;
- 分配一個(gè)物理頁(yè),用作存儲(chǔ)程序文件的內(nèi)容,并把這個(gè)物理頁(yè)的地址記錄在頁(yè)表中;
線性地址 0x4040_0000 的中間 10 位的索引值是 0,所以登記在頁(yè)表的第一個(gè)表項(xiàng)中;

線性地址到物理地址的變換過(guò)程
如果理解了上一個(gè)主題的內(nèi)容,那么部分應(yīng)該就可以不用再看了,因?yàn)樗鼈z是相反的過(guò)程,而且查找過(guò)程更簡(jiǎn)單一些。
也就是如下圖所示:
- 用戶程序的長(zhǎng)度是 20 MB,存放在虛擬內(nèi)存 0x4000_0000 ~ 0x4140_0000 (線性地址)這段空間內(nèi);
- 代碼段的長(zhǎng)度是 8 MB,從虛擬內(nèi)存的 0x40C0_0000 處開(kāi)始存放;


頁(yè)目錄表的開(kāi)始地址,肯定是從 CR3 寄存器獲取的;然后,根據(jù)線性地址的中間 10 位(00_0000___1000),得到頁(yè)表中的索引值為8。
------ End ------