Linux內(nèi)存尋址之分段機(jī)制

前言

最近在學(xué)習(xí)Linux內(nèi)核,讀到《深入理解Linux內(nèi)核》的內(nèi)存尋址一章吵护。原本以為自己對分段分頁機(jī)制已經(jīng)理解了盒音,結(jié)果發(fā)現(xiàn)其實(shí)是一知半解。于是馅而,查找了很多資料祥诽,最終理順了內(nèi)存尋址的知識。現(xiàn)在把我的理解記錄下來瓮恭,希望對內(nèi)核學(xué)習(xí)者有一定幫助雄坪,也希望大家指出錯誤之處。

分段到底是怎么回事

相信學(xué)過操作系統(tǒng)課程的人都知道分段分頁屯蹦,但是奇怪的是書上基本沒提分段分頁是怎么產(chǎn)生的维哈,這就導(dǎo)致我們知其然不知其所以然。下面我們先扒一下分段機(jī)制產(chǎn)生的歷史登澜。

實(shí)模式的誕生(16位處理器及尋址)

在8086處理器誕生之前阔挠,內(nèi)存尋址方式就是直接訪問物理地址。8086處理器為了尋址1M的內(nèi)存空間脑蠕,把地址總線擴(kuò)展到了20位购撼。但是,一個尷尬的問題出現(xiàn)了空郊,ALU的寬度只有16位份招,也就是說,ALU不能計算20位的地址狞甚。為了解決這個問題锁摔,分段機(jī)制被引入,登上了歷史舞臺哼审。

為了支持分段谐腰,8086處理器設(shè)置了四個段寄存器:CS, DS, SS, ES.每個段寄存器都是16位的孕豹,同時訪問內(nèi)存的指令中的地址也是16位的。但是十气,在送入地址總線之前励背,CPU先把它與某個段寄存器內(nèi)的值相加。這里要注意:段寄存器的值對應(yīng)于20位地址總線的中的高16位砸西,所以相加時實(shí)際上是16位內(nèi)存地址(即段內(nèi)偏移值)的高12位與段寄存器中的16位相加叶眉,而低4位保留不變,這樣就形成一個20位的實(shí)際地址芹枷,也就實(shí)現(xiàn)了從16位內(nèi)存地址到20位實(shí)際地址的轉(zhuǎn)換衅疙,或者叫“映射”。

上面關(guān)于分段機(jī)制計算內(nèi)存地址的描述比較難理解鸳慈,畫了一個圖幫助理解

+-----------------+
|       20        |  20位地址總線
+-----------------+
+------------+
|      16    |       16位段地址
+------------+
    +-------------+
    |    12  | 4  |  16位內(nèi)存地址(段內(nèi)偏移量)
    +--------+----+

實(shí)際物理地址 = (段寄存器地址 << 4) + (CPU 提交的訪存地址)

保護(hù)模式的誕生(32位處理器及尋址)

  • 80286處理器的地址總線為24位饱溢,尋址空間達(dá)16M,同時引入了保護(hù)模式(內(nèi)存段的訪問受到限制)
  • 80386處理器是一個32位處理器走芋,ALU和地址總線都是32位的绩郎,尋址空間達(dá) 4G。也就是說它可以不通過分段機(jī)制翁逞,直接訪問4G的內(nèi)存空間肋杖。雖然它是新時代的小王子,超越它的無數(shù)前輩挖函,然而兽愤,它需要背負(fù)家族的使命–兼容前代的處理器。也就是說挪圾,它必須支持實(shí)模式和保護(hù)模式。所以逐沙,80386在段寄存器的基礎(chǔ)上構(gòu)筑保護(hù)模式哲思,并且保留16位的段寄存器。
  • 從80386之后的處理器吩案,架構(gòu)基本相似棚赔,統(tǒng)稱為IA32(32 Bit Intel Architecture)。

IA32的內(nèi)存尋址機(jī)制

尋址硬件

