<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Linux從頭學(xué)16:操作系統(tǒng)-如何把【頁(yè)目錄和頁(yè)表】當(dāng)做普通物理頁(yè)進(jìn)行操作的?

          共 4998字,需瀏覽 10分鐘

           ·

          2021-10-20 16:21

          作 ?者:道哥,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 格式)。


          目錄

          • 問(wèn)題描述

            • CPU接收的是線(xiàn)性地址,不是物理地址

          • 對(duì)頁(yè)目錄進(jìn)行"自操作"

            • 一級(jí)查表:構(gòu)造線(xiàn)性地址的前十位

            • 二級(jí)查表:構(gòu)造線(xiàn)性地址的中間十位

            • 三級(jí)查表:構(gòu)造線(xiàn)性地址的最后十二位

            • 三個(gè)地址段合體

          • 對(duì)頁(yè)表進(jìn)行"自操作"

          x86 系統(tǒng)中,內(nèi)存管理中的分頁(yè)機(jī)制是非常重要的,在Linux操作系統(tǒng)相關(guān)的各種書(shū)籍中,這部分內(nèi)容也是重筆濃彩。

          如果你看過(guò) Linux 內(nèi)核相關(guān)書(shū)籍,一定對(duì)下面這張圖又熟悉、又恐懼:

          這是 Linux 系統(tǒng)中,頁(yè)處理單元的多級(jí)頁(yè)表查詢(xún)方式。

          其中黃色背景部分:頁(yè)上級(jí)目錄索引 和 頁(yè)中間目錄索引,是 Linux 系統(tǒng)自己擴(kuò)展的,在原本的 x86 處理器中是不存在的,這也是導(dǎo)致 Linux 中相關(guān)部分代碼更加復(fù)雜的原因。

          在上一篇文章中,我們主要對(duì) x86 中的頁(yè)目錄和頁(yè)表的“反向構(gòu)造”、“正向查找”這兩個(gè)過(guò)程進(jìn)行了圖文并茂的討論。文章鏈接在此:Linux從頭學(xué)15:【頁(yè)目錄和頁(yè)表】-理論 + 實(shí)例 + 圖文的最完全、最接地氣詳解!,但是其中有一個(gè)環(huán)節(jié)被特意忽略過(guò)去了。

          那就是:在操作系統(tǒng)構(gòu)造頁(yè)目錄和頁(yè)表的時(shí)候,如何對(duì)它們自身進(jìn)行尋址和操作?

          這部分內(nèi)容,也是內(nèi)存管理中比較復(fù)雜的地方,就好比一名醫(yī)生給病人做手術(shù),但是病人卻是“醫(yī)生自己”。

          這篇文章,我們繼續(xù)通過(guò)圖片+實(shí)例的方式,一起來(lái)研究一下內(nèi)核代碼一般都是如何來(lái)進(jìn)行這些“自操作”的。

          把這里面的操作機(jī)制研究透徹之后,再去看 Linux 內(nèi)核代碼時(shí),就不會(huì)暈頭轉(zhuǎn)向了。

          問(wèn)題描述

          在上一篇文章中,我們舉了這樣一個(gè)示例:

          1. 假設(shè)實(shí)際的物理內(nèi)存是1 GB;

          2. 用戶(hù)程序文件在硬盤(pán)上的長(zhǎng)度是20 MB;

          3. 操作系統(tǒng)把用戶(hù)程序加載到內(nèi)存中時(shí),從 0x4000_0000 的虛擬內(nèi)存地址處開(kāi)始存放;

          4. 操作系統(tǒng)讀取程序結(jié)束后,為所有的地址構(gòu)造好了頁(yè)目錄和頁(yè)表;

          如下圖所示:

          頁(yè)目錄和頁(yè)表的每一個(gè)有效表項(xiàng)中,存儲(chǔ)的地址都是一個(gè)個(gè)實(shí)實(shí)在在的物理頁(yè)的前 20 位(因?yàn)橐粋€(gè)物理頁(yè)的長(zhǎng)度固定是 4KB,在分配時(shí)都是對(duì)齊的,末尾的 12 位全部為 0)。

          并且頁(yè)目錄和頁(yè)表“們”自身,都占用一個(gè)物理頁(yè)的空間,所以它們都有自己的物理地址。

          當(dāng)頁(yè)目錄和頁(yè)表都構(gòu)造妥當(dāng)之后,處理器面對(duì)一個(gè)線(xiàn)性地址,例如:0x4100_1800,頁(yè)處理單元就會(huì)按照分級(jí)查表的方式,把這個(gè)線(xiàn)性地址轉(zhuǎn)換為一個(gè)物理地址:

          1. 拆分線(xiàn)性地址:0x4100_1800 = 0100_0001_0000_0000___0001_1000_0000_0000;

          2. 根據(jù)線(xiàn)性地址的前 10 位,找到頁(yè)目錄中的索引 260,從而確定頁(yè)表的物理地址是 0x0800_4000(表項(xiàng)中的值是 0x08004,還要補(bǔ)上低位的 12 個(gè) 0);

          3. 根據(jù)線(xiàn)性地址的中間 10 位,找到 0x0800_4000 這個(gè)頁(yè)表中的索引 1,從而確定普通物理頁(yè)的物理地址是 0x0210_1000(表項(xiàng)中的值是 0x02101,還要補(bǔ)上低位的 12 個(gè) 0);

          4. 根據(jù)線(xiàn)性地址的最后 12 位,確定普通頁(yè)內(nèi)的偏移量是 2048,普通頁(yè)的開(kāi)始地址加上這個(gè)偏移量,就得到了最終的物理地址 0x0210_1800。

          詳細(xì)的討論過(guò)程,請(qǐng)參考上一篇文章:Linux從頭學(xué)15:【頁(yè)目錄和頁(yè)表】-理論 + 實(shí)例 + 圖文的最完全、最接地氣詳解!。

          那么,問(wèn)題來(lái)了:

          在頁(yè)處理單元開(kāi)啟的情況下,處理器面對(duì)的是線(xiàn)性地址,那么操作系統(tǒng)在構(gòu)造頁(yè)目錄中的每一個(gè)表項(xiàng)的時(shí)候,如何對(duì)這個(gè)表項(xiàng)進(jìn)行尋址?

          具體到上圖來(lái)說(shuō)就是:操作系統(tǒng)想把第一個(gè)頁(yè)表的物理地址 0x0800_0000,填寫(xiě)到頁(yè)目錄的第 256 個(gè)表項(xiàng)中時(shí),那么 CPU 就需要找到這個(gè)表項(xiàng),這個(gè)表項(xiàng)肯定有物理地址的。

          但是,我們不能把這個(gè)表項(xiàng)的物理地址直接告訴 CPU,因?yàn)?CPU 只接收線(xiàn)性地址,它會(huì)自動(dòng)經(jīng)過(guò)分頁(yè)單元的處理來(lái)得到對(duì)應(yīng)的物理地址。

          那么,這個(gè)線(xiàn)性地址的值應(yīng)該是多少呢?

          繼續(xù)用實(shí)例來(lái)說(shuō)明,這樣容易理解。

          假設(shè)頁(yè)目錄所處的物理頁(yè)開(kāi)始地址0x0100_0000,那么第256個(gè)表項(xiàng)的物理地址就是 0x0100_0400。

          有些小伙伴可能會(huì)說(shuō):直接把物理地址 0x0100_0400 告訴處理器,不就可以了嗎?

          這是不對(duì)的!

          處理器接收的是線(xiàn)性地址,不是物理地址

          因?yàn)楝F(xiàn)在已經(jīng)開(kāi)啟了分頁(yè)處理單元,0x0100_0400 是我們最后想得到的物理地址,而處理器只接受線(xiàn)性地址,雖然我們知道這是一個(gè)物理地址,但是處理器不知道啊!

          當(dāng)我們給處理器一個(gè)地址的時(shí)候,處理器會(huì)按部就班的對(duì)這個(gè)地址進(jìn)行[段轉(zhuǎn)換],再進(jìn)行[頁(yè)轉(zhuǎn)換],這時(shí)才得到它認(rèn)為的物理地址。

          由于使用的是“平坦型”的段結(jié)構(gòu),所以這里就忽略了段處理過(guò)程,直接討論頁(yè)處理過(guò)程。

          所以,我們應(yīng)該使用某些方法,構(gòu)造出一個(gè)線(xiàn)性地址 addr,讓這個(gè)地址經(jīng)過(guò)頁(yè)處理單元之后,得到 0x0100_0400 這個(gè)物理地址:

          這里有點(diǎn)遞歸的味道,又有點(diǎn)像一個(gè)醫(yī)生給他自己做一個(gè)外科手術(shù)!

          現(xiàn)在,應(yīng)該明白面對(duì)的問(wèn)題了吧?

          目標(biāo)就是:通過(guò)某種方法,構(gòu)造出一個(gè)線(xiàn)性地址 addr,并且通過(guò)頁(yè)處理單元轉(zhuǎn)換之后,得到物理地址 0x0100_0400

          對(duì)頁(yè)目錄進(jìn)行操作

          重新梳理一下思路:如果對(duì)一個(gè)普通物理頁(yè)(下文簡(jiǎn)稱(chēng)為:普通頁(yè))里的一個(gè)地址處的數(shù)據(jù)進(jìn)行操作,需要經(jīng)過(guò)3次查表操作:

          從頁(yè)表的某個(gè)表項(xiàng)中,找到的那個(gè)物理地址,就是最后要操作的普通物理頁(yè)。

          現(xiàn)在我們的問(wèn)題是:需要把頁(yè)目錄作為最終的操作對(duì)象。

          也就是說(shuō),從頁(yè)表中找到的“普通頁(yè)”的物理地址,應(yīng)該等于頁(yè)目錄的物理地址!

          作為一名軟件開(kāi)發(fā)人員,遞歸思想都是有的。

          我們就來(lái)構(gòu)造一個(gè)線(xiàn)性地址 addr,讓它經(jīng)過(guò)3次查表操作之后,能夠指向頁(yè)目錄的物理地址。

          一級(jí)查表:構(gòu)造線(xiàn)性地址的前 10 位,來(lái)確定頁(yè)表的物理地址

          一級(jí)查表:查找的對(duì)象是頁(yè)目錄。

          線(xiàn)性地址addr的前10位,決定了頁(yè)目錄內(nèi)的索引。

          很顯然,需要讓這個(gè)索引對(duì)應(yīng)的那個(gè)表項(xiàng)中所登記的地址,必須是指向頁(yè)目錄自己才可以。

          常用的解決方案是:利用頁(yè)目錄中的最后一個(gè)表項(xiàng),讓這個(gè)表項(xiàng)中記錄的地址,指向頁(yè)目錄自己,如下圖所示:

          也就是說(shuō),預(yù)先在頁(yè)目錄的最后一個(gè)表項(xiàng)中,填入頁(yè)目錄自己的物理地址,然后只要線(xiàn)性地址addr10位的值為 1023,就能夠得到這個(gè)表項(xiàng)。

          很容易就能得到addr的前10位應(yīng)該是:0x3FF(二進(jìn)制:1111_1111_11)。

          由于這個(gè)表項(xiàng)中存儲(chǔ)的地址是頁(yè)目錄自己的開(kāi)始地址(0x0100_0000, 最后的12個(gè)0是自動(dòng)補(bǔ)上的),這樣就相當(dāng)于:下面進(jìn)入第二級(jí)查找時(shí),頁(yè)目錄即將被當(dāng)做“頁(yè)表”來(lái)使用

          如下圖所示:

          這里紅色虛線(xiàn)的“頁(yè)表”其實(shí)就是頁(yè)目錄自己,只是一個(gè)影子而已。

          二級(jí)查表:構(gòu)造線(xiàn)性地址的中間 10 位,來(lái)確定“普通頁(yè)”的物理地址

          二級(jí)查表:查找的對(duì)象是頁(yè)表,也就是一級(jí)查表得到的那個(gè)“頁(yè)表”。

          雖然一級(jí)查表的結(jié)果是頁(yè)目錄自己,但是處理器不管這些,它會(huì)把這個(gè)表當(dāng)做頁(yè)表來(lái)使用。

          現(xiàn)在,來(lái)考慮線(xiàn)性地址addr的中間10位,它決定了頁(yè)表中的索引號(hào)。

          很顯然,需要繼續(xù)讓這個(gè)索引號(hào)對(duì)應(yīng)的那個(gè)表項(xiàng)中,記錄的地址必須繼續(xù)指向頁(yè)目錄自己。

          那就繼續(xù)利用這個(gè)“頁(yè)表”(其實(shí)它是頁(yè)目錄)中的最后一個(gè)表項(xiàng)唄,就是index = 1023的這個(gè)表項(xiàng)。

          這個(gè)表項(xiàng)中存儲(chǔ)的物理地址,即將是最終查表得到的“普通頁(yè)”的物理地址了。

          由于這個(gè)表項(xiàng)中,被預(yù)先填寫(xiě)了 0x01000,補(bǔ)上尾部的12個(gè)0之后就是 0x0100_0000,仍然指向頁(yè)目錄自己,完美!

          于是,就得到了中間10位的結(jié)果:0x3FF(二進(jìn)制:11_1111_1111)

          如下圖所示:

          最右面紅色虛線(xiàn)的“物理頁(yè)”,就是二級(jí)查找的結(jié)果,它本質(zhì)上仍然是頁(yè)目錄本身,只不過(guò)它即將被當(dāng)做一個(gè)普通物理頁(yè)來(lái)使用。

          三級(jí)查表:構(gòu)造線(xiàn)性地址的最后 12 位,來(lái)確定“普通頁(yè)”的頁(yè)內(nèi)偏移量

          現(xiàn)在,已經(jīng)構(gòu)造出了線(xiàn)性地址addr(這是我們的最終目標(biāo))的前20位,并且經(jīng)過(guò)頁(yè)表的前兩級(jí)查表,成功的定位到了頁(yè)目錄自己!

          就差最后一步了!

          我們知道,從線(xiàn)性地址到物理地址的轉(zhuǎn)換過(guò)程中,最后的12位表示頁(yè)內(nèi)偏移,是直接從線(xiàn)性地址中取過(guò)來(lái)的。

          也就是說(shuō):線(xiàn)性地址 與 物理地址 的最后12位偏移量,值是一樣的!

          所以,我們就反過(guò)來(lái)倒推一下:

          我們最終想操作的是頁(yè)目錄中第256個(gè)表項(xiàng),它的物理地址是 0x0100_0400,這個(gè)物理地址距離這個(gè)頁(yè)目錄開(kāi)始位地址的偏移量是:0x400(0x0100_0400 減去 0x0100_0000)。

          因此,線(xiàn)性地址addr中的最后12位的值也應(yīng)該是 0x400

          三個(gè)地址段合體

          把上面三個(gè)步驟中,得到的地址聚合在一起:

          0xFFFF_F400 就是最終想得到的線(xiàn)性地址!

          也就是說(shuō),我們只要把這個(gè)線(xiàn)性地址 0xFFFF_F400 告訴處理器,它就會(huì)經(jīng)過(guò)頁(yè)處理單元的轉(zhuǎn)換,最終查找到頁(yè)目錄這個(gè)物理頁(yè)中的第 256個(gè)表項(xiàng),也就是物理地址 0x0100_0400。

          例如:mov [0xFFFF_4000], xxxx

          以上就是操作系統(tǒng)在操作頁(yè)目錄自身時(shí),所采取的策略。

          具體到每個(gè)操作系統(tǒng)來(lái)說(shuō),可能稍微有差別,但是其中的道理都是差不多的。

          例如本文開(kāi)頭的第一張圖中,Linux 使用了4級(jí)表格來(lái)查找,并且中間的兩個(gè)表格還可以省略不用。

          如何跨過(guò)中間的這兩個(gè)表格,Linux 內(nèi)核代碼中的代碼更復(fù)雜一些,但是策略都是一樣的。

          對(duì)頁(yè)表進(jìn)行尋址

          既然已經(jīng)弄明白了操作系統(tǒng)是如何操作頁(yè)目錄的,那么對(duì)頁(yè)表的操作就不是什么大問(wèn)題了。

          比如下面這張圖:

          目標(biāo):把最右面的普通物理頁(yè)地址 0x0200_0000,放入 0x0800_0000 這個(gè)頁(yè)表的第一個(gè)表項(xiàng)中(只需要存儲(chǔ)前20位),那么應(yīng)該傳遞什么樣的線(xiàn)性地址給處理器?

          思路是完全一樣的。

          一級(jí)查表

          按照正常的分頁(yè)查找流程,從頁(yè)目錄的某個(gè)表項(xiàng)中,查找我們想操作的那個(gè)頁(yè)表

          頁(yè)目錄中的這個(gè)表項(xiàng)位于索引值256的地方,因此可以構(gòu)造出線(xiàn)性地址的前10位是:0100_0000_00(0x100)。

          所以,經(jīng)過(guò)一級(jí)查表得到的這個(gè)頁(yè)表的物理地址是 0x0800_0000。

          二級(jí)查表

          利用這個(gè)頁(yè)表的最后一個(gè)表項(xiàng)(index = 1023),預(yù)先填寫(xiě)一個(gè)地址(0x08000),讓它指向這個(gè)頁(yè)表自己的開(kāi)始物理地址。

          于是,可以構(gòu)造出線(xiàn)性地址的中間10位是:11_1111_1111(0x3FF)。

          由于這個(gè)表項(xiàng)中存儲(chǔ)的地址是 0x0800_0000,指向的正是頁(yè)表自己,只不過(guò)馬上它就被當(dāng)作普通物理頁(yè)被使用。

          三級(jí)查表

          此時(shí),已經(jīng)找到最后的普通物理頁(yè)了(其實(shí)它是一個(gè)頁(yè)表,被當(dāng)作普通物理頁(yè)使用)。

          線(xiàn)性地址的最后12位,可以直接從最后想操作的那個(gè)目標(biāo)物理地址中最后12位直接拿過(guò)來(lái)。

          我們的目標(biāo)是:操作頁(yè)表中的第 0 個(gè)表項(xiàng),這個(gè)表項(xiàng)的物理地址是 0x0800_0000,最后的12位偏移量是 0000_0000_0000。

          把以上3個(gè)地址段合體,即可得到正確的線(xiàn)性地址


          ------ End ------

          這里討論的方法,并不是處理頁(yè)目錄和頁(yè)表的唯一方式。

          當(dāng)處理邏輯更加復(fù)雜時(shí),可能需要對(duì)頁(yè)目錄或頁(yè)表中更多的表項(xiàng),進(jìn)行一些特殊的預(yù)處理。

          如果你想挑戰(zhàn)一下,可以看一下Linux內(nèi)核中的相關(guān)文檔或代碼!

          在這個(gè)系列中,關(guān)于頁(yè)目錄和頁(yè)表的知識(shí)點(diǎn)就介紹結(jié)束了。

          如果文中有錯(cuò)誤或者誤導(dǎo)的地方,非常期待與您一起探討、學(xué)習(xí)!

          寫(xiě)這篇文章真不容易,讓我深深的體會(huì)到那句話(huà):

          寫(xiě)作就是:將網(wǎng)狀的思考-通過(guò)樹(shù)狀的結(jié)構(gòu)-用線(xiàn)性的語(yǔ)言清晰的表達(dá)出來(lái)。

          如果您覺(jué)得還不錯(cuò),請(qǐng)點(diǎn)個(gè)贊,鼓勵(lì)一下,轉(zhuǎn)發(fā)給身邊的技術(shù)小伙伴,真心的感謝!

          推薦閱讀

          【1】《Linux 從頭學(xué)》系列文章

          【2】C語(yǔ)言指針-從底層原理到花式技巧,用圖文和代碼幫你講解透徹

          【3】原來(lái)gdb的底層調(diào)試原理這么簡(jiǎn)單

          【4】?jī)?nèi)聯(lián)匯編很可怕嗎?看完這篇文章,終結(jié)它!

          其他系列專(zhuān)輯:精選文章、應(yīng)用程序設(shè)計(jì)物聯(lián)網(wǎng)、 C語(yǔ)言。


          星標(biāo)公眾號(hào),能更快找到我!


          瀏覽 61
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲www啪成人一区二区麻豆 | 青青草免费在线公开视频播放 | 97超碰人人操 | 日韩中文字幕成人 | 天天日夜夜干 |