[OS_0x01]實(shí)模式 --> 保護(hù)模式(段式-->段頁式)

``本文的一些截圖來自于<IntelDEV卷3>和<x86匯編從實(shí)模式到保護(hù)模式>`

最近復(fù)習(xí)一些操作系統(tǒng)的知識,首先遇到了個坑便是計(jì)算機(jī)尋址問題.
本文是一些偏理論的東西(匯編可能在工作中用不到,需要的時(shí)候再深入研究吧- -!)

本文參考的一些博客和書本:

<a href=http://blog.csdn.net/trochiluses/article/details/8954527>[實(shí)模式與保護(hù)模式解惑之(一)——二者的起源與區(qū)別]</a>
匯編相關(guān)-從匯編研究局部變量機(jī)制
我看保護(hù)模式
x86匯編語言:從實(shí)模式到保護(hù)模式

實(shí)模式

  • 是什么

INTEL 8086CPU的尋址方式.

具體利用分段機(jī)制訪問內(nèi)存,訪問時(shí)給出一個段基地址和一個段內(nèi)偏移;(如DS:AX,其中DS和AX在這里的字長為16),訪問的地址為實(shí)實(shí)在在的物理地址.

  • 為什么

    一個原因是8086特點(diǎn) - 數(shù)據(jù)總線16位(字長為16)、地址總線卻有20位(準(zhǔn)確地說是CPU的尋址能力需要被設(shè)計(jì)成20位).

    這樣程序員在沒有特殊機(jī)制的引入時(shí),只能訪存2^16 B = 64 KB的空間(16位字長),達(dá)不到產(chǎn)品經(jīng)理的需求(20位地址空間 = 2^20 B = 1 MB

    于是,這里設(shè)計(jì)成了使用段基地址 + 段內(nèi)偏移的方式: 物理地址 = 段基地址<<16 + 段內(nèi)偏移
    有個小問題就是這種方式實(shí)際上可訪問的空間大于1M,即最大可以訪問到0xFFFF*16 + 0xFFFF = 0x10FFEF > 0xFFFFF,多出的這些地址將從0開始(即對1M取模).

  • 實(shí)模式的段的特點(diǎn) -

    • 每個段基地址都是16的倍數(shù);

    • 每個段的最小長度是16 Bytes,最大是64 KB;<----------更新: 段的大小從1B到64KB都可以.見wiki:

      1. WIKIPEDIA.png

    • 訪問的每個地址都是實(shí)際的物理地址.

  • 遺留的問題

系統(tǒng)程序與用戶程序訪問的地址共存于同一地址空間,并且一視同仁:

  • 沒有權(quán)限保護(hù)(用戶可以隨意訪問1M的全部內(nèi)存地址空間)
  • 不支持多任務(wù).

保護(hù)模式

保護(hù)模式之所以叫“保護(hù)模式”,因?yàn)樗麑Χ嗳蝿?wù)提供了保護(hù),并加入了權(quán)限.
瑪?shù)挛蚁肫饋砹舜蠖R編時(shí)保護(hù)模式這一章跳過了因?yàn)槠谀┎豢嘉秩?

  • 是什么
    80386及后續(xù)系列CPU所資磁的內(nèi)存尋址方式.

  • 為什么
    引入保護(hù)模式的目的有:

    • 保護(hù) - 權(quán)限管理
    • 多任務(wù)隔離
    • 向下兼容 - ...
  • 怎么做
    這里簡要介紹一下保護(hù)模式.

    • 尋址方式的改變:

    • 實(shí)模式下 - 可以直接訪問段基地址(段寄存器16_bit)<<16 + 段內(nèi)偏移并做一些單字長操作(訪存/賦值) .

    • 保護(hù)模式下 -
      先來點(diǎn)廢話:
      由于需要引入權(quán)限管理, 需要:訪存時(shí)指出當(dāng)前執(zhí)行指令的人有沒有權(quán)限訪問這個地址;
      這句話至少包括了2點(diǎn):

      • 當(dāng)前指令的權(quán)限描述;
      • 目標(biāo)地址的權(quán)限描述.

      所以當(dāng)前的解決方案已經(jīng)呼之欲出了:需要一個數(shù)據(jù)結(jié)構(gòu)來描述內(nèi)存地址.

保護(hù)模式下的尋址_part1段式(1MB --> 4GB 32Bit)

  • 機(jī)器啟動流程 - 一個PC啟動時(shí)都會先進(jìn)入實(shí)模式,接著由某指令轉(zhuǎn)入保護(hù)模式.

  • 段描述符表 - 是一堆段描述符的集合,有GDT和LDT.其中GDT是全局描述符表Global Descriptor Table,該表是為整個OS服務(wù)的,在進(jìn)入保護(hù)模式之前定義好(由bootloader). LDT,相對應(yīng)地,則是局部段描述表,是每個進(jìn)程自己獨(dú)有的?.

    GDTR寄存器存放著GDT的地址.
    理論上說GDT可以位于4GB中的任何一個位置,但因?yàn)樾枰獜膶?shí)模式轉(zhuǎn)保護(hù)模式,所以一般只在1M以內(nèi)的位置...(參見<x86從實(shí)模式到保護(hù)模式>)

    2. GDTR,48bit的寄存器

  • 描述符表項(xiàng) - 無論是全局還是局部的描述符表,表項(xiàng)字段都如下圖所示:(it‘s a fucking Data Structure!)

    3. 描述符表項(xiàng)

    里面記錄了這個段的:

    • 段基地址BASE(32位一共,被分割成了3部分)
    • 段界限LIMIT(20位)
    • 段界限Granularity (Seg.LIMIT字段)的單位(0 - 1B/1 - 4KB).

段界限及其單位表示 段內(nèi)偏移的最值(對于向上增長的段而言表示了最大值,而向下增長的段如堆棧段則是表示段內(nèi)偏移的最小值哦)<------why?舉個例子如下:(如有錯誤請指出)

0xFFFFFFFF |********|
                     ... 
0x00A01001 |********|<-----SS:EBP (指向棧底) }
0x00A00FFF |********|<-----SS:ESP (指向棧頂) }EBP,ESP兩個的值可能都是段內(nèi)偏移
                     ...
0x00A00002 |        |
0x00A00000 |********|<-----SS:<段界限*粒度+1> Suppose a MIN_VAL's linear addr
                     ...
                     |
0x0000FF00 |********|
                     ...
0x00000002 |        |(每次存進(jìn)來一個字2 bytes,所以地址上相差2)
0x00000000 |********|<-----SS:0
0.假設(shè)某程序運(yùn)行時(shí)調(diào)用了一個函數(shù),這時(shí)該程序的動作包括`保存現(xiàn)場`,`聲明堆棧段`;我們關(guān)心的是聲明堆棧段(用來存儲函數(shù)參數(shù)、局部變量等).
1.這個段也有基址(我們假設(shè)為0x0,后面你就知道其實(shí)這個假設(shè)是ok的)
2.聲明時(shí)要做的事是指定EBP、ESP(有點(diǎn)欽定的感覺),接著ESP減2,push進(jìn)來函數(shù)參數(shù)之類的,每次push,ESP會自動減2(別問我為什么自動),每次pop,ESP會自動加2
3.上面所說的段內(nèi)偏移的最小值,指的就是當(dāng)ESP一直減,最多只能減到<段界限*粒度> ,再減就提示堆棧溢出了.

<a href=http://www.360doc.com/content/16/0223/12/28062682_536641957.shtml>匯編相關(guān)-從匯編研究局部變量機(jī)制</a>

所以現(xiàn)在一個段最大可以是4G,最小可以是1B.

理論上訪問4G內(nèi)存不再需要什么段啥的,一個段足矣(這種情況便是所謂的平坦模式):


4. flat mode
  • 段選擇子 - 段寄存器CS/DS/SS/etc...此時(shí)不再是被用來左移并相加的對象,而是存放一個索引+標(biāo)記+權(quán)限,這個索引指向了需要訪問的段所在描述符.這些存放的內(nèi)容被稱為段選擇子.

    5. 段選擇子

    由段選擇子的索引量可以看出一個表最多有2^13=8192個表項(xiàng)
    (實(shí)際上32位處理器如80386,他們的段寄存器的長度為32位,只不過后面16位我們不可見,是處理器用作描述符高速緩存的)

  • 整體描述 (from high level)


    6. 線性地址如何得到-此圖來自<a href=http://bbs.chinaunix.net/thread-2083672-1-1.html>thread</a>('8'是表示立即數(shù)填充Index字段)
  • 權(quán)限 :
    權(quán)限字 0 - 3(高 - 低),如下:


    7. 特權(quán)級別
  • 權(quán)限的判斷機(jī)制:

    • 代碼段CS寄存器里的CPL為當(dāng)前執(zhí)行權(quán)限,稱其為CPL(current);
    • 數(shù)據(jù)段DS(或SS等,發(fā)出尋址請求指令所在寄存器)里的CPL為請求權(quán)限,稱其為RPL(request);
    • 段描述符中的段權(quán)限D(zhuǎn)PL;

3者進(jìn)行一個判斷,如果合法(比較復(fù)雜的判斷,例如滿足DPL>=CPL,DPL >= RPL,且CPL <= RPL等),則進(jìn)行地址轉(zhuǎn)換(此時(shí)得出的是虛擬地址,需要轉(zhuǎn)為物理地址,轉(zhuǎn)換又跟此時(shí)的另一些東西相關(guān),見part2),轉(zhuǎn)換后最終尋址到所需地址.

總之,在保護(hù)模式下做什么事都得先進(jìn)行權(quán)限檢查.

PS: 這里的ring 0也就是所謂的內(nèi)核態(tài),而linux中的用戶態(tài)是ring 3.

參考:<a href=http://blog.csdn.net/xiao_0429/article/details/47165169>我看保護(hù)模式</a>
(具體如何做的不深入了,涉及太多匯編相關(guān)的東西,知道就好)<-------fuck that!既然有興趣讀到這些知識,最好搞懂, 不然辜負(fù)這些知識.

保護(hù)模式下的尋址_part2段頁式(更復(fù)雜的機(jī)制來了)

  • 整理一下目前為止接觸到的東西:
    1. 機(jī)器啟動階段先進(jìn)入的是實(shí)模式(1M內(nèi)存尋址空間,20Bit地址 + 16Bit數(shù)據(jù)), 然后由某指令轉(zhuǎn)入保護(hù)模式.
    2. 保護(hù)模式_段式 - 最主要是為了解決權(quán)限問題,比如數(shù)據(jù)段不能被拿來當(dāng)代碼段運(yùn)行,當(dāng)前權(quán)限低的不能訪問權(quán)限高的段等.
      2.1 保護(hù)模式_段式 - GDT 和LDT(本來該設(shè)計(jì)是整個OS有一個GDT,每個進(jìn)程有自己的LDT,而linux的進(jìn)程極少使用LDT,基本上GDT和LDT起始為止都是0x00000000
      2.2 GDT的地址和長度存放于GDTR(Global Descriptor Table Register),其中0~15為GDT長度,16~47為GDT所在地址.
  1. 保護(hù)模式_段式 - the entry of GDT(or LDT),每個表項(xiàng)描述了一個段.
  2. 段選擇子 - 段寄存器(如CS,SS等)存放的內(nèi)容包括INDEX、TI、RPL.
  3. 權(quán)限4個等級(特權(quán)0 - 內(nèi)核態(tài), 特權(quán)3 - 用戶態(tài))- also known as ring 0 ~ 3.不管做什么都需要進(jìn)行權(quán)限判斷。
  • 目前為止,訪問內(nèi)存是這樣訪問的:
用戶提供段選擇子(Index,TI,RPL)和段內(nèi)偏移(offset)  |
GDTR/LDTR提供了GDT和LDT的地址和長度                |-->權(quán)限檢查(結(jié)合代碼段CS中的權(quán)限字,段選擇子中的權(quán)限字和GDT/LDT表項(xiàng)中的權(quán)限字DPL進(jìn)行檢查)
                                                        -->找到Index所在段,取出其段基地址
                                                            -->把用戶提供的段內(nèi)偏移與段基地址結(jié)合構(gòu)成一個完整的地址(這里稱為**線性地址**)

也就是上面的圖6表示的.
在單純只有分段的情形下,這個線性地址就是物理地址.

  • 目前為止,基于段的內(nèi)存替換是這樣進(jìn)行的:
  • 分段的缺點(diǎn) - 內(nèi)存外部碎片.(段大小不確定,使用一段時(shí)間之后,內(nèi)存可能會有很多微小的空洞,不足以提供分配)===>因此分頁機(jī)制就來了.

分頁機(jī)制

  • 大致描述:
    • 4GB物理內(nèi)存以4KB大小分為 4GB / 4KB = 1048576(1M)個頁,頁也稱為頁框(page frame).
    • 引入虛擬內(nèi)存的概念.對于每個進(jìn)程而言,大家都有自己的4GB內(nèi)存空間,并且通過一定的手段(后面解釋), 按頁為單位映射到實(shí)際的物理內(nèi)存上.
    • 映射表,上一點(diǎn)提到的虛擬內(nèi)存中的頁面與實(shí)際內(nèi)存物理頁面間通過映射表來聯(lián)系,甚至每個進(jìn)程都有自己的映射表, 另外這個映射表也很可能是分級的以解決空間利用效率.
    • 頁面的管理和頁面的分配沒有關(guān)系,線性地址(也就是段管理單元得到的地址)也與頁面的分配沒有關(guān)系.
8. 段--線性地址---頁映射表(簡稱頁表)---物理頁
  • 詳細(xì)深入:
  • MMU(Memory Management Unit)是CPU中的內(nèi)存管理單元.
  • 頁目錄:分頁機(jī)制實(shí)際上更復(fù)雜一些,上面說的"頁表"一共有1M個,每個表項(xiàng)有4Bytes,那么整個表有4MB這么大. 每個進(jìn)程有自己的頁表,并且一般不會用到這么大,每次換入換出RAM是不是TM很煩?頁目錄就是解決這個的.(也就是上文里提到的頁映射表分級問題)
  • 4GB內(nèi)存中一共有1M個頁 --> 現(xiàn)在把這1M分成2個層次,即 1K * 1K,給第一個1K一個新的名字——頁目錄表(Page Dir. Tbl.),第二個1K是真正的頁表. 頁表的規(guī)模變小了但相應(yīng)地?cái)?shù)目變?yōu)?K個,所謂頁目錄表,就是存放這1K個頁表的頁.這里需要注意兩種表的表項(xiàng)大小都是4B,所以兩種表的大小正好都是4K即一個頁的大小.
    還是有點(diǎn)亂,見下面一系列的圖.(退后 我要開始裝逼了?。?br> Powered By Processon.com
  • Naive的頁表:


    Naive頁表
  • 分層次的頁表:


    分層次的頁表
  • 分段/分頁的關(guān)系:


    分段/分頁的關(guān)系
  • 段頁式物理地址的獲取:


    段頁式物理地址的獲取

一些其他問題:

  • 吐槽 - 為什么經(jīng)常弄不懂呢?一部分原因是自己太懶,另一部分書確實(shí)也有些沒講清楚或者考試不考尼瑪就跳過了.(歸根到底還是自己的問題,別怪別人...)


    9.-1 教科書中出現(xiàn)的地址轉(zhuǎn)換還是不夠精確,沒有說明段選擇子也沒有設(shè)計(jì)頁的分層
  • 目前關(guān)注的這些應(yīng)該還算是尋址方式的一些東西,而上面有提到需要給進(jìn)程分配頁面,以及在該頁面不怎么使用時(shí)換出,那么這些動作是怎么做的呢?
    9. 有錯請指出,該圖來自<a href = http://www.cnblogs.com/bizhu/archive/2012/10/09/2717303.html>cnblog</a>

上面最后幾個圖如果看不清請猛戳<a href = https://www.processon.com/view/link/578a274ce4b0701cc02852c6> 我的文件</a>
歡迎大家糾錯,共同進(jìn)步.


寫這篇的時(shí)候聯(lián)想到的一些問題:(亟待深入)

  • 快表? - ok.

  • 缺頁中斷 ?

  • dirty ?

  • 伙伴系統(tǒng) ?

  • slab/slub ?

  • malloc ?

  • page cache / buffer cache 又是什么呢 ?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本文試圖用有限的篇幅來闡述80386保護(hù)模式重要知識點(diǎn)。本文不是一個系統(tǒng)全面的知識介紹,您可能需要了解相關(guān)的803...
    JeffreyLi閱讀 2,355評論 0 10
  • 最近開始想稍微深入一點(diǎn)地學(xué)習(xí)Linux內(nèi)核,主要參考內(nèi)容是《深入理解Linux內(nèi)核》和《深入理解Linux內(nèi)核架構(gòu)...
    ice_camel閱讀 1,926評論 0 2
  • 1 內(nèi)存尋址 1.1 物理地址、虛擬地址以及線性地址 物理地址: 物理內(nèi)存的內(nèi)存單元地址 虛擬地址: 程序員看到的...
    瘋狂小王子閱讀 3,123評論 3 21
  • 但是我早早的起來了卻在玩手機(jī)。持續(xù)性一事無成。這不是搬起石頭砸自己腳嗎? 解決拖延癥的方法最好就是硬剛 在學(xué)習(xí)上一...
    Charging99閱讀 162評論 0 0
  • 照片加上水中倒影之后,感覺整個世界都不一樣了。
    小輯輕渡閱讀 342評論 0 0

友情鏈接更多精彩內(nèi)容