這段時間學(xué)操作系統(tǒng)圆到,好奇計(jì)算機(jī)是怎么從通電到成功加載操作系統(tǒng)的怎抛,看了一些文章順便做下總結(jié)。
第 0芽淡、1 小節(jié)介紹了一些地址和寄存器的基本概念马绝,后面介紹了 80386 從通電后,怎么把操作系統(tǒng)加載到內(nèi)存中來運(yùn)行的過程挣菲。
0. 幾個地址的概念
先來理解這幾個地址的概念:物理地址富稻、虛擬地址(線性地址)掷邦、邏輯地址。
任何時候椭赋,計(jì)算機(jī)上都存在一個程序能夠產(chǎn)生的地址集合抚岗,我們稱之為地址范圍。這個范圍的大小由 CPU 的位數(shù)決定哪怔,例如一個 32 位的 CPU苟跪,它的地址范圍是 0~0xFFFFFFFF
(4G), 而對于一個 64 位的 CPU,它的地址范圍為 0~0xFFFFFFFFFFFFFFFF
(64T)蔓涧。 這個范圍就是程序能夠產(chǎn)生的地址范圍,我們把這個地址范圍稱為虛擬地址空間笋额,該空間中的某一個地址我們稱之為虛擬地址元暴。與虛擬地址空間和虛擬地址相對應(yīng)的則是物理地址空間和物理地址,大多數(shù)時候我們的系統(tǒng)所具備的物理地址空間只是虛擬地址空間的一個子集兄猩。
舉一個最簡單的例子直觀地說明這兩者:對于一臺內(nèi)存為 256M 的 32bit X86 主機(jī)來說茉盏,它的虛擬地址空間范圍是
0~0xFFFFFFFF
(4G), 而物理地址空間范圍是0x000000000~0x0FFFFFFF
(256M)。
這里有一個虛擬內(nèi)存的概念枢冤,虛擬內(nèi)存(virtual memory)是對整個內(nèi)存(不要和機(jī)器上插那條對上號)的抽像描述鸠姨。它是相對于物理內(nèi)存來講的,能直接理解成 “不直實(shí)的”淹真,“假的” 內(nèi)存⊙惹ǎ現(xiàn)代操作系統(tǒng)都提供了一種內(nèi)存管理的抽像,即虛擬內(nèi)存(virtual memory)核蘸。進(jìn)程使用虛擬內(nèi)存中的地址巍糯,由操作系統(tǒng)協(xié)助相關(guān)硬件,把它 “轉(zhuǎn)換” 成真正的物理地址客扎。這個“轉(zhuǎn)換”祟峦,是所有問題討論的關(guān)鍵。
有了這樣的抽像徙鱼,一個程序就能使用比真實(shí)物理地址大得多的地址空間(拆東墻宅楞,補(bǔ)西墻,銀行也是這樣子做的)袱吆,甚至多個進(jìn)程能使用相同的地址厌衙。這不奇怪,因?yàn)檗D(zhuǎn)換后的物理地址并非相同的绞绒。
邏輯地址(Logical Address):是指由程序產(chǎn)生的與段相關(guān)的偏移地址部分迅箩。是在有地址變換功能的計(jì)算機(jī)中,訪內(nèi)指令給出的地址(操作數(shù))叫邏輯地址, 也叫相對地址处铛,也就是是機(jī)器語言指令中饲趋,用來指定一個操作數(shù)或是一條指令的地址拐揭。要經(jīng)過尋址方式的計(jì)算或變換才得到內(nèi)存儲器中的實(shí)際有效地址即物理地址。
一個邏輯地址由兩部分組成奕塑,段標(biāo)識符: 段內(nèi)偏移量堂污。段標(biāo)識符是由一個 16 位長的字段組成,稱為段選擇子(Segment Selector)龄砰,其中前 13 位是個索引號盟猖,后面 3 位包含其它信息 (后面有介紹)。而偏移量是一個 32 位長的字段换棚。
線性地址(Linear Address):也叫虛擬地址(Virtual Address)式镐。是邏輯地址到物理地址變換之間的中間層。在分段部件中固蚤,邏輯地址是段中的偏移地址娘汞,然后加上基地址就是線性地址。
線性地址是一個 32 位無符號整數(shù)夕玩,可以用來表示高達(dá) 4GB 的地址你弦,也就是,高達(dá) 4294967296 個內(nèi)存單元燎孟。線性地址通常用十六進(jìn)制數(shù)字表示禽作,值的范圍從
0x00000000 ~ 0xffffffff
。程序代碼會產(chǎn)生邏輯地址揩页,通過邏輯地址變換就可以生成一個線性地址旷偿。如果啟用了分頁機(jī)制,那么線性地址可以再經(jīng)過變換以產(chǎn)生一個物理地址爆侣。如果沒有啟用分頁機(jī)制狸捅,那么線性地址就是物理地址。
物理地址(Physical Address):CPU 地址總線傳來的地址累提,由硬件電路控制(現(xiàn)在這些硬件是可編程的了)其具體含義尘喝。物理地址中很大一部分是留給內(nèi)存條中的內(nèi)存的,但也常被映射到其他存儲器上(如顯存斋陪、BIOS 等)朽褪。
在沒有使用虛擬存儲器的機(jī)器上,虛擬地址被直接送到內(nèi)存總線上无虚,使具有相同地址的物理存儲器被讀寫缔赠;而在使用了虛擬存儲器的情況下,虛擬地址不是被直接送到內(nèi)存地址總線上友题,而是送到存儲器管理單元 MMU嗤堰,把虛擬地址映射為物理地址。
CPU 將一個邏輯地址轉(zhuǎn)換為物理地址度宦,需要進(jìn)行兩步:
- 將給定的邏輯地址(其實(shí)是段內(nèi)偏移量踢匣,這個一定要理解8娼场),CPU 內(nèi)存管理單元(MMU)利用其段式內(nèi)存管理單元(segmentation unit)离唬,先將邏輯地址轉(zhuǎn)換成一個線性地址后专。
- 利用其頁式內(nèi)存管理單元(paging unit),把線性地址轉(zhuǎn)換為物理地址输莺。
這樣做兩次轉(zhuǎn)換戚哎,的確是非常麻煩而且沒有必要的,因?yàn)橹苯涌梢园丫€性地址抽象給進(jìn)程嫂用。之所以這樣冗余型凳,Intel 完全是為了兼容而已(Intel 為了兼容,將遠(yuǎn)古時代的段式內(nèi)存管理方式保留了下來嘱函,x86 體系的處理器剛開始時只有 20 根地址線甘畅,尋址寄存器是 16 位。我們知道 16 位的寄存器可以訪問 64K 的地址空間实夹,如果程序要想訪問大于 64K 的內(nèi)存,就需要把內(nèi)存分段粒梦,每段 64K亮航,用段地址+偏移量的方式來訪問,這樣使 20 根地址線全用上匀们,最大的尋址空間就可以到 1M 字節(jié)缴淋,這在當(dāng)時已經(jīng)是非常大的內(nèi)存空間了。
現(xiàn)代的多用戶多進(jìn)程操作系統(tǒng)泄朴,需要 MMU 才能達(dá)到每個用戶進(jìn)程都擁有自己獨(dú)立的地址空間的目標(biāo)重抖。使用 MMU,操作系統(tǒng)劃分出一段地址區(qū)域祖灰,在這塊地址區(qū)域中, 每個進(jìn)程看到的內(nèi)容都不一定一樣钟沛。例如 Windows 操作系統(tǒng)將地址范圍 4M-2G 劃分為用戶地址空間,進(jìn)程 A 在地址 0X400000(4M)映射了可執(zhí)行文件局扶,進(jìn)程 B 同樣在地址 0X400000(4M)映射了可執(zhí)行文件恨统,如果 A 進(jìn)程讀地址 0X400000,讀到的是 A 的可執(zhí)行文件映射到 RAM 的內(nèi)容三妈,而進(jìn)程 B 讀取地址 0X400000 時畜埋,讀到的則是 B 的可執(zhí)行文件映射到 RAM 的內(nèi)容。這就是 MMU 在當(dāng)中進(jìn)行地址轉(zhuǎn)換所起的作用畴蒲。
1. X86 寄存器說明
32 位 CPU 所含有的寄存器有:
- 4 個數(shù)據(jù)寄存器:EAX悠鞍、EBX、ECX 和 EDX模燥。
- 2 個變址和指針寄存器:ESI 和 EDI咖祭。
- 2 個指針寄存器:ESP 和 EBP掩宜。
- 6 個段寄存器:ES、CS心肪、SS锭亏、DS、FS 和 GS硬鞍。
- 1 個指令指針寄存器:EIP慧瘤。
- 1 個標(biāo)志寄存器:EFlags。
1.1 數(shù)據(jù)寄存器
數(shù)據(jù)寄存器主要用來保存操作數(shù)和運(yùn)算結(jié)果等信息固该,從而節(jié)省讀取操作數(shù)所需占用總線和訪問存儲器的時間锅减。
32 位 CPU 有 4 個 32 位的通用寄存器 EAX、EBX伐坏、ECX 和 EDX怔匣。對低 16 位數(shù)據(jù)的存取,不會影響高 16 位的數(shù)據(jù)桦沉。這些低 16 位寄存器分別命名為:AX每瞒、BX、CX 和 DX纯露,它和先前的 16 位 CPU 中的寄存器相一致剿骨。
4 個 16 位寄存器又可分割成 8 個獨(dú)立的 8 位寄存器(AX:AH-AL、BX:BH-BL埠褪、CX:CH-CL浓利、DX:DH-DL),每個寄存器都有自己的名稱钞速,可獨(dú)立存取贷掖。程序員可利用數(shù)據(jù)寄存器的這種“可分可合”的特性,靈活地處理字/字節(jié)的信息渴语。
- 寄存器 AX 和 AL 通常稱為累加器(Accumulator)苹威,用累加器進(jìn)行的操作可能需要更少時間。累加器可用于乘驾凶、除屠升、輸入/輸出等操作,它們的使用頻率很高狭郑。
- 寄存器 BX 稱為基地址寄存器(BaseRegister)腹暖。它可作為存儲器指針來使用。
- 寄存器 CX 稱為計(jì)數(shù)寄存器(CountRegister)翰萨。在循環(huán)和字符串操作時脏答,要用它來控制循環(huán)次數(shù);在位操作中,當(dāng)移多位時殖告,要用 CL 來指明移位的位數(shù)阿蝶。
- 寄存器 DX 稱為數(shù)據(jù)寄存器(DataRegister)。在進(jìn)行乘黄绩、除運(yùn)算時羡洁,它可作為默認(rèn)的操作數(shù)參與運(yùn)算,也可用于存放 I/O 的端口地址爽丹。
在 16 位 CPU 中筑煮,AX、BX粤蝎、CX 和 DX 不能作為基址和變址寄存器來存放存儲單元的地址真仲,但在 32 位 CPU 中,其 32 位寄存器 EAX初澎、EBX掖肋、ECX 和 EDX 不僅可傳送數(shù)據(jù)虫埂、暫存數(shù)據(jù)保存算術(shù)邏輯運(yùn)算結(jié)果,而且可作為指針寄存器衅鹿,所以窖铡,這些 32 位寄存器更具有通用性齐唆。
1.2 變址寄存器
32 位 CPU 有 2 個 32 位通用寄存器 ESI 和 EDI巫击。其低 16 位對應(yīng)先前 16 位 CPU 中的 SI 和 DI七蜘,對低 16 位數(shù)據(jù)的存取,不影響高 16 位的數(shù)據(jù)捕仔。
寄存器 ESI匕积、EDI盈罐、SI 和 DI 稱為變址寄存器(IndexRegister)榜跌,它們主要用于存放存儲單元在段內(nèi)的偏移量,用它們可實(shí)現(xiàn)多種存儲器操作數(shù)的尋址方式盅粪,為以不同的地址形式訪問存儲單元提供方便钓葫。
變址寄存器不可分割成 8 位寄存器。作為通用寄存器票顾,也可存儲算術(shù)邏輯運(yùn)算的操作數(shù)和運(yùn)算結(jié)果础浮。
它們可作一般的存儲器指針使用。在字符串操作指令的執(zhí)行過程中奠骄,對它們有特定的要求豆同,而且還具有特殊的功能。
1.3 指針寄存器
32 位 CPU 有 2 個 32 位通用寄存器 EBP 和 ESP含鳞。其低 16 位對應(yīng)先前 16 位 CPU 中的 BP 和 SP影锈,對低 16 位數(shù)據(jù)的存取,不影響高 16 位的數(shù)據(jù)。
寄存器 EBP鸭廷、ESP枣抱、BP 和 SP 稱為指針寄存器(PointerRegister),主要用于存放堆棧內(nèi)存儲單元的偏移量辆床,用它們可實(shí)現(xiàn)多種存儲器操作數(shù)的尋址方式佳晶,為以不同的地址形式訪問存儲單元提供方便。
指針寄存器不可分割成 8 位寄存器讼载。作為通用寄存器轿秧,也可存儲算術(shù)邏輯運(yùn)算的操作數(shù)和運(yùn)算結(jié)果。
它們主要用于訪問堆棧內(nèi)的存儲單元维雇,并且規(guī)定:
- BP 為基指針(BasePointer)寄存器淤刃,用它可直接存取堆棧中的數(shù)據(jù)。
- SP 為堆棧指針(StackPointer)寄存器吱型,用它只可訪問棧頂逸贾。
1.4 段寄存器
在 32 位 CPU 中,有 6 個 16 位的段寄存器津滞,所以铝侵,在此環(huán)境下開發(fā)的程序最多可同時訪問 6 個段。
段寄存器是根據(jù)內(nèi)存分段的管理模式而設(shè)置的触徐。內(nèi)存單元的物理地址是由段寄存器的值和一個偏移量組合而成
的咪鲜,這樣可用兩個較少位數(shù)的值組合成一個可訪問較大物理空間的內(nèi)存地址。
CPU 內(nèi)部的段寄存器為:
CS(Code Segment):代碼段寄存器撞鹉,其值為代碼段的段值疟丙。
DS(Data Segment):數(shù)據(jù)段寄存器,其值為數(shù)據(jù)段的段值鸟雏。
SS(Stack Segment):堆棧段寄存器享郊,其值為堆棧段的段值。
ES(Extra Segment):附加段寄存器孝鹊,其值為附加數(shù)據(jù)段的段值炊琉。
FS(Extra Segment):附加段寄存器,其值為附加數(shù)據(jù)段的段值又活。
GS(Extra Segment):附加段寄存器苔咪,其值為附加數(shù)據(jù)段的段值。
盡管只有 6 個段寄存器柳骄,但程序可以把同一個段寄存器用于不同地目的团赏,這 6 個段寄存器中的 3 個有專門的用途:
- CS:代碼段寄存器,指向包含程序指令的段耐薯。
- SS:棧段寄存器舔清,指向包含當(dāng)前程序棧的段隘世。
- DS:數(shù)據(jù)段寄存器,指向包含靜態(tài)數(shù)據(jù)或者全局?jǐn)?shù)據(jù)段鸠踪。
其它 3 個段寄存器用作一般用途丙者,可以指向任意的數(shù)據(jù)段。
1.4.1 實(shí)模式與保護(hù)模式簡述
32 位 CPU 有兩個不同的工作模式:實(shí)模式和保護(hù)模式营密。實(shí)模式和保護(hù)模式都是 CPU 的工作模式械媒,而 CPU 的工作模式是指 CPU 的尋址方式、寄存器大小等用來反應(yīng) CPU 在該環(huán)境下如何工作的概念评汰。
在這兩種模式下纷捞,段寄存器的作用是不同的。有關(guān)規(guī)定簡單描述如下:
實(shí)模式: 前 4 個段寄存器 CS被去、DS主儡、SS 和 ES 與先前 CPU 中的所對應(yīng)的段寄存器的含義完全一致,內(nèi)存單元的邏輯地址仍為
段基址:段偏移量
的形式惨缆。為訪問某內(nèi)存段內(nèi)的數(shù)據(jù)糜值,必須使用該段寄存器和存儲單元的偏移量。保護(hù)模式: 在此模式下坯墨,情況要復(fù)雜得多寂汇,裝入段寄存器的不再是段值,而是被稱為“段選擇子”(Segment Selector)的某個值捣染,下面 1.4.3 小節(jié)會介紹骄瓣。
1.4.2 實(shí)模式原理
實(shí)模式出現(xiàn)于早期 8086 和 8088 CPU 時期。當(dāng)時由于 CPU 的性能有限耍攘,一共只有 20 位地址線 A19 ~ A0榕栏,尋址 1MB 的存儲空間,其物理地址范圍為 00000H ~ FFFFFH
(即 2^20 = 1048576 Byte蕾各,所以地址空間只有 1MB)扒磁。由于復(fù)位后首先從地址高端的 FFFFFH 開始執(zhí)行指令,所以將地址高端設(shè)置位 ROM 空間示损,而低端作為 RAM 空間渗磅。
80386 的實(shí)模式是為了與 8086 處理器兼容而設(shè)置的嚷硫,在實(shí)模式下检访,80386 處理器就相當(dāng)于一個快速的 8086 處理器。80386 處理器被復(fù)位或加電的時候以實(shí)模式啟動仔掸,這時候處理器中的各寄存器以實(shí)模式的初始值工作脆贵。
此時 80386 的 32 位地址線只使用了低 20 位,即可訪問 1MB 的物理地址空間起暮。在實(shí)模式下卖氨,80386 處理器不能對內(nèi)存進(jìn)行分頁機(jī)制的管理,所以指令尋址的地址就是內(nèi)存中實(shí)際的物理地址。在實(shí)模式下筒捺,所有的段都是可以讀柏腻、寫和執(zhí)行的。實(shí)模式下 80386 不支持優(yōu)先級系吭,所有的指令相當(dāng)于工作在特權(quán)級(即優(yōu)先級 0)五嫂,所以它可以執(zhí)行所有特權(quán)指令,包括讀寫控制寄存器 CR0 等肯尺。這實(shí)際上使得在實(shí)模式下不太可能設(shè)計(jì)一個有保護(hù)能力的操作系統(tǒng)沃缘。
它有 8 個 16 位的通用寄存器,以及 4 個 16 位的段寄存器则吟。所以為了能夠通過這些 16 位的寄存器去構(gòu)成 20 位的主存地址槐臀,必須采取一種特殊的方式。當(dāng)某個指令想要訪問某個內(nèi)存地址時氓仲,它通常需要用下面的這種格式來表示:
段基址:段偏移量
其中第一個字段是段基址水慨,它的值是由段寄存器提供的(一般來說,段寄存器有 6 種敬扛,上面有介紹)讥巡。
第二個字段是段內(nèi)偏移量,代表要訪問的這個內(nèi)存地址距離這個段基址的偏移舔哪。它的值就是由通用寄存器來提供的欢顷,所以也是 16 位。那么兩個 16 位的值如何組合成一個 20 位的地址呢捉蚤?CPU 采用的方式是把段寄存器所提供的段基址先向左移4位抬驴。這樣就變成了一個 20 位的值,然后再與段偏移量相加缆巧。即:
物理地址 = 段基址<<4 + 段內(nèi)偏移
所以假設(shè)段寄存器中的值是 0xff00
布持,段偏移量為 0x0110
。則這個地址對應(yīng)的真實(shí)物理地址是:0xff00<<4 + 0x0110 = 0xff110
陕悬。
由上面的介紹可見题暖,實(shí)模式的“實(shí)”更多地體現(xiàn)在其地址是真實(shí)的物理地址。
1.4.3 保護(hù)模式原理
隨著 CPU 的發(fā)展捉超,CPU 地址線的個數(shù)也從原來的 20 根變?yōu)楝F(xiàn)在的 32 根胧卤,可以訪問的內(nèi)存空間也從 1MB 變?yōu)楝F(xiàn)在4GB,寄存器的位數(shù)也變?yōu)?32 位拼岳。所以實(shí)模式下的內(nèi)存地址計(jì)算方式就已經(jīng)不再適合了枝誊,因此就引入了現(xiàn)在的保護(hù)模式,實(shí)現(xiàn)更大空間的惜纸、更靈活也更安全的內(nèi)存訪問叶撒。
簡單地說绝骚,通過保護(hù)模式,可以把虛擬地址空間映射到不同的物理地址空間祠够,且在超出預(yù)設(shè)的空間范圍會報錯(一種保護(hù)機(jī)制的體現(xiàn))压汪,且可以保證處于低特權(quán)級的代碼無法訪問高特權(quán)級的數(shù)據(jù)(另外一種保護(hù)機(jī)制的體現(xiàn))。
在保護(hù)模式下古瓤,80386 的 32 條地址線全部有效蛾魄,但是內(nèi)存尋址方式還是得兼容老辦法,即(段基址:段偏移量)的表示方式湿滓,這使得其:
- 可尋址高達(dá) 4GB 的線性地址空間和物理地址空間滴须。此時 CPU 中的通用寄存器都要換成 32 位寄存器(除了段寄存器)來保證寄存器能訪問所有的 4GB 空間。
- 可訪問 64TB(有 2^14 個段叽奥,每個段最大空間為 2^32 字節(jié))的虛擬地址空間扔水。
- 可采用分段存儲管理機(jī)制和分頁存儲管理機(jī)制。
保護(hù)模式下的偏移值和實(shí)模式下是一樣的朝氓,就是變成了 32 位而已魔市。段值仍舊是存放在原來 16 位的段寄存器中,但是這些段寄存器存放的卻不再是段基址了赵哲。之前說過實(shí)模式下尋址方式不安全待德,在保護(hù)模式下需要加一些限制,而這些限制不是一個寄存器能夠容納的枫夺,于是把這些關(guān)于內(nèi)存段的限制信息放在一個叫做全局描述符表(GDT:Global Descriptor Table)的結(jié)構(gòu)里将宪。全局描述符表中含有一個個表項(xiàng),每一個表項(xiàng)稱為段描述符橡庞。在保護(hù)模式下较坛,段寄存器存放的便是相當(dāng)于一個數(shù)組索引的東西,即段選擇子(Segment Selector)扒最,通過這個索引丑勤,可以找到對應(yīng)的表項(xiàng)。
在保護(hù)模式下吧趣,每個內(nèi)存段就是一個段描述符法竞。段描述符存放了段基址(Base)、段界限(Limit)强挫、內(nèi)存段類型屬性(比如是數(shù)據(jù)段還是代碼段岔霸,注意一個段描述符只能用來定義一個內(nèi)存段)等許多屬性,具體信息見下圖:
其中,段界限表示段邊界的擴(kuò)張最值纠拔,即最大擴(kuò)展多少或最小擴(kuò)展多少秉剑,用 20 位來表示泛豪,它的單位可以是字節(jié)稠诲,也可以是 4KB侦鹏,這是由 G 位決定的(G 為 1 時表示單位為 4KB)。
實(shí)際段界限邊界值 =
(描述符中的段界限+1)*(段界限的單位大型涡稹(即字節(jié)或 4KB))-1
略水,如果偏移地址超過了段界限,CPU 會拋出異常劝萤。
此外渊涝, 擴(kuò)充的存儲器分段管理機(jī)制和可選的存儲器分頁管理機(jī)制床嫌,有如下好處:
- 不僅為存儲器共享和保護(hù)提供了硬件支持跨释,而且為實(shí)現(xiàn)虛擬存儲器提供了硬件支持。
- 支持多任務(wù)厌处,能夠快速地進(jìn)行任務(wù)切換(switch)和保護(hù)任務(wù)環(huán)境(context)鳖谈。
- 4 個特權(quán)級和完善的特權(quán)檢查機(jī)制,既能實(shí)現(xiàn)資源共享又能保證代碼和數(shù)據(jù)的安全和保密及任務(wù)的隔離阔涉。
- 支持虛擬 8086 方式缆娃,便于執(zhí)行 8086 程序。
1.4.4 延伸知識:段選擇子瑰排、特權(quán)級細(xì)節(jié)
這一小節(jié)會對段選擇子及特權(quán)級的細(xì)節(jié)進(jìn)行總結(jié)贯要。
特權(quán)級
- 通過提供 4 個特權(quán)級和完善的特權(quán)檢查機(jī)制,既能實(shí)現(xiàn)資源共享又能保證代碼和數(shù)據(jù)的安全和保密及任務(wù)的隔離椭住。
- 在保護(hù)模式下崇渗,特權(quán)級總共有 4 個,編號從 0(最高特權(quán))到 3(最低特權(quán))京郑。有 3 種主要的資源受到保護(hù):內(nèi)存显押,I/O 地址空間以及執(zhí)行特殊機(jī)器指令的能力。Linux 只用 0 級和 3 級傻挂,分別稱之為內(nèi)核態(tài)和用戶態(tài)乘碑。
- 在任一時刻,80386 CPU 都是在一個特定的特權(quán)級下運(yùn)行的金拒,從而決定了代碼可以做什么兽肤,不可以做什么。這些特權(quán)級經(jīng)常被稱為為保護(hù)環(huán)(protection ring)绪抛,最內(nèi)的環(huán)(ring 0)對應(yīng)于最高特權(quán) 0资铡,最外面的環(huán)(ring 3)一般給應(yīng)用程序使用,對應(yīng)最低特權(quán) 3幢码。
- 在保護(hù)模式下笤休,可以通過查看 CS 寄存器的低 2 位來了解進(jìn)程的當(dāng)前特權(quán)級(CPL)。
CPL症副、RPL 與 DPL
CPL 是當(dāng)前進(jìn)程的權(quán)限級別(Current Privilege Level)店雅,是當(dāng)前正在執(zhí)行的代碼所在的段的特權(quán)級政基,存在于 CS 寄存器的低 2 位。
RPL 說明的是進(jìn)程對段訪問的請求權(quán)限(Request Privilege Level)闹啦,它存在于段選擇子的低 2 位沮明,每個段選擇子有自己的 RPL。它說明的是進(jìn)程對段訪問的請求權(quán)限窍奋,有點(diǎn)像函數(shù)參數(shù)荐健。而且 RPL 對每個段來說不是固定的,兩次訪問同一段時的 RPL 可以不同琳袄。RPL 可能會削弱 CPL 的作用江场,例如當(dāng)前 CPL=0 的進(jìn)程要訪問一個數(shù)據(jù)段,它把段選擇子中的 RPL 設(shè)為 3窖逗,這樣它對該段只有特權(quán)為 3 的訪問權(quán)限扛稽。
RPL 引入的目的是避免低特權(quán)級的程序訪問高特權(quán)級的資源。
DPL 存儲在段描述符中滑负,規(guī)定訪問該段所需的權(quán)限級別(Descriptor Privilege Level)在张,每個段的 DPL 是固定的。
當(dāng)進(jìn)程訪問一個段時矮慕,即處理器將段選擇子載入段寄存器之前帮匾,要進(jìn)行特權(quán)級檢查,比較當(dāng)前運(yùn)行的進(jìn)程或任務(wù)的特權(quán)級(CPL)痴鳄,段選擇子的 RPL瘟斜,還有該段的段描述符的 DPL。一般要求在數(shù)值上:
CPL <= DPL && RPL <= DPL
(注意痪寻,數(shù)字越大特權(quán)級越低)螺句,滿足此條件時處理器會將段選擇子裝載入段寄存器,否則不會裝載到段寄存器橡类。
段選擇子的存儲布局
- Index:段寄存器的高 13 位存放的就是 GDT 的 Index蛇尚。
- Table Indicator(TI):Index 后面的 1 位,用來指示描述符表的類別顾画,0 表示 GDT取劫,1 表示 LDT(Local Descriptor Table)。
- Requested Privilege Level(RPL):低 2 位存放的是進(jìn)程對段訪問的請求特權(quán)級研侣。
段選擇子(Segment Selector)
它是這樣定義的:
A segment selector is a 16-bit identifier for a segment . It does not point directly to the segment, but instead points to the segment descriptor that defines the segment.
也就是前面說的谱邪,段選擇子是一個 16 位的段標(biāo)識符,它不直接指向段庶诡,而是指向 GDT 中定義該段的段描述符惦银。此外還需要理解:
- 處理器一共提供了 6 個段寄存器來保存段選擇子。
- 當(dāng)一個進(jìn)程要訪問某個段的時候,這個段的段選擇子必須被賦值到某一個段寄存器中扯俱。因此书蚪,盡管系統(tǒng)定義了數(shù)千個段,只有 6 個段是可以被直接使用的蘸吓。其他段只有在它們的段選擇子被置入這些寄存器中時才可以被使用善炫。
- 對任何程序的執(zhí)行而言撩幽,至少要將代碼段寄存器(CS)库继,數(shù)據(jù)段寄存器(DS)和堆棧段寄存器(SS)賦予有效的段選擇子。此外窜醉,處理器還提供了另外 3 個數(shù)據(jù)段寄存器(ES宪萄,F(xiàn)S 和 GS)供進(jìn)程使用。
也就是說:
- 在同一時刻程序中可以有多個段選擇子榨惰,也就是可以有多個 RPL拜英,然而只有 CS 寄存器(也就是存放正在執(zhí)行的代碼的寄存器)中的 RPL才等于 CPL。
CS 段寄存器指向的是 CPU 中當(dāng)前運(yùn)行的指令琅催,所以 CS 的 RPL 位稱為當(dāng)前特權(quán)級 CPL居凶。所以得出結(jié)論:CS.RPL 的值 = CPL
的值。
1.5 指令指針寄存器
32 位 CPU 把指令指針擴(kuò)展到 32 位藤抡,并記作 EIP侠碧,EIP 的低 16 位與先前 16 位 CPU 中的 IP 作用相同。
指令指針 EIP缠黍、IP(InstructionPointer)是存放下次將要執(zhí)行的指令在代碼段的偏移量弄兜。在具有預(yù)取指令功能的系統(tǒng)中,下次要執(zhí)行的指令通常已被預(yù)取到指令隊(duì)列中瓷式,除非發(fā)生轉(zhuǎn)移情況替饿。所以,在理解它們的功能時贸典,不考慮存在指令隊(duì)列的情況视卢。
在實(shí)模式下,由于每個段的最大范圍為 64K廊驼,所以腾夯,EIP 中的高 16 位肯定都為 0,此時蔬充,相當(dāng)于只用其低 16 位的 IP 來反映程序中指令的執(zhí)行次序蝶俱。
1.6 標(biāo)志寄存器
以下是運(yùn)算結(jié)果標(biāo)志位。
1.6.1 進(jìn)位標(biāo)志 CF(CarryFlag)
進(jìn)位標(biāo)志 CF 主要用來反映運(yùn)算是否產(chǎn)生進(jìn)位或借位饥漫。如果運(yùn)算結(jié)果的最高位產(chǎn)生了一個進(jìn)位或借位榨呆,那么,其值為 1庸队,否則其值為 0积蜻。
使用該標(biāo)志位的情況有:多字(字節(jié))數(shù)的加減運(yùn)算闯割,無符號數(shù)的大小比較運(yùn)算,移位操作竿拆,字(字節(jié))之間移位宙拉,專門改變 CF 值的指令等。
1.6.2 奇偶標(biāo)志 PF(ParityFlag)
奇偶標(biāo)志 PF 用于反映運(yùn)算結(jié)果中“1”的個數(shù)的奇偶性丙笋。如果“1”的個數(shù)為偶數(shù)谢澈,則 PF 的值為 1,否則其值為 0御板。
利用 PF 可進(jìn)行奇偶校驗(yàn)檢查锥忿,或產(chǎn)生奇偶校驗(yàn)位。在數(shù)據(jù)傳送過程中怠肋,為了提供傳送的可靠性敬鬓,如果采用奇偶校驗(yàn)的方法,就可使用該標(biāo)志位笙各。
1.6.3 輔助進(jìn)位標(biāo)志 AF(AuxiliaryCarryFlag)
在發(fā)生下列情況時钉答,輔助進(jìn)位標(biāo)志 AF 的值被置為 1,否則其值為 0:
- 在字操作時杈抢,發(fā)生低字節(jié)向高字節(jié)進(jìn)位或借位時数尿。
- 在字節(jié)操作時,發(fā)生低 4 位向高 4 位進(jìn)位或借位時春感。
1.6.4 零標(biāo)志 ZF(ZeroFlag)
零標(biāo)志 ZF 用來反映運(yùn)算結(jié)果是否為 0砌创。如果運(yùn)算結(jié)果為 0,則其值為 1鲫懒,否則其值為 0嫩实。在判斷運(yùn)算結(jié)果是否為 0 時,可使用此標(biāo)志位窥岩。
16.5 符號標(biāo)志 SF(SignFlag)
符號標(biāo)志 SF 用來反映運(yùn)算結(jié)果的符號位甲献,它與運(yùn)算結(jié)果的最高位相同。在微機(jī)系統(tǒng)中颂翼,有符號數(shù)采用補(bǔ)碼表示法晃洒,所以,SF 也就反映運(yùn)算結(jié)果的正負(fù)號朦乏。運(yùn)算結(jié)果為正數(shù)時球及,SF 的值為 0,否則其值為 1呻疹。
1.6.6 溢出標(biāo)志 OF(OverflowFlag)
溢出標(biāo)志 OF 用于反映有符號數(shù)加減運(yùn)算所得結(jié)果是否溢出吃引。如果運(yùn)算結(jié)果超過當(dāng)前運(yùn)算位數(shù)所能表示的范圍,則稱為溢出,OF 的值被置為 1镊尺,否則朦佩,OF 的值被清為 0。
對以上 6 個運(yùn)算結(jié)果標(biāo)志位庐氮,在一般編程情況下语稠,標(biāo)志位 CF、ZF弄砍、SF 和 OF 的使用頻率較高仙畦,而標(biāo)志位 PF 和 AF 的使用頻率較低。
2. X86 平臺啟動過程
2.1 讀取第一條指令
在計(jì)算機(jī)通電后输枯,寄存器在初始狀態(tài)下會有一個缺省值议泵。
CS(16 位的段寄存器的基址) 和 EIP(段內(nèi)偏移) 結(jié)合在一起形成了啟動后的第一條地址占贫,CPU 會從該地址獲取指令來執(zhí)行桃熄。
上面有介紹,X86 為了向下兼容 8086型奥,剛啟動時是處于 16 位的實(shí)模式瞳收,會按照實(shí)模式的尋址方式來尋址:
CS: IP 的值:
: IP
表示 CS 左移了 4 位再加上 IP 地址。CS:16 位
EIP:16 位
所以第一條地址的實(shí)際值如下:
CS = F000H厢汹,EIP = 0000FFF0H
實(shí)際地址是:
Base + EIP = FFFF0000H + 0000FFF0H = FFFFFFF0H
螟深,這就是 BIOS 的 EPROM(Erasable Programmable Read Only Memory)所在地址。當(dāng) CS 被新值加載烫葬,地址轉(zhuǎn)換規(guī)則將開始起作用界弧。
通常第一條指令是一條長跳轉(zhuǎn)指令(這樣 CS 和 EIP 都會更新),會跳轉(zhuǎn)到 BIOS 代碼中做初始化工作搭综。
2.2 BIOS 所做的工作
BIOS 主要做一些硬件的初始化工作垢箕,完成一些關(guān)鍵硬件的自檢,隨后:
- BIOS 會加載存儲設(shè)備的第一個扇區(qū)(Master Boot Record 即主引導(dǎo)扇區(qū)兑巾,簡寫為 MBR)中的的 512 字節(jié)內(nèi)容讀取到內(nèi)存的固定地址
0x7c00
處条获。- 然后 IP 寄存器地址會跳轉(zhuǎn)到
0x7c00
這個地址,使得 CPU 可以執(zhí)行 MBR 中的的代碼蒋歌,即 Bootloader 的代碼帅掘。- Bootloader 會完成操作系統(tǒng)的進(jìn)一步加載。
2.3 Bootloader 所做的工作
Bootloader 所做的工作堂油,簡單說就是進(jìn)入保護(hù)模式修档,加載操作系統(tǒng)到內(nèi)存,如下:
- 從16 位尋址空間的實(shí)模式切換到32 位尋址空間的保護(hù)模式(即從 1MB 到 4GB)府框,為后續(xù)操作系統(tǒng)的執(zhí)行做準(zhǔn)備吱窝,同時段機(jī)制(Segment-Level Protection)也被加載。
- 從硬盤中 MBR 后面的扇區(qū)讀取操作系統(tǒng) kernel 代碼,加載到內(nèi)存中固定位置癣诱。
- 根據(jù) CS 寄存器中 Index 值來找到操作系統(tǒng)所在內(nèi)存的起始地址计维,即操作系統(tǒng)的入口點(diǎn)(entry point),來把控制權(quán)交給了操作系統(tǒng)撕予。
這樣 Bootloader 就完成了操作系統(tǒng)的加載工作鲫惶。
當(dāng)然這還沒完,僅僅是把操作系統(tǒng)加載到內(nèi)存中实抡,接下來終于要輪到操作系統(tǒng)登場了欠母。
在總結(jié)完后看到條 2017 年的新聞:Intel 已經(jīng)決心在 2020 年之前,徹底淘汰 PC BIOS吆寨,全面向 UEFI 固件過渡赏淌,而這也意味著一個時代的終結(jié)。
繼任者叫 UEFI啄清。