設(shè)置
  • 日夜間
    隨系統(tǒng)
    淺色
    深色
  • 主題色

Linux 0.11 第 6 回 | 段寄存器的歷史包袱

低并發(fā)編程 2022/10/5 12:41:10 責(zé)編:云熙

本文來自微信公眾號:低并發(fā)編程 (ID:dibingfa),作者:閃客

原文標(biāo)題:《第六回 | 先解決段寄存器的歷史包袱問題》

本系列會以一個讀小說的心態(tài),從開機(jī)啟動后的代碼執(zhí)行順序,帶著大家閱讀和賞析 Linux 0.11 全部核心代碼,了解操作系統(tǒng)的技術(shù)細(xì)節(jié)和設(shè)計思想。

你會跟著我一起,看著一個操作系統(tǒng)從啥都沒有開始,一步一步最終實現(xiàn)它復(fù)雜又精巧的設(shè)計,讀完這個系列后希望你能發(fā)出感嘆,原來操作系統(tǒng)源碼就是這破玩意。

以下是已發(fā)布文章的列表,詳細(xì)了解本系列可以先從開篇詞看起。

開篇詞

第一回 | 最開始的兩行代碼

第二回 | 自己給自己挪個地兒

第三回 | 做好最最基礎(chǔ)的準(zhǔn)備工作

第四回 | 把自己在硬盤里的其他部分也放到內(nèi)存來

第五回 | 進(jìn)入保護(hù)模式前的最后一次折騰內(nèi)存

本系列的 GitHub 地址如下

https://github.com/sunym1993/flash-linux0.11-talk

------- 正文開始 -------

書接上回,上回書咱們說到,操作系統(tǒng)又折騰了一下內(nèi)存,之后的很長一段時間內(nèi)存布局就不會變了,終于穩(wěn)定下來了,目前它長這個樣子。

0 地址開始處存放著操作系統(tǒng)的全部代碼嗎,也就是 system 模塊,0x90000 位置處往后的幾十個字節(jié)存放著一些設(shè)備的信息,方便以后使用。

內(nèi)存地址長度 (字節(jié))名稱
0x900002光標(biāo)位置
0x900022
擴(kuò)展內(nèi)存數(shù)
0x900042顯示頁面
0x900061
顯示模式
0x900071字符列數(shù)
0x900082未知
0x9000A1
顯示內(nèi)存
0x9000B1
顯示狀態(tài)
0x9000C2顯卡特性參數(shù)
0x9000E1
屏幕行數(shù)
0x9000F1屏幕列數(shù)
0x9008016
硬盤 1 參數(shù)表
0x9009016硬盤 2 參數(shù)表
0x901FC2
根設(shè)備號

是不是十分清晰?不過別高興得太早,清爽的內(nèi)存布局,是方便后續(xù)操作系統(tǒng)的大顯身手!

接下來就要進(jìn)行真正的第一項大工程了,那就是模式的轉(zhuǎn)換,需要從現(xiàn)在的 16 位的實模式轉(zhuǎn)變?yōu)橹?32 位的保護(hù)模式

當(dāng)然,雖說是一項非常難啃的大工程,但從代碼量看,卻是少得可憐,所以不必太過擔(dān)心。

每次講這里都十分的麻煩,因為這是 x86 的歷史包袱問題,現(xiàn)在的 CPU 幾乎都是支持 32 位模式甚至 64 位模式了,很少有還僅僅停留在 16 位的實模式下的 CPU。所以我們要為了這個歷史包袱,寫一段模式轉(zhuǎn)換的代碼,如果 Intel CPU 被重新設(shè)計而不用考慮兼容性,那么今天的代碼將會減少很多甚至不復(fù)存在。

所以不用擔(dān)心,聽懂就聽懂,聽不懂就拉倒,放寬心。

我不打算直接說實模式和保護(hù)模式的區(qū)別,我們還是跟著代碼慢慢品味,來。

這里仍然是 setup.s 文件中的代碼咯。

  idt_48      ; load idt with 0,0
  gdt_48      ; load gdt with whatever ropriate

idt_48:
    .word   0     ; idt limit=0
    .word   0,0   ; idt base=0L

上來就是兩行看不懂的指令,別急。

要理解這兩條指令,就涉及到實模式和保護(hù)模式的第一個區(qū)別了。我們現(xiàn)在還處于實模式下,這個模式的 CPU 計算物理地址的方式還記得么?不記得的話看一下 第一回 最開始的兩行代碼

就是段基址左移四位,再加上偏移地址。比如:

是不是覺得很別扭,那更別扭的地方就要來了。當(dāng) CPU 切換到保護(hù)模式后,同樣的代碼,內(nèi)存地址的計算方式還不一樣,你說氣不氣人?

變成啥樣了呢?剛剛那個 ds 寄存器里存儲的值,在實模式下叫做段基址,在保護(hù)模式下叫段選擇子。段選擇子里存儲著段描述符的索引。

通過段描述符索引,可以從全局描述符表 gdt 中找到一個段描述符,段描述符里存儲著段基址。

段基址取出來,再和偏移地址相加,就得到了物理地址,整個過程如下。

你就說煩不煩吧?同樣一段代碼,實模式下和保護(hù)模式下的結(jié)果還不同,但沒辦法,x86 的歷史包袱我們不得不考慮,誰讓我們沒其他 CPU 可選呢。

總結(jié)一下就是,段寄存器(比如 ds、ss、cs)里存儲的是段選擇子,段選擇子去全局描述符表中尋找段描述符,從中取出段基址

