x86保護模式的幾點思考

     研究x86其實主要的目的還是操作系統,即使在有原碼的情況下你還得要看懂彙編,要看懂彙編就得明白體系結構,最好再懂點兒硬件原理……到此熱身運動基本完成,開始看操作系統原碼吧!值得注意的這些準備活動還都只是看懂,而世界上最遙遠的距離不是“生與死”,也不是“曾經櫃檯上有一袋鹽擺在我面前我沒有珍惜,等到一位大媽將櫃檯撲倒才追悔莫及”,而是“看懂了與寫出來的距離”。
     當然,如果你只想對操作系統做定性的分析或者關注的是其中的某些算法,那麼恭喜你,不用受x86複雜的體系結構煎熬了……我所說x86的複雜也是相對於DSP,因爲DSP的應用程序開發人員不會去寫惡意的代碼,這使得DSP不用過多的考慮安全性而把重心放在性能上。
     x86不同,一方面,它的市場定位和跑在其上的windows系列操作系統(漏洞太多)註定讓那些Intel的設計師們想方設法的把內存的物理地址掩藏起來,這也是保護模式的主要意義;另一方面,保護之意也可以理解爲避免因程序設計上的缺陷而導致的非惡意進程越權行爲。根本上說是CPU對進程特權優先級的管理。可以將x86的保護模式理解爲尋址方式的模型,或者說是一種內存管理機制。
     在學習保護模式的過程中我主要有以下幾點疑惑:
     1.對x86中段的理解?
     2.什麼叫一致性代碼段?這裏又可以引出系統調用的問題。
     3.系統調用到底是個啥?
     4.中斷號、中斷向量、IRQ三者之間的關係?
     5.跳轉指令中,call和jmp有什麼區別?
     6.從實模式跳轉到保護模式之前,要將cs寄存器保存起來,而cs的初始值從何而來?
     7.特權級轉移的過程爲什麼要伴隨着堆棧的變化?如果不變換堆棧,有什麼方案可以替換?
     8.調用門過程中,被調用者堆棧需要存儲調用者的eip和cs,那個eip和cs的存儲的先後順序有影響嗎?
     9.內存的分段機制已經很好的保護了內存,那麼還要分頁機制有什麼用啊?分頁機制的妙處何在?
     10.一條指令能做多少事?
     11.保護機制,“保護”二字都體現在哪?
問題1.對x86中段的理解?
     段是一段具有相同屬性的代碼或者數據的集合,實際上對於CPU而言並不知道段的存在,CPU只知道從內存中取得一個字節的數據,然後將這個字節直接做爲指令執行(如ldgdr)或者再從內存和寄存器中取得操作數拼接成指令執行(如mov ax cs)。如果CPU不需要段的話,那麼可以說段並非必須的。在某種情況(如不需要內存保護,直接對物理地址進行操作)下,我們可以把整個內存都看成一個段;也可以按常用的代碼段、數據段、堆棧段來分;當然也可以自己再隨便再添加N個段,看你!
     x86中,如果沒有全局描述符表和局部描述符表,那麼段沒有任何意義。這兩個表中的段描述符定義了每個段的基地址、段界限和訪問特權級等屬性信息(由於此內容並非本文重點,這裏不再贅述),前面說過我們自己可以添加任意多個段,可如果你不添加相應的段描述符那自定義的段將會被編譯器默認爲代碼段,該自定義段也就沒有任何用處。
     一般來說常用的段有三個:代碼段(.code或.text)、數據段(.data)、堆棧段(.stack)。它們各有各的段基址、段界限和段屬性,對應的選擇子也分別存儲在cs、ds和ss段寄存器中。這三個段也構成了x86的保護模式的基本構架,CPU主要的尋址操作都在這三個段中;尋址空間的範圍和大小取決於段描述符段基址和段屬性初始化的值;應用程序對一塊內存的訪問權限也主要是由這三個段描述符決定的。這也就保證了應用程序無法訪問不該訪問的內存空間,從而對內存起到了保護作用,從另一角度講這也算是一種內存管理方案。
