``本文的一些截圖來自于<IntelDEV卷3>和<x86匯編從實(shí)模式到保護(hù)模式>`
最近復(fù)習(xí)一些操作系統(tǒng)的知識,首先遇到了個(gè)坑便是計(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 8086
CPU的尋址方式.
具體利用分段機(jī)制訪問內(nèi)存,訪問時(shí)給出一個(gè)段基地址和一個(gè)段內(nèi)偏移檩奠;(如DS:AX宋舷,其中DS和AX在這里的字長為16),訪問的地址為實(shí)實(shí)在在的物理地址.
-
為什么
一個(gè)原因是
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)偏移
有個(gè)小問題就是這種方式實(shí)際上可訪問的空間大于1M笨触,即最大可以訪問到0xFFFF*16 + 0xFFFF = 0x10FFEF > 0xFFFFF
懦傍,多出的這些地址將從0開始(即對1M取模). -
實(shí)模式的段的特點(diǎn) -
每個(gè)段基地址都是16的倍數(shù);
-
每個(gè)段的最小長度是16 Bytes芦劣,最大是64 KB粗俱;<----------更新: 段的大小從1B到64KB都可以.見wiki:
1. WIKIPEDIA.png 訪問的每個(gè)地址都是實(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)限訪問這個(gè)地址寸认;
這句話至少包括了2點(diǎn):- 當(dāng)前指令的權(quán)限描述签财;
- 目標(biāo)地址的權(quán)限描述.
所以當(dāng)前的解決方案已經(jīng)呼之欲出了:需要一個(gè)數(shù)據(jù)結(jié)構(gòu)來描述內(nèi)存地址.
保護(hù)模式下的尋址_part1段式(1MB --> 4GB 32Bit)
機(jī)器啟動(dòng)流程 - 一個(gè)PC啟動(dòng)時(shí)都會(huì)先進(jìn)入實(shí)模式,接著由某指令轉(zhuǎn)入保護(hù)模式.
-
段描述符表
- 是一堆段描述符的集合偏塞,有GDT和LDT.其中GDT是全局描述符表Global Descriptor Table
,該表是為整個(gè)OS服務(wù)的唱蒸,在進(jìn)入保護(hù)模式之前定義好(由bootloader). LDT,相對應(yīng)地烛愧,則是局部段描述表油宜,是每個(gè)進(jìn)程自己獨(dú)有的?.GDTR寄存器存放著GDT的地址.
理論上說GDT可以位于4GB中的任何一個(gè)位置怜姿,但因?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)里面記錄了這個(gè)段的:
- 段基地址BASE(32位一共蚁堤,被分割成了3部分)
- 段界限LIMIT(20位)
- 段界限Granularity (Seg.LIMIT字段)的單位(0 - 1B/1 - 4KB).
段界限及其單位
表示 段內(nèi)偏移的最值
(對于向上增長的段而言表示了最大值,而向下增長的段如堆棧段則是表示段內(nèi)偏移的最小值哦)<------why但狭?舉個(gè)例子如下:(如有錯(cuò)誤請指出)
0xFFFFFFFF |********|
...
0x00A01001 |********|<-----SS:EBP (指向棧底) }
0x00A00FFF |********|<-----SS:ESP (指向棧頂) }EBP,ESP兩個(gè)的值可能都是段內(nèi)偏移
...
0x00A00002 | |
0x00A00000 |********|<-----SS:<段界限*粒度+1> Suppose a MIN_VAL's linear addr
...
|
0x0000FF00 |********|
...
0x00000002 | |(每次存進(jìn)來一個(gè)字2 bytes披诗,所以地址上相差2)
0x00000000 |********|<-----SS:0
0.假設(shè)某程序運(yùn)行時(shí)調(diào)用了一個(gè)函數(shù),這時(shí)該程序的動(dòng)作包括`保存現(xiàn)場`立磁,`聲明堆棧段`呈队;我們關(guān)心的是聲明堆棧段(用來存儲(chǔ)函數(shù)參數(shù)、局部變量等).
1.這個(gè)段也有基址(我們假設(shè)為0x0唱歧,后面你就知道其實(shí)這個(gè)假設(shè)是ok的)
2.聲明時(shí)要做的事是指定EBP宪摧、ESP(有點(diǎn)欽定的感覺),接著ESP減2颅崩,push進(jìn)來函數(shù)參數(shù)之類的几于,每次push,ESP會(huì)自動(dòng)減2(別問我為什么自動(dòng))沿后,每次pop沿彭,ESP會(huì)自動(dòng)加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)在一個(gè)段最大可以是4G,最小可以是1B.
理論上訪問4G內(nèi)存不再需要什么段啥的,一個(gè)段足矣(這種情況便是所謂的平坦模式):
-
段選擇子
- 段寄存器CS/DS/SS/etc...此時(shí)不再是被用來左移并相加的對象漆弄,而是存放一個(gè)索引+標(biāo)記+權(quán)限饱搏,這個(gè)索引指向了需要訪問的段所在描述符.這些存放的內(nèi)容被稱為段選擇子
.
5. 段選擇子
由段選擇子的索引量可以看出一個(gè)表最多有2^13=8192
個(gè)表項(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)行一個(gè)判斷,如果合法(比較復(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ī)制來了)
- 整理一下目前為止接觸到的東西:
- 機(jī)器啟動(dòng)階段先進(jìn)入的是實(shí)模式(1M內(nèi)存尋址空間殊霞,20Bit地址 + 16Bit數(shù)據(jù)), 然后由某指令轉(zhuǎn)入保護(hù)模式.
-
保護(hù)模式_段式
- 最主要是為了解決權(quán)限問題摧阅,比如數(shù)據(jù)段不能被拿來當(dāng)代碼段運(yùn)行,當(dāng)前權(quán)限低的不能訪問權(quán)限高的段等.
2.1 保護(hù)模式_段式 - GDT 和LDT(本來該設(shè)計(jì)是整個(gè)OS有一個(gè)GDT绷蹲,每個(gè)進(jìn)程有自己的LDT棒卷,而linux的進(jìn)程極少使用LDT,基本上GDT和LDT起始為止都是0x00000000
)
2.2 GDT的地址和長度存放于GDTR(Global Descriptor Table Register)瘸右,其中0~15
為GDT長度,16~47
為GDT所在地址.
- 保護(hù)模式_段式 - the entry of GDT(or LDT)娇跟,每個(gè)表項(xiàng)描述了一個(gè)段.
- 段選擇子 - 段寄存器(如CS,SS等)存放的內(nèi)容包括INDEX太颤、TI苞俘、RPL.
- 權(quán)限4個(gè)等級(特權(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)成一個(gè)完整的地址(這里稱為**線性地址**)
也就是上面的圖6表示的.
在單純只有分段的情形下岗憋,這個(gè)線性地址就是物理地址.
- 目前為止,基于段的內(nèi)存替換是這樣進(jìn)行的:
- 分段的缺點(diǎn) - 內(nèi)存外部碎片.(段大小不確定锚贱,使用一段時(shí)間之后仔戈,內(nèi)存可能會(huì)有很多微小的空洞,不足以提供分配)===>因此
分頁機(jī)制
就來了.
分頁機(jī)制
- 大致描述:
- 4GB物理內(nèi)存以4KB大小分為
4GB / 4KB = 1048576(1M)
個(gè)頁,頁也稱為頁框(page frame). - 引入
虛擬內(nèi)存
的概念.對于每個(gè)進(jìn)程而言监徘,大家都有自己的4GB內(nèi)存空間
,并且通過一定的手段(后面解釋), 按頁為單位映射到實(shí)際的物理內(nèi)存上. - 映射表晋修,上一點(diǎn)提到的虛擬內(nèi)存中的頁面與實(shí)際內(nèi)存物理頁面間通過映射表來聯(lián)系,甚至每個(gè)進(jìn)程都有自己的映射表, 另外這個(gè)映射表也很可能是分級的以解決空間利用效率.
- 頁面的管理和頁面的分配沒有關(guān)系凰盔,線性地址(也就是段管理單元得到的地址)也與頁面的分配沒有關(guān)系.
- 4GB物理內(nèi)存以4KB大小分為
- 詳細(xì)深入:
- MMU(Memory Management Unit)是CPU中的內(nèi)存管理單元.
- 頁目錄:分頁機(jī)制實(shí)際上更復(fù)雜一些墓卦,上面說的"頁表"一共有1M個(gè),每個(gè)表項(xiàng)有4Bytes户敬,那么整個(gè)表有4MB這么大. 每個(gè)進(jìn)程有自己的頁表落剪,并且一般不會(huì)用到這么大,每次換入換出RAM是不是TM很煩尿庐?頁目錄就是解決這個(gè)的.(也就是上文里提到的頁映射表分級問題)
- 4GB內(nèi)存中一共有1M個(gè)頁 --> 現(xiàn)在把這1M分成2個(gè)層次忠怖,即 1K * 1K,給第一個(gè)1K一個(gè)新的名字——頁目錄表(Page Dir. Tbl.)屁倔,第二個(gè)1K是真正的頁表. 頁表的規(guī)模變小了但相應(yīng)地?cái)?shù)目變?yōu)?K個(gè)脑又,所謂頁目錄表,就是存放這1K個(gè)頁表的頁.這里需要注意兩種表的表項(xiàng)大小都是4B锐借,所以兩種表的大小正好都是4K即一個(gè)頁的大小.
還是有點(diǎn)亂问麸,見下面一系列的圖.(退后 我要開始裝逼了!)
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í)換出汰扭,那么這些動(dòng)作是怎么做的呢稠肘?
9. 有錯(cuò)請指出,該圖來自<a href = http://www.cnblogs.com/bizhu/archive/2012/10/09/2717303.html>cnblog</a>
上面最后幾個(gè)圖如果看不清請猛戳<a href = https://www.processon.com/view/link/578a274ce4b0701cc02852c6> 我的文件</a>
歡迎大家糾錯(cuò)萝毛,共同進(jìn)步.
寫這篇的時(shí)候聯(lián)想到的一些問題:(亟待深入)
快表? - ok.
缺頁中斷 ?
dirty ?
伙伴系統(tǒng) ?
slab/slub ?
malloc ?
page cache / buffer cache 又是什么呢 ?