好了,那問題自然就出來了,全局描述符表(gdt)長什么樣?它在哪?怎么讓 CPU 知道它在哪?

長什么樣先別管,一定又是一個令人頭疼的數(shù)據(jù)結(jié)構(gòu),先說說它在哪?在內(nèi)存中唄,那么怎么告訴 CPU 全局描述符表(gdt)在內(nèi)存中的什么位置呢?答案是由操作系統(tǒng)把這個位置信息存儲在一個叫 gdtr 的寄存器中。

怎么存呢?就是剛剛那條指令。

  gdt_48

其中 lgdt 就表示把后面的值(gdt_48)放在 gdtr 寄存器中,gdt_48 標(biāo)簽,我們看看它長什么樣。

gdt_48:
    .word   0x800       ; gdt limit=2048, 256 GDT entries
    .word   512+gdt,0x9 ; gdt base = 0X9xxxx

可以看到這個標(biāo)簽位置處表示一個 48 位的數(shù)據(jù),其中高 32 位存儲著的正是全局描述符表 gdt 的內(nèi)存地址

0x90200 + gdt

gdt 是個標(biāo)簽,表示在本文件內(nèi)的偏移量,而本文件是 setup.s,編譯后是放在 0x90200 這個內(nèi)存地址的,還記得吧?所以要加上 0x90200 這個值。

那 gdt 這個標(biāo)簽處,就是全局描述符表在內(nèi)存中的真正數(shù)據(jù)了。

gdt:
    .word   0,0,0,0     ; dummy

    .word   0x07FF      ; 8Mb - limit=2047 (2048*4096=8Mb)
    .word   0x0000      ; base address=0
    .word   0x9A00      ; code read/exec
    .word   0x00C0      ; granularity=4096, 386

    .word   0x07FF      ; 8Mb - limit=2047 (2048*4096=8Mb)
    .word   0x0000      ; base address=0
    .word   0x9200      ; data read/write
    .word   0x00C0      ; granularity=4096, 386

具體細(xì)節(jié)不用關(guān)心,跟我看重點。

根據(jù)剛剛的段描述符格式。

可以看出目前全局描述符表有三個段描述符,第一個為,第二個是代碼段描述符(type=code),第三個是數(shù)據(jù)段描述符(type=data),第二個和第三個段描述符的段基址都是 0,也就是之后在邏輯地址轉(zhuǎn)換物理地址的時候,通過段選擇子查找到無論是代碼段還是數(shù)據(jù)段,取出的段基址都是 0,那么物理地址將直接等于程序員給出的邏輯地址(準(zhǔn)確說是邏輯地址中的偏移地址)。先記住這點就好。

具體段描述符的細(xì)節(jié)還有很多,就不展開了,比如這里的高 22 位就表示它是代碼段還是數(shù)據(jù)段。

接下來我們看看目前的內(nèi)存布局,還是別管比例。

這里我把 idtr 寄存器也畫出來了,這個是中斷描述符表,其原理和全局描述符表一樣。全局描述符表是讓段選擇子去里面尋找段描述符用的,而中斷描述符表是用來在發(fā)生中斷時,CPU 拿著中斷號去中斷描述符表中尋找中斷處理程序的地址,找到后就跳到相應(yīng)的中斷程序中去執(zhí)行,具體我們后面遇到了再說。

好了,今天我們就講,操作系統(tǒng)設(shè)置了個全局描述符表 gdt,為后面切換到保護(hù)模式后,能去那里尋找到段描述符,然后拼湊成最終的物理地址,就這個作用。當(dāng)然,還有很多段描述符,作用不僅僅是轉(zhuǎn)換成最終的物理地址,不過這是后話了。

這僅僅是進(jìn)入保護(hù)模式前準(zhǔn)備工作的其中一個,后面的路還長著呢。欲知后事如何,且聽下回分解。

------- 本回擴(kuò)展資料 -------

保護(hù)模式下邏輯地址到線性地址(不開啟分頁時就是物理地址)的轉(zhuǎn)化,看 Intel 手冊:

Volume 3 Chapter 3.4 Logical And Linear Addresses

段描述符結(jié)構(gòu)和詳細(xì)說明,看 Intel 手冊:

Volume 3 Chapter 3.4.5 Segment Descriptors

比如文中說的數(shù)據(jù)段與代碼段的劃分,其實還有更細(xì)分的權(quán)限控制。

------- 關(guān)于本系列 -------

本系列的開篇詞看這

閃客新系列!你管這破玩意叫操作系統(tǒng)源碼

本系列的擴(kuò)展資料看這(也可點擊閱讀原文),這里有很多有趣的資料、答疑、互動參與項目,持續(xù)更新中,希望有你的參與。

https://github.com/sunym1993/flash-linux0.11-talk

本系列全局視角

最后,祝大家都能追更到系列結(jié)束,只要你敢持續(xù)追更,并且把每一回的內(nèi)容搞懂,我就敢讓你在系列結(jié)束后說一句,我對 Linux 0.11 很熟悉。

廣告聲明:文內(nèi)含有的對外跳轉(zhuǎn)鏈接(包括不限于超鏈接、二維碼、口令等形式),用于傳遞更多信息,節(jié)省甄選時間,結(jié)果僅供參考,IT之家所有文章均包含本聲明。

相關(guān)文章

關(guān)鍵詞:Linux,linux

軟媒旗下網(wǎng)站: IT之家 最會買 - 返利返現(xiàn)優(yōu)惠券 iPhone之家 Win7之家 Win10之家 Win11之家

軟媒旗下軟件: 軟媒手機(jī)APP應(yīng)用 魔方 最會買 要知