問題2:什麼叫一致性代碼段?
     出於內存保護的目的,各個段都有自己的特權級,高特權級進程訪問低特權級的段自是沒問題,可低特權級進程就不能訪問高特權級段嗎?當然能,系統調用就是一種從低特權級代碼跳轉到高特權級代碼過程,一般是從應用程序的代碼(特權級一般爲3)跳轉到操作系統內核某函數接口(特權級一般爲0)的代碼,有關係統調用的故事詳見問題3。
     如果想從一個低優先級跳轉到高優先級代碼,需要目標代碼段是一致性代碼段,而這時你也許會認爲應用程序此時運行在高優先級下了,那你就錯了!一致性代碼段最大的特點就是即使你跳轉到高優先級的一致性代碼段,但當前任進程的優先級不變,仍爲低優先級。比如你當前運行的應用程序(爲一個進程)優先級爲3,運行過程中需要跳轉到一個優先級爲0的一致性代碼段,OK,跳轉成功之後,當前進程的優先級仍爲3。
(這裏其實有許多保護機制的概念,如描述符、CPL、DPL、RPL等,我只想概念性的講一下,所以省略這些細節,你一定也不會很想看到這種涉及到寄存器級的東西)
     一句話總結一下:一致性代碼段是在兩個不同特權級代碼段之間做跳轉的時候用到的定義,當從一個特權級較低的代碼段跳轉到特權級較高的代碼段的時候,特權級較高的代碼段可以臨時的將特權級降到較低特權級,進而允許這次跳轉並繼續執行,那麼就說該目標代碼段是一致性代碼段,否則是不一致性代碼段。
     也許你明白了,這種一致性代碼段的機制是爲了避免低優先級進程通過跳轉而變向的提高自己的優先級,進而在高優先級特權下對你的操作系統你的計算機爲所欲爲!而明白這些也就可以了,詳細的如果你有興趣可以去查手冊。
問題3:系統調用到底是個啥?
     系統調用是操作系統爲應用程序提供的一種接口函數,比如說任務掛起函數。同樣出於安全性考慮,操作系統不想讓用戶直接調用內核函數或者接口,於是就制定了某種規則,基於中斷機制的系統調用就是其中的一種。我對系統調用的理解:與其說系統調用是一種函數,到不如說系統調用是一種協議,一種由操作系統制定的應用程序如何調用內核函數接口的規則。
     要不然你就別翻譯成系統調用,這種蹩腳的翻譯真是讓讀者一頭霧水,本來《操作系統概念》就號稱計算機專業的天書,再加上這些莫名其妙的定義,我們除了更莫名其妙之外還有其他的結果嗎?相同的例子還有“任務上下文”,筆者當初看到這個定義的感覺簡直就如劉姥姥穿越到計算中心——太TM扯了!心中不住的嘆息,學個OS怎麼還把中學給文章分段的那套用上了,這任務的上下文該分成“總分總”啊?還是“總分”啊?翻譯的仁兄,您不是上帝派來玩兒我的吧……咳咳,回來接着說系統調用(沒辦法,權威的書都是這翻譯的,我也只能沿用了)。
     雖然系統調用已經有了新的實現方式,但學習需要循序漸進,我們就從經典開始。
     在奔騰Pro以前,x86中的系統調用是通過中斷機制實現的,對應的中斷號是0x80。中斷主要分爲外部中斷和陷阱:外部中斷是由硬件通過相應引腳,發送中斷信號給CPU產生的,比如說時鐘中斷,外部中斷也叫硬中斷;陷阱是由trap指令產生的,由編程人員用手敲的,比如說這裏講到的0x80號中斷,陷阱又叫做軟中斷。
     Linux中常見的系統如fork(),當你調用一個fork()去創建一個進程時,實際上你離Linux的內核還很遠,因爲這個fork()只是一個接口,它做的只是以下三步:
1.把系統調用的編號存入EAX;
2.把函數參數存入其它通用寄存器;
3.觸發0x80號中斷(int 0x80)。
     中斷觸發後,0x80對應的中斷處理子程序(ISR)開始運行,並調用真正實現fork操作的內核接口函數,最後將返回值存入到EAX中。
     以上就是Linux在x86中,中斷機制實現的系統調用的主要過程。
     當然,也有人把實現真正功能的那些內核接口函數叫作系統調用,不過我覺得那都是浮雲,以後你看到這些不懂的名詞就看一下英文原文,看不懂它什麼意思也沒事兒,你就把它當個名詞就得了,哪怕你叫它“小黑”都成,然後再看它的詳細解釋,別跟着譯者的翻譯理解就行了,要不然真成天書了。
     剩下的問題以後再寫……
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章