- 論壇徽章:
- 13
|
本帖最后由 _nosay 于 2016-09-15 00:53 編輯
Intel CPU.png (5.84 KB, 下載次數(shù): 24)
下載附件
2016-09-14 22:10 上傳
上圖為Intel CPU的發(fā)展過程,學(xué)過《微機原理》這本書的朋友,應(yīng)該對8086這塊CPU比較熟悉,它是16位的,即邏輯運算單元(ALU)每次可以計算16位數(shù)據(jù),而數(shù)據(jù)總線卻為20位。C語言中的基本數(shù)據(jù)類型大小(char 8bit, short 16bit, int 32bit, long 32/64bit)是依據(jù)ALU位數(shù)設(shè)計的,而不是數(shù)據(jù)總線位數(shù),所以說在任何一門計算機語言里,看不到20bit的基本數(shù)據(jù)類型,所以運行8086 CPU上的程序,如果用兩個16bit變量表示地址,則每個地址都會浪費12bit,Intel那幫無孔不入的天才,怎會容忍的了,所以誕生了段寄存器。
題外話,和內(nèi)核學(xué)習(xí)無關(guān):提到寄存器,不禁想起匯編語言對于段寄存器賦值的一些限制,比如不能給CS段寄存器賦值,但可以通過call,ret或jmp指令間接的達(dá)到修改CS的目的,DS、SS、ES也不能直接賦立即數(shù),而是要通過通用寄存器賦值(就是因為一些“沒有理由的限制”,讓我一直學(xué)不進匯編過,后來學(xué)習(xí)opcode,即機器碼格式規(guī)范,摸清了一部分理由,比如不同的匯編指令會得到同一條opcode,那只好在匯編器里限制其中一條屬于不合法指令)。
等發(fā)展到80386的時候,已經(jīng)完全支持保護模式(即實模式<->保護模式可以雙向切換,而80286只支持從實模式->保護模式切換),而且它的ALU和數(shù)據(jù)總線都是32位,一個地址正好可以由一個32bit變量表示了,按道理80386 CPU里面不再需要有段寄存器這種東西了。然而,比如一個i386之前的電腦用戶,發(fā)現(xiàn)換個80386的CPU后,之前用習(xí)慣的一些軟件都用不了了,然后壞事傳千里,80386 CPU還能賣的出去嗎?所以Intel攻城獅們決定留下段寄存器,但也不是白留,還是要盡量“榨取”,所以在段式內(nèi)存管理的基礎(chǔ)上實現(xiàn)保護模式(下面即將學(xué)習(xí))。
實模式<->保護模式“切換”,包括今后將會學(xué)習(xí)的用戶態(tài)<->內(nèi)核態(tài)“切換”,切換的本質(zhì)是什么?
不妨先想想大自然現(xiàn)象的變化,白天黑夜在切換,一年四季在切換,其實就是太陽與地球位置關(guān)系的一個變化,換到計算機的世界里,其實就是一些硬件狀態(tài)的變化。我曾經(jīng)問過老師,為什么在C代碼里寫個if,它就表示“如果”?不都是C語言寫的程序么,為什么別人寫的就是內(nèi)核代碼,權(quán)限很高,想干嘛干嘛,而我寫的就不可以?其實都反應(yīng)在硬件的狀態(tài)上:編譯器將if編譯成01串,電路按照這個01串加上高低電平,它就成為一個“判斷電路”(要了解數(shù)字電路等電子工程的知識);內(nèi)核代碼由BIOS啟動,而用戶程序由內(nèi)核啟動,內(nèi)核優(yōu)先拿到權(quán)限,并且限制了用戶程序的權(quán)限,所以內(nèi)核與用戶程序的區(qū)別,本質(zhì)不是在指令上,而是它們執(zhí)行時硬件的狀態(tài)不一樣,比如如果用內(nèi)核代碼編譯出來的二進制代碼,能在shell里啟動,那它就是一個用戶進程。
硬件狀態(tài)具體是指哪些硬件呢?
主要是一些寄存器,比如標(biāo)志寄存器、段寄存器。比如保護模式時,段寄存器的含義就不再像實模式時單純表示段基址,就有3bit用于表示狀態(tài):
segment register in protect mode.png (40.22 KB, 下載次數(shù): 21)
下載附件
2016-09-14 23:47 上傳
這里順便也看到段寄存器確實不只是為實模式保留,在保護模式下并沒有閑著沒用,還是被利用的滿滿的:3~15bit表示一個全局/局部描述符表下標(biāo),TI位表示使用全局還是局部描述符表,RPL表示當(dāng)前進程處于用戶態(tài)還是內(nèi)核態(tài)(不知道有沒有朋友對于Linux內(nèi)核存在這樣的錯覺:內(nèi)核代碼是由一個權(quán)限最高的進程獨立執(zhí)行。其實層次式內(nèi)核,確實是這樣,但Linux是宏內(nèi)核,內(nèi)核指令相當(dāng)于一塊公共區(qū)域,可以在用戶進程的上下文環(huán)境中執(zhí)行,只不過用戶必須通過系統(tǒng)調(diào)用/陷阱/中斷進入內(nèi)核態(tài),就像銀行,普通人不能隨意進出,但是你的錢可以合理的進出,另外關(guān)于用戶態(tài)與內(nèi)核態(tài)的切換,上述也提到了,本質(zhì)就是硬件狀態(tài)的改變,詳見《Linux內(nèi)核代碼情景分析》第三章內(nèi)容)。
雖然80386 CPU的地址可以用單獨一個變量表示了,但那樣為僅僅能表示段的起始地址,而保護模式,需要關(guān)于段的更多信息,比如訪問這個段最低要求什么權(quán)限、是否允許讀/寫等,所以出現(xiàn)了段描述符結(jié)構(gòu):
segment descriptor.png (30.36 KB, 下載次數(shù): 26)
下載附件
2016-09-15 00:22 上傳
其中B31-B24、B23-B16、B15-B0合起來32位表示段基址,其它的即為保護模式下要求的額外信息,至少為什么結(jié)構(gòu)設(shè)計這么奇怪,之前提到過:Intel發(fā)展過程中對兼容性的考慮。
上述說明過,段寄存器3~15bit表示一個全局/局部描述符表下標(biāo),那么硬件在完成地址映射的過程中,去哪找全局/局部描述符表呢?
為此,80386 CPU增加了兩個寄存器:GDTR、LDTR,分別指向全局描述符表、局部描述符表。并且由于是新增的,舊版本的CPU沒有這兩個寄存器,所以80386又將訪問/修改這兩個寄存器的指令設(shè)計為“特權(quán)指令”,從而為內(nèi)核態(tài)與用戶態(tài)的分離,做出了基礎(chǔ)保障。
實際上,Linux內(nèi)核采用的是頁式內(nèi)存管理,只不過80386 CPU的設(shè)計,決定了MMU進行地址映射時必須得經(jīng)過段式映射,所以Linux內(nèi)核也不得不設(shè)置一個描述符表,保證硬件這步操作通過,但內(nèi)核根本就“不稀罕”能從段式管理過程中得到什么好處,比如它將描述符中的段基址設(shè)置為0,長度設(shè)置為4G,也不指望依靠p標(biāo)志位對段進行虛存管理(交換分區(qū)技術(shù):將暫時不用的內(nèi)存換出到磁盤,需要時再換入,不要與虛擬地址管理混淆),也幾乎不用局部描述符表,所以就不詳細(xì)分析段式管理了(后面會一起學(xué)習(xí)更先進的頁式內(nèi)存管理機制)。
最后提醒一下,實模式-保護模式、段式內(nèi)存管理-頁式內(nèi)存管理,是兩組獨立的概念,實模式下段式管理,段寄存器存的僅僅是段基址,保護模式下,段寄存器的含義被劃分為3~15bit+TI+RPL。另外CPU處于實模式還是保護模式,是否開啟了段式映射/頁式映射,這些都屬于硬件狀態(tài),可以通過標(biāo)志寄存器中相應(yīng)的位進行設(shè)置或判斷。
|
|