Linux 是如何保護(hù)內(nèi)核代碼的?
作 者:道哥,10+年的嵌入式開(kāi)發(fā)老兵。
公眾號(hào):【IOT物聯(lián)網(wǎng)小鎮(zhèn)】,專注于:C/C++、Linux操作系統(tǒng)、應(yīng)用程序設(shè)計(jì)、物聯(lián)網(wǎng)、單片機(jī)和嵌入式開(kāi)發(fā)等領(lǐng)域。 公眾號(hào)回復(fù)【書(shū)籍】,獲取 Linux、嵌入式領(lǐng)域經(jīng)典書(shū)籍。
轉(zhuǎn) 載:歡迎轉(zhuǎn)載文章,轉(zhuǎn)載需注明出處。
從 16 位進(jìn)入到 32 位
8086 的 16 位模式
80386 的 32 位模
從實(shí)模式進(jìn)入到保護(hù)模式
如何進(jìn)入保護(hù)模式
GDT 全局段描述符表
GDTR 全局描述符表寄存器
段尋址過(guò)程描述
在之前的 7 篇文章中,我們一直學(xué)習(xí)的是最原始的 8086 處理器中的最底層的基本原理,重點(diǎn)是內(nèi)存的尋址方式。
也就是:CPU 是如何通過(guò)[段地址:偏移地址],來(lái)對(duì)內(nèi)存進(jìn)行尋址的。
不知道你是否發(fā)現(xiàn)了一個(gè)問(wèn)題:
所有的程序都可以對(duì)內(nèi)存中的任意位置的數(shù)據(jù)進(jìn)行讀取和修改,即使這個(gè)位置并不屬于這個(gè)應(yīng)用程序。
這是非常危險(xiǎn)的,想一想那些心懷惡意的黑帽子黑客,如果他們想做一些壞事情,可以說(shuō)是隨心所欲!
面對(duì)這樣的不安全行為,處理器一點(diǎn)辦法都沒(méi)有。
所以,Intel 從 80286 開(kāi)始,就對(duì)增加了一個(gè)叫做保護(hù)模式的機(jī)制。
PS: 相應(yīng)的,之前 8086 中的處理器執(zhí)行模式就叫做“實(shí)模式”。
雖然 80286 沒(méi)有形成一定的氣候,但是它對(duì)后來(lái)的 80386 處理器提供了基礎(chǔ),讓 386 獲得了極大的成功。
這篇文章,我們就從 80386 處理器開(kāi)始,聊一聊
保護(hù)模式究竟保護(hù)了誰(shuí)?
底層是通過(guò)什么機(jī)制來(lái)實(shí)現(xiàn)保護(hù)模式的?
我們的學(xué)習(xí)目標(biāo),就是弄明白下面這張圖:
從 16 位進(jìn)入到 32 位 8086 的 16 位模式
在 8086 處理器中,所有的寄存器都是 16 位的。
也正因?yàn)槿绱?處理器為了能夠得到 20 位的物理地址,需要把段寄存器的內(nèi)容左移 4 位之后,再加上偏移寄存器的內(nèi)容,才能得到一個(gè) 20 位的物理地址,最終訪問(wèn)最大 1MB 的內(nèi)存空間。
例如:在訪問(wèn)代碼段的時(shí)候,把 cs 寄存器左移 4 位,再加上 ip 寄存器,就得到 20 位的物理地址了;
20 位的地址,最大尋址范圍就是 2 的 20 次方 = 1 MB 的空間;
還記得我們第 1 篇文章Linux 從頭學(xué) 01:CPU 是如何執(zhí)行一條指令的?中的寄存器示意圖嗎?
以上這些寄存器都是 16 位的,在這種模式下,對(duì)內(nèi)存的訪問(wèn)只能分段進(jìn)行。
而且每一個(gè)段的偏移地址,最大只能到 64 KB 的范圍(2 的 16 次方)。
在訪問(wèn)代碼段的時(shí)候,使用 cs:ip 寄存器;
在訪問(wèn)數(shù)據(jù)段的時(shí)候,使用 ds 寄存器;
在訪問(wèn)棧的時(shí)候,使用 ss:sp 寄存器;
80386 的 32 位模式
進(jìn)入到 32 位的處理器之后,這些寄存器就擴(kuò)展到 32 位了:
從寄存器的名稱上可以出,在最前面增加了字母 E,表示 Extend 的意思。
這些 32 位的寄存器,低 16 位保持與 16 位處理器的兼容性,也就是可以使用 16 位的寄存器(例如:AX),也可以使用 8 位的寄存器(例如:AH, AL)。
注意:高 16 位不可以獨(dú)立使用。
下面這張圖是 32 位處理器的另外 4 個(gè)通用寄存器(注意它們是不能按照 8 位寄存器來(lái)使用的):
在 32 位的模式下,處理器中的地址線達(dá)到了 32 位,最大的內(nèi)存空間可尋址能力達(dá)到 4 GB(2 的 32 次方)。
在 32 位處理器中,依然可以兼容 16 位的處理模式,此時(shí)依然使用 16 位的寄存器;
如果不兼容的話,就會(huì)失去很大的市場(chǎng)占有率;
是不是感覺(jué)到上面的寄存器示意圖中漏掉了什么東西?
是的,圖中沒(méi)有展示出段寄存器(cs, ds, ss等等)。
這是因?yàn)樵?32 位模式下,段寄存器依然是 16 位的長(zhǎng)度,但是對(duì)其中內(nèi)容的解釋,發(fā)生了非常非常大的變化。
它們不再表示段的基地址,而是表示一個(gè)索引值以及其他信息。
通過(guò)這個(gè)索引值(或者叫索引號(hào)),到一個(gè)表中去查找該段的真正基地址(有點(diǎn)類似于中斷向量表的查找方式):
有些書(shū)上把描述符稱之為:段選擇子;
也有一些書(shū)上把段寄存器中的值稱之索引值,把段描述符在 GDT 中的偏移量稱之為選擇子;
不必糾結(jié)于稱呼,明白其中的道理就可以了;
正是因?yàn)樘幚砥饔?32 根地址線,可尋址的范圍已經(jīng)非常大了(4 GB),因此理論上它是不需要像 8086 中那樣的尋址方式(段地址左移 4 位 + 偏移地址)。
但是由于 x86 處理器的基因,在 32 位模式下,依然要以段為單位來(lái)訪問(wèn)內(nèi)存。
這里請(qǐng)大家不要繞暈了:剛才描述的段寄存器的內(nèi)容時(shí),僅僅是說(shuō)明如何來(lái)找到一個(gè)段的基地址,也即是說(shuō):
對(duì)于 8086 來(lái)說(shuō),段寄存器中的內(nèi)容左移 4 位之后,就是段的基地址;
對(duì)于 80386 來(lái)說(shuō),段寄存器中的內(nèi)容是一個(gè)表的索引號(hào),通過(guò)這個(gè)索引號(hào),去查找表中相應(yīng)位置中的內(nèi)容,這個(gè)內(nèi)容中就有段的基地址(如何查找,下文有描述);
找到了這個(gè)段的基地址之后,在訪問(wèn)內(nèi)存的時(shí)候,仍然是按照段機(jī)制+偏移量的方式。
由于在 32 位處理器中,存儲(chǔ)偏移地址的寄存器都是 32 位的,最大偏移地址可達(dá) 4 GB,所以,我們可以把段的基地址設(shè)置為 0x0000_0000:
這樣的分段方式,稱作“平坦模型”,也可以理解為沒(méi)有分段。
看到這里,是否聯(lián)想起之前的一篇文章中,我們?cè)?jīng)畫(huà)過(guò)一張 Linux 操作系統(tǒng)中的分段模型:
現(xiàn)在是不是大概就明白了:為什么這 4 個(gè)段的基地址和段的長(zhǎng)度,都是一樣的?
從實(shí)模式進(jìn)入到保護(hù)模式 如何進(jìn)入保護(hù)模式
CPU 是如何判斷:當(dāng)前是執(zhí)行的是實(shí)模式?還是保護(hù)模式?
在處理器內(nèi)部,有一個(gè)寄存器 CR0。這個(gè)寄存器的 bit0 位的值,就決定了當(dāng)前的工作模式:
bit0 = 0: 實(shí)模式;
bit1 = 1: 保護(hù)模式;
在處理器上電之后,默認(rèn)狀態(tài)下是工作在實(shí)模式。
當(dāng)操作系統(tǒng)做好進(jìn)入保護(hù)模式的一切準(zhǔn)備工作之后,就把 CR0 寄存器的 bit0 位設(shè)置為 1,此后 CPU 就開(kāi)始工作在保護(hù)模式。
也就是說(shuō):在 bit0 設(shè)置為 1 之前,CPU 都是按照實(shí)模式下的機(jī)制來(lái)進(jìn)行尋址(段地址左移 4 位 + 偏移地址);
當(dāng) bit0 設(shè)置為 1 之后,CPU 就按照保護(hù)模式下的機(jī)制來(lái)進(jìn)行尋址(通過(guò)段寄存器中的索引號(hào),到一個(gè)表中查找段的基地址,然后再加上偏移地址)。
GDT 全局段描述符表
由于這張表中的每一個(gè)條目(Entry),描述的是一個(gè)段的基本信息,包括:基地址、段的長(zhǎng)度界限、安全級(jí)別等等,因此我們稱之為全局描述符表(Global Descriper Table, GDT)。
之所以稱之為全局的,是因?yàn)槊恳粋(gè)應(yīng)用程序還可以把段描述符信息,放在自己的一個(gè)私有的局部描述符表中(Local Descriper Table,LDT),在以后的文章中一定會(huì)介紹到。
處理器規(guī)定:第一個(gè)描述符必須為空,主要是為了規(guī)避一些程序錯(cuò)誤。
從上圖中可以看出:GDT 中每一個(gè)條目的長(zhǎng)度是 8 個(gè)字節(jié),其中描述了一個(gè)段的具體信息,如下所示:
黃色部分:表示這個(gè)段在內(nèi)存中的基地址。
綠色部分:表示這個(gè)段的最大長(zhǎng)度是多少。
第一次看到這張圖時(shí),是不是心中有 2 個(gè)疑問(wèn):
為什么段的基地址不是用連續(xù)的 32 bit 位來(lái)表示?
段的界限怎么是 20 位的?20 位只能表示 1 MB 的范圍啊?
第一個(gè)問(wèn)題的答案是:歷史原因(兼容性)。
第二問(wèn)題的答案是:在每一個(gè)描述符中的標(biāo)志位 G,對(duì)段的界限進(jìn)行了進(jìn)一步的粒度描述:
如果 G = 0: 表示段界限是以字節(jié)為單位,此時(shí),段界限的最大表示范圍就是 1 MB;
如果 G = 1:表示段界限是以 4 KB 為單位,此時(shí),段界限的最大表示范圍就是 4 GB( 1 MB 乘以 4KB);
為了完整性,我把所有標(biāo)志位的含義都匯總?cè)缦?方便參考:
D/B (bit22):用來(lái)決定數(shù)據(jù)段 or 棧段使用的偏移寄存器是 16 位 還是 32 位。
L (bit21):在 64 位系統(tǒng)中才會(huì)使用,暫時(shí)先忽略。
AVL (bit20):處理器沒(méi)有使用這一位內(nèi)容,被操作系統(tǒng)可以利用這一位來(lái)做一些事情。
P (bit15):表示這個(gè)段的內(nèi)容,當(dāng)前是否已經(jīng)駐留在物理內(nèi)存中。
在 Linux 系統(tǒng)中,每一個(gè)應(yīng)用程序都擁有 4 GB(32位處理器) 的虛擬內(nèi)存空間,而且一個(gè)系統(tǒng)中可以同時(shí)存在多個(gè)應(yīng)用程序。
這些應(yīng)用程序在虛擬內(nèi)存中的代碼段、數(shù)據(jù)段等等,最終都是要映射到物理內(nèi)存中的。
但是物理內(nèi)存的空間畢竟是有限的,當(dāng)物理內(nèi)存緊張的時(shí)候,操作系統(tǒng)就會(huì)把當(dāng)前不在執(zhí)行的那些段的內(nèi)容,臨時(shí)保存在硬盤(pán)上(此時(shí),這個(gè)段描述符的 P 位就設(shè)置為 0),這稱之為換出。
當(dāng)這個(gè)被換出的段需要執(zhí)行時(shí),處理器發(fā)現(xiàn) P 位為 0,就知道段中的內(nèi)容不在物理內(nèi)存中,于是就在物理內(nèi)存中找出一塊空閑的空間,然后把硬盤(pán)中的內(nèi)容復(fù)制到物理內(nèi)存中,并且把 P 位設(shè)置為 1,這稱之為換入。
DPL (bit14 ~ 13):指定段的特權(quán)級(jí)別,處理器一共支持 4 個(gè)特權(quán)級(jí)別:0,1,2,3(特權(quán)級(jí)別最低)。
比如:操作系統(tǒng)的代碼段的特權(quán)級(jí)別是 0,而一個(gè)應(yīng)用程序在剛開(kāi)始啟動(dòng)的時(shí)候,操作系統(tǒng)給它分配的特權(quán)級(jí)別是 3,那么這個(gè)應(yīng)用程序就不能直接去轉(zhuǎn)移到操作系統(tǒng)的代碼段去執(zhí)行。
在 Linux 操作系統(tǒng)中,只利用了 0 和 3 這兩個(gè)特權(quán)級(jí)別。
S (bit12):決定這個(gè)段的類型。
TYPE (bit11 ~ 8):用來(lái)描述段的一些屬性,例如:可讀、可寫(xiě)、擴(kuò)展方向、代碼段的執(zhí)行特性等等。
這里的依從屬性不太好理解,它主要用于決定:從一個(gè)低特權(quán)級(jí)別的代碼,是否可以進(jìn)入另一個(gè)高特權(quán)級(jí)別的代碼。
如果可以進(jìn)入,那么當(dāng)前任務(wù)的請(qǐng)求級(jí)別 RPL 是否發(fā)生改變(以后會(huì)討論這個(gè)問(wèn)題)。
另外,操作系統(tǒng)可以把 A 標(biāo)志,加入到物理內(nèi)存的換出換入計(jì)算策略中。
這樣的話,就可以避免把最近頻繁訪問(wèn)的物理內(nèi)存換出,達(dá)到更好的系統(tǒng)性能。
GDTR 全局段描述符表寄存器
還有一個(gè)問(wèn)題需要處理:GDT 表本身也是數(shù)據(jù),也是需要存放在內(nèi)存中的。
那么: 它存放在內(nèi)存中的什么位置呢?CPU 又怎么能知道這個(gè)起始位置呢?
在處理器的內(nèi)部,有一個(gè)寄存器:GDTR (GDT Register),其中存儲(chǔ)了兩個(gè)信息:
我們可以從上一篇文章Linux從頭學(xué)07:【中斷】那么重要,它的本質(zhì)到底是什么?中,中斷向量表的安裝過(guò)程中進(jìn)行類比:
程序代碼把每一個(gè)中斷的處理程序地址,放在中斷向量表中的對(duì)應(yīng)位置;
中斷向量表的起始地址放在內(nèi)存的 0 地址處;
也就是說(shuō):處理器是到固定的地址 0 處,查找中斷向量表的,這是一個(gè)固定的地址。
而對(duì)于 GDT 表而言,它的起始地址不是固定的,而是可以放在內(nèi)存中的任意位置。
只要把這個(gè)位置存放到寄存器 GDTR 中,處理器在需要的時(shí)候就可以通過(guò) GDTR 來(lái)定位到 GDT 的起始地址。
其實(shí),GDT 在上電剛開(kāi)始的時(shí)候,也不能放在內(nèi)存中的任意位置。
因?yàn)樵谶M(jìn)入保護(hù)模式之前,處理器還是工作在實(shí)模式,只能尋址 1 MB 的內(nèi)存空間,因此,GDT 只能放在 1 MB 內(nèi)的地址空間中。
在進(jìn)入保護(hù)模式之后,能尋址更大的地址空間了,此時(shí)就可以重新把 GDT 放在更大的地址空間中了,然后把這個(gè)新的起始地址,存儲(chǔ)到 GDTR 寄存器中。
從 GDTR 寄存器中的內(nèi)容可以看出,它不僅存儲(chǔ)了 GDT 的起始地址,而且還限制了 GDT 的長(zhǎng)度。
這個(gè)長(zhǎng)度一共是 16 位,最大值是 64 KB( 2 的 16 次方),而一個(gè)段描述符信息是 8 B,那么 64 KB 的空間,最多一共可以存放 8192 個(gè)描述符。
這個(gè)數(shù)字,對(duì)于操作系統(tǒng)或者是一般的應(yīng)用程序來(lái)說(shuō),是綽綽有余了。
段尋址過(guò)程描述
在上面的段寄存器示意圖中,我們只說(shuō)明了段寄存器依然是 16 位的。
在保護(hù)模式下,對(duì)其中內(nèi)容的解釋,與實(shí)模式下是大不相同的。
我們以代碼段寄存器 CS 為例:
RPL: 表示當(dāng)前正在執(zhí)行的這個(gè)代碼段的請(qǐng)求特權(quán)級(jí);
TI: 表示到哪一個(gè)表中去找這個(gè)段的描述信息:全局描述符表(GDT) or 局部描述符表(LDT)?
TI = 0 時(shí),到 GDT 中找段描述符;
TI = 1 時(shí),到 LDT 中找段描述符;
假設(shè)當(dāng)前代碼段寄存器 cs 的值為 0x0008,處理器按照保護(hù)模式的機(jī)制來(lái)解釋其中的內(nèi)容:
TI = 0,表示到 GDT 中查找段描述符;
RPL = 0,表示請(qǐng)求特權(quán)級(jí)別是 0;
描述符索引是 1,表示這個(gè)段描述符在 GDT 中的第 1 個(gè)條目中。由于每一個(gè)描述符占用 8 個(gè)字節(jié),因此這個(gè)描述符的開(kāi)始地址位于 GDT 中的偏移地址為 8 的位置(1 * 8 = 8);
找到了這個(gè)段描述符條目之后,就可以從中獲取到這個(gè)代碼段的具體信息了:
代碼段的基地址在內(nèi)存中什么位置;
代碼段的最大長(zhǎng)度是多少(在獲取指令時(shí),如果偏移地址超過(guò)這個(gè)長(zhǎng)度,就引發(fā)異常);
代碼段的特權(quán)級(jí)別是多少,當(dāng)前是否駐留在物理內(nèi)存中等等;
另外,從上文描述的 GDTR 寄存內(nèi)容知道,它限制了 GDT 中最多一共可以存放 8192 個(gè)描述符。
我們?cè)購(gòu)拇a段寄存器中,描述符索引字段所占據(jù)的 13 個(gè) bit 位可以計(jì)算出,最多可以查找 8192 個(gè)段描述符。
2 的 13 次方 = 8192。
至此,處理器就在保護(hù)模式下,查找到了一個(gè)段的所有信息。
下面步驟就是:到這個(gè)段所在的內(nèi)存空間中,執(zhí)行其中的代碼,或者讀寫(xiě)其中的數(shù)據(jù)。
下一篇文章我們繼續(xù)。。。
發(fā)表評(píng)論
請(qǐng)輸入評(píng)論內(nèi)容...
請(qǐng)輸入評(píng)論/評(píng)論長(zhǎng)度6~500個(gè)字
最新活動(dòng)更多
-
10月31日立即下載>> 【限時(shí)免費(fèi)下載】TE暖通空調(diào)系統(tǒng)高效可靠的組件解決方案
-
即日-11.13立即報(bào)名>>> 【在線會(huì)議】多物理場(chǎng)仿真助跑新能源汽車(chē)
-
11月28日立即報(bào)名>>> 2024工程師系列—工業(yè)電子技術(shù)在線會(huì)議
-
12月19日立即報(bào)名>> 【線下會(huì)議】OFweek 2024(第九屆)物聯(lián)網(wǎng)產(chǎn)業(yè)大會(huì)
-
即日-12.26火熱報(bào)名中>> OFweek2024中國(guó)智造CIO在線峰會(huì)
-
即日-2025.8.1立即下載>> 《2024智能制造產(chǎn)業(yè)高端化、智能化、綠色化發(fā)展藍(lán)皮書(shū)》
推薦專題
- 1 【一周車(chē)話】沒(méi)有方向盤(pán)和踏板的車(chē),你敢坐嗎?
- 2 特斯拉發(fā)布無(wú)人駕駛車(chē),還未迎來(lái)“Chatgpt時(shí)刻”
- 3 特斯拉股價(jià)大跌15%:Robotaxi離落地還差一個(gè)蘿卜快跑
- 4 馬斯克給的“驚喜”夠嗎?
- 5 海信給AI電視打樣,12大AI智能體全面升級(jí)大屏體驗(yàn)
- 6 打完“價(jià)格戰(zhàn)”,大模型還要比什么?
- 7 馬斯克致敬“國(guó)產(chǎn)蘿卜”?
- 8 神經(jīng)網(wǎng)絡(luò),誰(shuí)是盈利最強(qiáng)企業(yè)?
- 9 比蘋(píng)果偉大100倍!真正改寫(xiě)人類歷史的智能產(chǎn)品降臨
- 10 諾獎(jiǎng)進(jìn)入“AI時(shí)代”,人類何去何從?
- 高級(jí)軟件工程師 廣東省/深圳市
- 自動(dòng)化高級(jí)工程師 廣東省/深圳市
- 光器件研發(fā)工程師 福建省/福州市
- 銷(xiāo)售總監(jiān)(光器件) 北京市/海淀區(qū)
- 激光器高級(jí)銷(xiāo)售經(jīng)理 上海市/虹口區(qū)
- 光器件物理工程師 北京市/海淀區(qū)
- 激光研發(fā)工程師 北京市/昌平區(qū)
- 技術(shù)專家 廣東省/江門(mén)市
- 封裝工程師 北京市/海淀區(qū)
- 結(jié)構(gòu)工程師 廣東省/深圳市