在 8086 的實(shí)模式下徘郭,把某一段寄存器左移4位靠益,然后與地址ADDR相加后被直接送到內(nèi)存總線上,這個相加后的地址就是內(nèi)存單元的物理地址残揉,而程序中的這個地址就叫邏輯地址(或叫虛地址)胧后。在IA32的保護(hù)模式下,這個邏輯地址不是被直接送到內(nèi)存總線而是被送到內(nèi)存管理單元(MMU)抱环。MMU由一個或一組芯片組成壳快,其功能是把邏輯地址映射為物理地址纸巷,即進(jìn)行地址轉(zhuǎn)換,如圖所示眶痰。

MMU

IA32的三種地址

  • 邏輯地址:
    機(jī)器語言指令仍用這種地址指定一個操作數(shù)的地址或一條指令的地址瘤旨。 這種尋址方式在Intel的分段結(jié)構(gòu)中表現(xiàn)得尤為具體,它使得MS-DOS或Windows程序員把程序分為若干段竖伯。每個邏輯地址都由一個段和偏移量組成存哲。
  • 線性地址:
    線性地址是一個32位的無符號整數(shù),可以表達(dá)高達(dá)232(4GB)的地址七婴。通常用16進(jìn)制表示線性地址祟偷,其取值范圍為0x00000000~0xffffffff。
  • 物理地址:
    也就是內(nèi)存單元的實(shí)際地址本姥,用于芯片級內(nèi)存單元尋址肩袍。 物理地址也由32位無符號整數(shù)表示。

MMU地址轉(zhuǎn)化過程

MMU是一種硬件電路婚惫,它包含兩個部件氛赐,一個是分段部件,一個是分頁部件先舷,在此艰管,我們把它們分別叫做分段機(jī)制和分頁機(jī)制,以利于從邏輯的角度來理解硬件的實(shí)現(xiàn)機(jī)制蒋川。分段機(jī)制把一個邏輯地址轉(zhuǎn)換為線性地址牲芋;接著,分頁機(jī)制把一個線性地址轉(zhuǎn)換為物理地址捺球。

IA32的段寄存器

IA32中有六個16位段寄存器:CS, DS, SS, ES缸浦,F(xiàn)S, GS.跟8086的段寄存器不同的是,這些寄存器存放的不再是某個段的基地址氮兵,而是某個段的選擇符(Selector)裂逐。

分段機(jī)制的實(shí)現(xiàn)

段是虛擬地址空間的基本單位,分段機(jī)制必須把虛擬地址空間的一個地址轉(zhuǎn)換為線性地址空間的一個線性地址泣栈。

為了實(shí)現(xiàn)這種映射卜高,僅僅用段寄存器來確定一個基地址是不夠的,至少還得描述段的長度南片,并且還需要段的一些其他信息掺涛,比如訪問權(quán)之類。所以疼进,這里需要的是一個數(shù)據(jù)結(jié)構(gòu)薪缆,這個結(jié)構(gòu)包括三個方面的內(nèi)容:

  1. 段的基地址(Base Address):在線性地址空間中段的起始地址。
  2. 段的界限(Limit):在虛擬地址空間中颠悬,段內(nèi)可以使用的最大偏移量矮燎。
  3. 段的保護(hù)屬性(Attribute):表示段的特性定血。例如,該段是否可被讀出或?qū)懭氲猓蛘咴摱问欠褡鳛橐粋€程序來執(zhí)行澜沟,以及段的特權(quán)級等等。

上面的數(shù)據(jù)結(jié)構(gòu)我們稱為段描述符峡谊,多個段描述符組成的表稱為段描述符表

段描述符

所謂描述符(Descriptor)茫虽,就是描述段的屬性的一個8字節(jié)存儲單元。在實(shí)模式下既们,段的屬性不外乎是代碼段濒析、堆棧段、數(shù)據(jù)段啥纸、段的起始地址号杏、段的長度等等,而在保護(hù)模式下則復(fù)雜一些斯棒。IA32將它們結(jié)合在一起用一個8字節(jié)的數(shù)表示盾致,稱為描述符 。


從圖可以看出荣暮,一個段描述符指出了段的32位基地址和20位段界限(即段長)庭惜。這里我們只關(guān)注基地址和段界限,其他的屬性略過穗酥。

段描述符表

