物理地址與虛擬地址
-
物理地址
,也叫實(shí)地址勺良、二進(jìn)制地址,它是在地址總線上骄噪,以電子形式存在的尚困,使得數(shù)據(jù)總線可以訪問(wèn)主存的某個(gè)特定存儲(chǔ)單元的內(nèi)存地址。 - 在早期的計(jì)算機(jī)中链蕊,進(jìn)程直接訪問(wèn)物理內(nèi)存事甜,但這種策略帶來(lái)了如下問(wèn)題:
- 內(nèi)存使用效率低谬泌。有內(nèi)存碎片的問(wèn)題。
- 進(jìn)程地址空間不隔離逻谦。一個(gè)進(jìn)程中的代碼可以更改正在由另一進(jìn)程或操作系統(tǒng)使用的物理內(nèi)存掌实。
- 運(yùn)行的地址不確定。由于操作系統(tǒng)給進(jìn)程分配的物理地址是不確定的邦马,而某些硬件是需要在固定的地址才能運(yùn)行贱鼻。
- 因此現(xiàn)代操作系統(tǒng)在進(jìn)程和物理地址間加入了
虛擬地址
來(lái)解決以上問(wèn)題。 - CPU芯片中的
內(nèi)存管理單元(MMU)
負(fù)責(zé)將虛擬地址映射為物理地址滋将。 - 操作系統(tǒng)采用
分段
和分頁(yè)
兩種方式來(lái)管理虛擬地址和物理地址間的關(guān)系忱嘹。分段可以給每一個(gè)進(jìn)程分配不同的線性地址空間
,分頁(yè)可以把同一線性地址空間映射到不同的物理空間
耕渴。
分段機(jī)制
分段機(jī)制
-
分段(Segmentation)機(jī)制
就是把虛擬地址空間中的虛擬內(nèi)存組織成一些長(zhǎng)度可變的稱為段的內(nèi)存塊單元。不同的段有不同的屬性齿兔,分別用于存放程序的代碼橱脸、數(shù)據(jù)和堆棧,或者系統(tǒng)數(shù)據(jù)結(jié)構(gòu)等分苇。
段
- 每個(gè)段是由一個(gè)8字節(jié)的
段描述符(Segment Descriptor)
表示添诉。它的參數(shù)包括:
段首字節(jié)的線性地址(Base),段最后一個(gè)內(nèi)存單元的偏移量医寿,即段的長(zhǎng)度(Limit)栏赴,段類型(Type),段是否在主存中(P),是系統(tǒng)段還是代碼或數(shù)據(jù)段(S)靖秩,特權(quán)級(jí)(DPL)等须眷。 - 段描述符放在
全局描述符表(GDT)
或局部描述符表(LDT)
中 - GDT在主存中的地址和大小存放在gdtr控制寄存器中,當(dāng)前正被使用的LDT地址和大小放在ldtr控制寄存器中沟突。
段選擇符
-
邏輯地址
由16位的段選擇符(Segment Selector)
和32位偏移量(offset)
兩部分組成花颗。 - 段選擇符是段的唯一標(biāo)識(shí),任意段選擇符包含3個(gè)字段:
index
:指定了放在GDT或LDT中的相應(yīng)段描述符的索引
TI
: 指明段描述符實(shí)在GDT(TI=0)或在LDT(TI=1)中
RPL
: 請(qǐng)求者權(quán)限級(jí)
邏輯地址到線性地址的轉(zhuǎn)換
- 通過(guò)邏輯地址中 段選擇符的TI 判斷描述符是在GDT還是LDT中
- 段描述符地址 = GDT/LDT首地址 + index * 8
- 線性地址 = 段描述符地址 + 偏移量
Linux中的分段
- Linux中所有段的段基地址Base都是0X00000000惠拭,Limit都是0xffffffff扩劝。因此Linux下邏輯地址與線性地址是一致的,即邏輯偏移量的值與對(duì)應(yīng)線性地址的值一致职辅。某種意義上繞過(guò)了分段機(jī)制棒呛。
- 所有進(jìn)程因此共享同一組線性地址。
分頁(yè)機(jī)制
分頁(yè)機(jī)制
-
分頁(yè)(Paging)機(jī)制
在分段機(jī)制之后進(jìn)行的域携,它進(jìn)一步將線性地址轉(zhuǎn)換為物理地址簇秒。 - 分頁(yè)把線性地址分成以固定長(zhǎng)度為單位的組,這樣一個(gè)連續(xù)并且尺寸固定的內(nèi)存空間稱為
頁(yè)(Page)
涵亏。也把物理地址頁(yè)分成以固定長(zhǎng)度為單位的組宰睡,稱為物理頁(yè)
或頁(yè)框(page frame)
蒲凶。在 Linux 下,每一頁(yè)和物理頁(yè)的大小為4KB
拆内。 - Linux對(duì)進(jìn)程的處理很大程度上依賴于分頁(yè)旋圆,實(shí)上,線性地址到物理地址的自動(dòng)轉(zhuǎn)換使下面的設(shè)計(jì)目標(biāo)變得可行:
- 給每一個(gè)進(jìn)程分配一塊不同的物理地址空間麸恍,這確保了可以有效地防止尋址錯(cuò)誤灵巧。
- 區(qū)別頁(yè)(即一組數(shù)據(jù))和頁(yè)框(即主存中的物理地址)之不同。這就允許存放在某個(gè)頁(yè)框中的一個(gè)頁(yè)抹沪,然后保存到磁盤上刻肄,以后重新裝入這同一頁(yè)時(shí)又被裝在不同的頁(yè)框中。
頁(yè)表
- 把線性地址映射到物理地址的數(shù)據(jù)結(jié)構(gòu)稱為
頁(yè)表(page table)
敏弃。頁(yè)表存放在主存中,并在啟用分頁(yè)單元之前必須由內(nèi)核對(duì)頁(yè)表進(jìn)行適當(dāng)?shù)某跏蓟?/li>
但這種方式會(huì)帶來(lái)空間上的缺陷噪馏,例如在32位的環(huán)境下麦到,虛擬地址空間共有4GB,包括 4GB/4KB = 2^20欠肾,約100萬(wàn)個(gè)頁(yè)瓶颠,頁(yè)表中每個(gè)映射項(xiàng)需要 4 個(gè)字節(jié)大小來(lái)存儲(chǔ),整個(gè)頁(yè)表就需要4MB內(nèi)存空間刺桃。而每個(gè)進(jìn)程都是有自己的虛擬地址空間的粹淋,也就說(shuō)都有自己的頁(yè)表。這就需要很多的內(nèi)存了瑟慈。由于頁(yè)表本身需要在內(nèi)存中是連續(xù)分布的桃移,而且即便沒(méi)有使用到的頁(yè),也會(huì)占用一個(gè)entry葛碧。
為解決該問(wèn)題谴轮, 多級(jí)分頁(yè)模型被提出:
- 一般來(lái)說(shuō)進(jìn)程實(shí)際使用的頁(yè)只占4G地址空間中的一小部分,其分布是稀疏的吹埠。一級(jí)頁(yè)表就可以覆蓋整個(gè) 4GB 虛擬地址空間第步,但如果某個(gè)一級(jí)頁(yè)表的頁(yè)表項(xiàng)沒(méi)有被用到,也就不需要?jiǎng)?chuàng)建這個(gè)頁(yè)表項(xiàng)對(duì)應(yīng)的二級(jí)頁(yè)表了缘琅。
- 把二級(jí)分頁(yè)再推廣到多級(jí)頁(yè)表粘都,頁(yè)表占用的內(nèi)存空間就能更少了
Linux四級(jí)分頁(yè)
- Linux采用了四級(jí)分頁(yè)模型,4種頁(yè)表為:
- 頁(yè)全局目錄(Page Global Directory):包含若干頁(yè)上級(jí)目錄的地址刷袍;
- 頁(yè)上級(jí)目錄(Page Upper Directory):包含若干頁(yè)中間目錄的地址翩隧;
- 頁(yè)中間目錄(Page Middle Directory):包含若干頁(yè)表的地址
- 頁(yè)表(Page Table):每一個(gè)頁(yè)表項(xiàng)指向一個(gè)頁(yè)框
線性地址因此被分成五個(gè)部分:
進(jìn)程頁(yè)表
- 進(jìn)程地址空間由進(jìn)程可尋址的虛擬內(nèi)存組成。每一個(gè)進(jìn)程有一個(gè)32位或64位的獨(dú)立連續(xù)空間呻纹《焉空間的具體大小取決于體系結(jié)構(gòu)专缠。
- 進(jìn)程只能訪問(wèn)有效內(nèi)存區(qū)域內(nèi)的內(nèi)存地址。如果一個(gè)進(jìn)程訪問(wèn)了不在有效范圍中的內(nèi)存區(qū)域淑仆,或以不正確的方式訪問(wèn)了有效地址涝婉,內(nèi)核將會(huì)終止該進(jìn)程,并返回“段錯(cuò)誤”信息蔗怠。
- x86 32 位系統(tǒng)里墩弯,一個(gè)進(jìn)程擁的線性地址空間被分成兩部分:
- 從0×00000000到0xbfffffff的線性地址,無(wú)論進(jìn)程運(yùn)行在用戶態(tài)還是內(nèi)核態(tài)都可以尋址寞射。
- 從0xc0000000到0xffffffff的線性地址渔工,只有內(nèi)核態(tài)的進(jìn)程才能尋址。
即進(jìn)程的第4個(gè)G保留給內(nèi)核桥温,前3個(gè)G可供內(nèi)核和用戶程序同時(shí)訪問(wèn)引矩。
- Linux采用
請(qǐng)求調(diào)頁(yè)(demand paging)
的內(nèi)存分配策略。讓進(jìn)程可以在它的頁(yè)還沒(méi)有在內(nèi)存時(shí)就開(kāi)始執(zhí)行侵浸。當(dāng)進(jìn)程訪問(wèn)一個(gè)不存在的頁(yè)時(shí)脓魏,MMU產(chǎn)生一個(gè)異常。異常處理程序找到受影響的內(nèi)存區(qū)域通惫,分配一個(gè)空閑的頁(yè),并用適當(dāng)?shù)臄?shù)據(jù)把它初始化混蔼。同理履腋,進(jìn)程調(diào)用malloc()或者brk()系統(tǒng)調(diào)用動(dòng)態(tài)的請(qǐng)求內(nèi)存時(shí),內(nèi)核僅僅修改進(jìn)程的堆內(nèi)存區(qū)的大小惭嚣,只有試圖引用進(jìn)程的虛擬內(nèi)存地址而發(fā)生異常時(shí)遵湖,才給進(jìn)程真正分配頁(yè)框。 - 當(dāng)一個(gè)父進(jìn)程創(chuàng)建子進(jìn)程時(shí)晚吞,采用了
寫時(shí)復(fù)制
的內(nèi)存分配策略延旧。當(dāng)一個(gè)新進(jìn)程被創(chuàng)建時(shí),父進(jìn)程僅僅把父進(jìn)程的頁(yè)框賦給子進(jìn)程的地址空間槽地,并把這些頁(yè)框標(biāo)記為只讀迁沫,一旦父或子進(jìn)程試圖修改頁(yè)中的內(nèi)容時(shí),一個(gè)異常就會(huì)產(chǎn)生捌蚊。異常處理程序把新頁(yè)框賦給受影響的進(jìn)程集畅,并用原來(lái)頁(yè)中的內(nèi)容初始化新頁(yè)。