轉(zhuǎn)載自:http://blog.csdn.net/michael2012zhao/article/details/17421383
一、 段寄存器的產(chǎn)生
段寄存器的產(chǎn)生源于Intel 8086 CPU體系結(jié)構(gòu)中數(shù)據(jù)總線與地址總線的寬度不一致。
數(shù)據(jù)總線的寬度,也即是ALU(算數(shù)邏輯單元)的寬度,平常說(shuō)一個(gè)CPU是“16位”或者“32位”指的就是這個(gè)。8086CPU的數(shù)據(jù)總線是16位。
地址總線的寬度不一定要與ALU的寬度相同。因?yàn)锳LU的寬度是固定的,它受限于當(dāng)時(shí)的工藝水平,當(dāng)時(shí)只能制造出16位的ALU;但地址總線不一樣,它可以設(shè)計(jì)得更寬。地址總線的寬度如果與ALU相同當(dāng)然是不錯(cuò)的辦法,這樣CPU的結(jié)構(gòu)比較均衡,尋址可以在單個(gè)指令周期內(nèi)完成,效率最高;而且從軟件的解決來(lái)看,一個(gè)變量地址的長(zhǎng)度可以用整型或者長(zhǎng)整型來(lái)表示會(huì)比較方便。
但是,地址總線的寬度還要受制于需求,因?yàn)榈刂房偩€的寬度決定了系統(tǒng)可尋址的范圍,即可以支持多少內(nèi)存。如果地址總線太窄的話,可尋址范圍會(huì)很小。如果地址總線設(shè)計(jì)為16位的話,可尋址空間是2^16=64KB,這在當(dāng)時(shí)被認(rèn)為是不夠的;Intel最終決定要讓8086的地址空間為1M,也就是20位地址總線。
地址總線寬度大于數(shù)據(jù)總線會(huì)帶來(lái)一些麻煩,ALU無(wú)法在單個(gè)指令周期里完成對(duì)地址數(shù)據(jù)的運(yùn)算。有一些容易想到的可行的辦法,比如定義一個(gè)新的寄存器專(zhuān)門(mén)用于存放地址的高4位,但這樣增加了計(jì)算的復(fù)雜性,程序員要增加成倍的匯編代碼來(lái)操作地址數(shù)據(jù)而且無(wú)法保持兼容性。
Intel想到了一個(gè)折中的辦法:把內(nèi)存分段,并設(shè)計(jì)了4個(gè)段寄存器,CS,DS,ES和SS,分別用于指令、數(shù)據(jù)、其它和堆棧。把內(nèi)存分為很多段,每一段有一個(gè)段基址,當(dāng)然段基址也是一個(gè)20位的內(nèi)存地址。不過(guò)段寄存器仍然是16位的,它的內(nèi)容代表了段基址的高16位,這個(gè)16位的地址后面再加上4個(gè)0就構(gòu)成20位的段基址。而原來(lái)的16位地址只是段內(nèi)的偏移量。這樣,一個(gè)完整的物理內(nèi)存地址就由兩部分組成,高16位的段基址和低16位的段內(nèi)偏移量,當(dāng)然它們有12位是重疊的,它們兩部分相加在一起,才構(gòu)成完整的物理地址。
| Base | b15 ~ b12 | b11 ~ b0 | |
| Offset | | o15 ~ o4 | o3 ~ o0 |
| Address | a19 ~ a0 |
這種尋址模式也就是“實(shí)地址模式”。在8086中,段寄存器還只是一個(gè)單純的16位寄存器,而且操作寄存器的指令也不是特權(quán)指令。通過(guò)設(shè)置段寄存器和段內(nèi)偏移,程序就可以訪問(wèn)整個(gè)物理內(nèi)存,無(wú)安全性可言。
總之一句話,段寄存器的設(shè)計(jì)是一個(gè)權(quán)宜之計(jì),現(xiàn)在看來(lái)可以說(shuō)是一個(gè)臨時(shí)性的解決方案,設(shè)計(jì)它的目的是為了把地址空間從64KB擴(kuò)展為1MB,僅此而已。但是它的加入?yún)s為日后Intel系列芯片的發(fā)展帶來(lái)諸多不便,也為理解i386體系帶來(lái)困擾。
二、 實(shí)現(xiàn)保護(hù)模式
到了80386問(wèn)世的時(shí)候,工藝已經(jīng)有了很大的進(jìn)步,386的ALU有已經(jīng)從16位躍升為32位,也就是說(shuō),38086是32位的CPU,而且結(jié)構(gòu)也已經(jīng)比較成熟,接下來(lái)的80486一直到Pentium系列雖然速度提高了幾個(gè)數(shù)量級(jí),但并沒(méi)有質(zhì)的變化,所以被統(tǒng)稱(chēng)為i386結(jié)構(gòu)。
對(duì)于32位的CPU來(lái)說(shuō),只要地址總線寬度與數(shù)據(jù)總線寬度相同,就可以尋址2^32=4GB的內(nèi)存空間,這已經(jīng)足夠用,已經(jīng)不再需要段寄存器來(lái)幫助擴(kuò)展。但這時(shí)Intel已經(jīng)無(wú)法把段寄存器從產(chǎn)品中去掉,因?yàn)樾碌腃PU也是產(chǎn)品系列中的一員,根據(jù)兼容性的需要,段寄存器必須保留下來(lái)。
這時(shí),技術(shù)的發(fā)展需求Intel在其CPU中實(shí)現(xiàn)“保護(hù)模式”,用戶(hù)程序的可訪問(wèn)內(nèi)存范圍必須受到限制,不能再任意地訪問(wèn)內(nèi)存所有地址。Intel決定利用段寄存器來(lái)實(shí)現(xiàn)他們的保護(hù)模式,把保護(hù)模式建立在段寄存器的基礎(chǔ)之上。
對(duì)于段的描述不再只是一個(gè)20位的起始地址,而是全新地定義了“段描述項(xiàng)”。段描述項(xiàng)的結(jié)構(gòu)如下:
| B31 ~ B24 | DES1 (4 bit) | L19 ~ L16 |
| DES2 (8 bit) | B23 ~ B16 |
| B15 ~ B0 |
| L15 ~ L0 |
每一行是兩個(gè)字節(jié),總共8個(gè)字節(jié),64位。
DES1和DES2分別是一些描述信息,用于描述本段是數(shù)據(jù)段還是代碼段,以及讀寫(xiě)權(quán)限等等。B0B31是段的基地址,L0L19是段的長(zhǎng)度。
注意,規(guī)定段的長(zhǎng)度是非常必要的,如果不限定段長(zhǎng)度,“保護(hù)”就無(wú)從談起,用戶(hù)程序的訪問(wèn)至少不能超過(guò)段的范圍。另外,段長(zhǎng)度只有20位,所代表的最大可能長(zhǎng)度為220=1M,而整個(gè)地址空間是232=4GB,這樣來(lái)看,段的長(zhǎng)度是不是太短了?其實(shí),在DES1中,有一位用于表示段長(zhǎng)度的單位,當(dāng)它被置1時(shí)(一般情況下都是如此),表示長(zhǎng)度單位為4KB,這樣,一個(gè)段的最大可能尺寸就成了1M*4K=4G,與地址空間相穩(wěn)合。4KB也正是一個(gè)內(nèi)存頁(yè)的大小,說(shuō)明段的大小也是向頁(yè)對(duì)齊的。
另外,注意到一個(gè)有趣的現(xiàn)象嗎?段描述項(xiàng)的結(jié)構(gòu)被設(shè)計(jì)得不連續(xù),不論是段基地址還是段長(zhǎng)度,都被分成了兩節(jié)表示。這樣的設(shè)計(jì)與80286的過(guò)渡有關(guān)。上面的段描述項(xiàng)結(jié)構(gòu)去掉第一行后剩下的三行正是286的段描述項(xiàng)。286被設(shè)計(jì)為24位地址總線,所以段基址是24位,相應(yīng)地段長(zhǎng)是16位。在386的地址總線擴(kuò)展為32位之后,還必須兼容286產(chǎn)品的設(shè)計(jì),所以只好在段描述項(xiàng)上“打補(bǔ)丁”。
在386中,段寄存器還是16位,那么16位的段寄存器如何存放得下64位的段描述項(xiàng)? 段描述項(xiàng)不再由段寄存器直接持有。段描述項(xiàng)存放在內(nèi)存里,系統(tǒng)中可以有很多個(gè)段描述項(xiàng),這些項(xiàng)連續(xù)存放,共同構(gòu)成一張表,16位的段寄存器里只是含有這張表里的一個(gè)索引,但也并不僅是一個(gè)簡(jiǎn)單的序號(hào),而是存儲(chǔ)了一種數(shù)據(jù)結(jié)構(gòu),這種結(jié)構(gòu)的定義如下:
| index (b15 ~ b3) | TI (b2) | RPL (b1 ~ b0) |
其中index是段描述表的索引,它指向其中的某一個(gè)段描述項(xiàng)。RPL表示權(quán)限,00最高,11最低。
還有一個(gè)關(guān)鍵的問(wèn)題,內(nèi)存中的段描述表的起始地址在哪里?顯然光有索引是有不夠的。為此,Intel又設(shè)計(jì)了兩個(gè)新的寄存器:GDTR(global descriptor table register)和LDTR(local descriptor table register),分別用來(lái)存儲(chǔ)段描述表的地址。段寄存器中的TI位正是用于指示使用GDTR還是LDTR。
當(dāng)用戶(hù)程序要求訪問(wèn)內(nèi)存時(shí),CPU根據(jù)指令的性質(zhì)確定使用哪個(gè)段寄存器,轉(zhuǎn)移指令中的地址在代碼段,取數(shù)指令中的地址在數(shù)據(jù)段;根據(jù)段寄存器中的索引值,找到段描述項(xiàng),取得段基址;指令中的地址是段內(nèi)偏移,與段長(zhǎng)比較,確保沒(méi)有越界;檢查權(quán)限;把段基址和偏移相加,構(gòu)成物理地址,取得數(shù)據(jù)。
新的設(shè)計(jì)中處處有權(quán)限與范圍的限制,用戶(hù)程序只能訪問(wèn)被授權(quán)的內(nèi)存空間,從而實(shí)現(xiàn)了保護(hù)機(jī)制。就這樣,在段寄存器的基礎(chǔ)上,Intel實(shí)現(xiàn)了自己的“保護(hù)模式”。
三、 與頁(yè)式存管并存
現(xiàn)代操作系統(tǒng)的發(fā)展要求CPU支持頁(yè)式存儲(chǔ)管理。
頁(yè)式存管本身是與段式存管分立的,兩者沒(méi)有什么關(guān)系。但對(duì)于Intel來(lái)說(shuō),同樣是由于“段寄存器”這個(gè)歷史的原因,它必須把頁(yè)式存管建立在段式存管的基礎(chǔ)之上,盡管這從設(shè)計(jì)的角度來(lái)說(shuō)這是沒(méi)有道理,也根本沒(méi)有必要的。
在段式存管中,由程序發(fā)出的變量地址經(jīng)映射(段基址+段內(nèi)偏移)之后,得到的32位地址就是一個(gè)物理地址,是可以直接放到地址總線是去取數(shù)的。
在頁(yè)式存管中,過(guò)程也是相似的,由程序發(fā)出的變量地址并不是實(shí)際的物理地址,而是一個(gè)三層的索引結(jié)構(gòu),這個(gè)地址經(jīng)過(guò)一系統(tǒng)的映射之后才可以得到物理地址。
現(xiàn)在對(duì)于Intel CPU來(lái)說(shuō),以上兩個(gè)映射過(guò)程就要先后各做一次。由程序發(fā)出的變量地址稱(chēng)為“邏輯地址”,先經(jīng)過(guò)段式映射成為“線性地址”,線性地址再做為頁(yè)式映射的輸入,最后得到“物理地址”。
Linux內(nèi)核實(shí)現(xiàn)了頁(yè)式存儲(chǔ)管理,而且并沒(méi)有因?yàn)閮蓪哟婀艿挠成涠兊酶鼜?fù)雜。Linux更關(guān)注頁(yè)式內(nèi)存管理,對(duì)于段式映射,采用了特殊的方式把它簡(jiǎn)化。讓每個(gè)段寄存器都指向同一個(gè)段描述項(xiàng),即只設(shè)了一個(gè)段,而這個(gè)段的基地址為0,段長(zhǎng)度設(shè)為最大值4G,這個(gè)段就與整個(gè)物理內(nèi)存相重合,邏輯地址經(jīng)映射之后就與線性地址相同,從而把段式存管變成“透明”的。
這,就是Intel處理器中“段寄存器”的故事。