各種各樣的用戶描述符和系統(tǒng)描述符护赊,都放在對應(yīng)的全局描述符表、局部描述符表和中斷描述符表中砾跃。描述符表(即段表)定義了IA32系統(tǒng)的所有段的情況骏啰。所有的描述符表本身都占據(jù)一個字節(jié)為8的倍數(shù)的存儲器空間,空間大小在8個字節(jié)(至少含一個描述符)到64K字節(jié)(至多含8K)個描述符之間抽高。

  1. 全局描述符表(GDT)
    全局描述符表GDT(Global Descriptor Table)器一,除了任務(wù)門,中斷門和陷阱門描述符外厨内,包含著系統(tǒng)中所有任務(wù)都共用的那些段的描述符。 它的第一個8字節(jié)位置沒有使用渺贤。
  2. 中斷描述符表IDT(Interrupt Descriptor Table)
    中斷描述符表IDT(Interrupt Descriptor Table)雏胃,包含256個門描述符。IDT中只能包含任務(wù)門志鞍、中斷門和陷阱門描述符瞭亮,雖然IDT表最長也可以為64K字節(jié),但只能存取2K字節(jié)以內(nèi)的描述符固棚,即256個描述符统翩,這個數(shù)字是為了和8086保持兼容仙蚜。
  3. 局部描述符表(LDT)
    局部描述符表LDT(local Descriptor Table),包含了與一個給定任務(wù)有關(guān)的描述符厂汗,每個任務(wù)各自有一個的LDT委粉。 有了LDT,就可以使給定任務(wù)的代碼娶桦、 數(shù)據(jù)與別的任務(wù)相隔離贾节。每一個任務(wù)的局部描述符表LDT本身也用一個描述符來表示,稱為LDT描述符衷畦,它包含了有關(guān)局部描述符表的信息栗涂,被放在全局描述符表GDT中。

總結(jié)

IA32的內(nèi)存尋址機(jī)制完成從邏輯地址–線性地址–物理地址的轉(zhuǎn)換祈争。其中斤程,邏輯地址的段寄存器中的值提供段描述符,然后從段描述符中得到段基址和段界限菩混,然后加上邏輯地址的偏移量忿墅,就得到了線性地址,線性地址通過分頁機(jī)制得到物理地址墨吓。
首先球匕,我們要明確,分段機(jī)制是IA32提供的尋址方式帖烘,這是硬件層面的亮曹。就是說,不管你是windows還是linux秘症,只要使用IA32的CPU訪問內(nèi)存照卦,都要經(jīng)過MMU的轉(zhuǎn)換流程才能得到物理地址,也就是說必須經(jīng)過邏輯地址–線性地址–物理地址的轉(zhuǎn)換乡摹。

Linux中分段的實(shí)現(xiàn)

前面說了那么多關(guān)于分段機(jī)制的實(shí)現(xiàn)役耕,其實(shí),對于Linux來說聪廉,并沒有什么卵用瞬痘。因為,Linux基本不使用分段的機(jī)制板熊,或者說框全,Linux中的分段機(jī)制只是為了兼容IA32的硬件而設(shè)計的。

Intel微處理器的段機(jī)制是從8086開始提出的干签, 那時引入的段機(jī)制解決了從CPU內(nèi)部16位地址到20位實(shí)地址的轉(zhuǎn)換津辩。為了保持這種兼容性,386仍然使用段機(jī)制,但比以前復(fù)雜得多喘沿。因此闸度,Linux內(nèi)核的設(shè)計并沒有全部采用Intel所提供的段方案,僅僅有限度地使用了一下分段機(jī)制蚜印。這不僅簡化了Linux內(nèi)核的設(shè)計莺禁,而且為把Linux移植到其他平臺創(chuàng)造了條件,因為很多RISC處理器并不支持段機(jī)制晒哄。但是睁宰,對段機(jī)制相關(guān)知識的了解是進(jìn)入Linux內(nèi)核的必經(jīng)之路。

從2.2版開始寝凌,Linux讓所有的進(jìn)程(或叫任務(wù))都使用相同的邏輯地址空間柒傻,因此就沒有必要使用局部描述符表LDT。但內(nèi)核中也用到LDT较木,那只是在VM86模式中運(yùn)行Wine红符,因為就是說在Linux上模擬運(yùn)行Winodws軟件或DOS軟件的程序時才使用。

在 IA32 上任意給出的地址都是一個虛擬地址伐债,即任意一個地址都是通過“選擇符:偏移量”的方式給出的预侯,這是段機(jī)制存訪問模式的基本特點(diǎn)。所以在IA32上設(shè)計操作系統(tǒng)時無法回避使用段機(jī)制峰锁。一個虛擬地址最終會通過“段基地址+偏移量”的方式轉(zhuǎn)化為一個線性地址萎馅。 但是,由于絕大多數(shù)硬件平臺都不支持段機(jī)制虹蒋,只支持分頁機(jī)制糜芳,所以為了讓 Linux 具有更好的可移植性,我們需要去掉段機(jī)制而只使用分頁機(jī)制魄衅。但不幸的是峭竣,IA32規(guī)定段機(jī)制是不可禁止的,因此不可能繞過它直接給出線性地址空間的地址晃虫。萬般無奈之下皆撩,Linux的設(shè)計人員干脆讓段的基地址為0,而段的界限為4GB哲银,這時任意給出一個偏移量扛吞,則等式為“0+偏移量=線性地址”,也就是說“偏移量=線性地址”荆责。另外由于段機(jī)制規(guī)定“偏移量<4GB”喻粹,所以偏移量的范圍為0H~FFFFFFFFH,這恰好是線性地址空間范圍草巡,也就是說虛擬地址直接映射到了線性地址,我們以后所提到的虛擬地址和線性地址指的也就是同一地址∩胶看來查乒,Linux在沒有回避段機(jī)制的情況下巧妙地把段機(jī)制給繞過去了。

另外郁竟,由于IA32段機(jī)制還規(guī)定玛迄,必須為代碼段和數(shù)據(jù)段創(chuàng)建不同的段,所以Linux必須為代碼段和數(shù)據(jù)段分別創(chuàng)建一個基地址為0棚亩,段界限為4GB的段描述符蓖议。不僅如此,由于Linux內(nèi)核運(yùn)行在特權(quán)級0讥蟆,而用戶程序運(yùn)行在特權(quán)級別3勒虾,根據(jù)IA32段保護(hù)機(jī)制規(guī)定,特權(quán)級3的程序是無法訪問特權(quán)級為0的段的瘸彤,所以Linux必須為內(nèi)核用戶程序分別創(chuàng)建其代碼段和數(shù)據(jù)段修然。這就意味著Linux必須創(chuàng)建4個段描述符——特權(quán)級0的代碼段和數(shù)據(jù)段,特權(quán)級3的代碼段和數(shù)據(jù)段质况。

總結(jié)

分段機(jī)制是IA32架構(gòu)CPU的特色愕宋,并不是操作系統(tǒng)尋址方式的必然選擇。Linux為了跨平臺结榄,巧妙的繞開段機(jī)制中贝,主要使用分頁機(jī)制來尋址。

參考資料
《深入分析Linux內(nèi)核源碼》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末臼朗,一起剝皮案震驚了整個濱河市邻寿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌依溯,老刑警劉巖老厌,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異黎炉,居然都是意外死亡枝秤,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門慷嗜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來淀弹,“玉大人,你說我怎么就攤上這事庆械∞崩#” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵缭乘,是天一觀的道長沐序。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么策幼? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任邑时,我火速辦了婚禮,結(jié)果婚禮上特姐,老公的妹妹穿的比我還像新娘晶丘。我一直安慰自己,他們只是感情好唐含,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布浅浮。 她就那樣靜靜地躺著,像睡著了一般捷枯。 火紅的嫁衣襯著肌膚如雪滚秩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天铜靶,我揣著相機(jī)與錄音叔遂,去河邊找鬼。 笑死争剿,一個胖子當(dāng)著我的面吹牛已艰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蚕苇,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼哩掺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涩笤?” 一聲冷哼從身側(cè)響起嚼吞,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蹬碧,沒想到半個月后舱禽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡恩沽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年誊稚,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片罗心。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡里伯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出渤闷,到底是詐尸還是另有隱情疾瓮,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布飒箭,位于F島的核電站狼电,受9級特大地震影響蜒灰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肩碟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一卷员、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧腾务,春花似錦、人聲如沸削饵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窿撬。三九已至启昧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間劈伴,已是汗流浹背密末。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留跛璧,地道東北人严里。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像追城,于是被迫代替她去往敵國和親刹碾。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容