Linux中的內(nèi)存管理

Linux的內(nèi)存管理方式經(jīng)常會(huì)在面試時(shí)作為操作系統(tǒng)基礎(chǔ)被問道项炼。搞清楚這個(gè)問題的好處很多揭厚,近的話可以應(yīng)付面試,遠(yuǎn)的可以提高對(duì)于操作系統(tǒng)底層的認(rèn)識(shí)救崔,為程序的性能優(yōu)化打下基礎(chǔ)惶看。

我們對(duì)于計(jì)算機(jī)內(nèi)存,最直觀和簡(jiǎn)陋的概念就是機(jī)器的物理內(nèi)存六孵,程序都被放在物理內(nèi)存上執(zhí)行纬黎。物理內(nèi)存一般都有限制,比如說4G或者8G劫窒。

但是如果真的這樣直接的使用物理內(nèi)存會(huì)發(fā)生什么狀況本今?

1、進(jìn)程地址空間不能隔離

由于程序直接訪問的是物理內(nèi)存主巍,這個(gè)時(shí)候程序所使用的內(nèi)存空間不是隔離的冠息。惡意程序或者是木馬程序可以輕而易舉的破快其他的程序,系統(tǒng)的安全性也就得不到保障了孕索,這對(duì)用戶來說也是不能容忍的逛艰。

2、內(nèi)存使用的效率低

由于物理內(nèi)存一般都有限制搞旭,當(dāng)物理內(nèi)存不夠用時(shí)散怖,需要把暫時(shí)不需要運(yùn)行的程序放到磁盤上,試想將整個(gè)程序放入磁盤肄渗,我們知道IO操作比較耗時(shí)镇眷,所以這個(gè)過程效率將會(huì)十分低下。

3翎嫡、程序運(yùn)行的地址不能確定

程序每次需要運(yùn)行時(shí)欠动,都需要在內(nèi)存中非配一塊足夠大的空閑區(qū)域,而問題是這個(gè)空閑的位置是不能確定的惑申,這會(huì)帶來一些重定位的問題具伍,重定位的問題確定就是程序中引用的變量和函數(shù)的地址铆遭。

可以通過引入一個(gè)中間層來解決上面的問題。

現(xiàn)在的內(nèi)存管理方法就是在程序和物理內(nèi)存之間引入了虛擬內(nèi)存這個(gè)概念沿猜。虛擬內(nèi)存位于程序和物理內(nèi)存之間,程序只能看見虛擬內(nèi)存碗脊,再也不能直接訪問物理內(nèi)存啼肩。每個(gè)程序都有自己獨(dú)立的進(jìn)程地址空間,這樣就做到了進(jìn)程隔離衙伶。這里的進(jìn)程地址空間是指虛擬地址祈坠。顧名思義既然是虛擬地址,也就是虛的矢劲,不是現(xiàn)實(shí)存在的地址空間赦拘。

既然我們?cè)诔绦蚝臀锢淼刂房臻g之間增加了虛擬地址,那么就要解決怎么從虛擬地址映射到物理地址芬沉,因?yàn)槌绦蜃罱K肯定是運(yùn)行在物理內(nèi)存中的躺同,主要有分段和分頁(yè)兩種技術(shù)。

分段(Segmentation):這種方法是人們最開始使用的一種方法丸逸,基本思路是將程序所需要的內(nèi)存地址空間大小的虛擬空間映射到某個(gè)物理地址空間蹋艺。

段映射機(jī)制

每個(gè)程序都有自己的獨(dú)立虛擬的進(jìn)程地址空間。進(jìn)程的只能看到自己的虛擬地址空間黄刚,這就使得進(jìn)程和實(shí)際的物理地址解除耦合捎谨。兩塊大小相同的虛擬地址空間和實(shí)際物理地址空間一一映射,即虛擬地址空間中的每個(gè)字節(jié)對(duì)應(yīng)于實(shí)際地址空間中的每個(gè)字節(jié)憔维,這個(gè)映射過程由軟件來設(shè)置映射的機(jī)制涛救,實(shí)際的轉(zhuǎn)換由硬件來完成。

這種分段的機(jī)制解決了文章一開始提到的3個(gè)問題中的進(jìn)程地址空間隔離(1)和程序地址重定位(3)的問題业扒。(PS:既然隔離了检吆,那么緩沖區(qū)溢出為啥還能那么牛掰?答案最后講凶赁。)

程序A和程序B有自己獨(dú)立的虛擬地址空間咧栗,而且該虛擬地址空間被映射到了互相不重疊的物理地址空間,如果程序A訪問虛擬地址空間的地址不在0x00000000-0x00A00000這個(gè)范圍內(nèi)虱肄,那么內(nèi)核就會(huì)拒絕這個(gè)請(qǐng)求致板,所以它解決了隔離地址空間的問題。我們應(yīng)用程序A只需要關(guān)心其虛擬地址空間0x00000000-0x00A00000咏窿,而其被映射到哪個(gè)物理地址我們無需關(guān)心斟或,所以程序永遠(yuǎn)按照這個(gè)虛擬地址空間來放置變量、代碼集嵌,不需要重新定位萝挤。

分段機(jī)制解決了上面兩個(gè)問題御毅,是一個(gè)很大的進(jìn)步,但是對(duì)于內(nèi)存效率問題仍然無能為力怜珍。因?yàn)檫@種內(nèi)存映射機(jī)制仍然是以程序?yàn)閱挝欢饲?dāng)內(nèi)存不足時(shí)仍然需要將整個(gè)程序交換到磁盤,這樣內(nèi)存使用的效率仍然很低酥泛。事實(shí)上今豆,根據(jù)程序的局部性運(yùn)行原理,一個(gè)程序在運(yùn)行的過程當(dāng)中柔袁,在某個(gè)時(shí)間段內(nèi)呆躲,只有一小部分?jǐn)?shù)據(jù)會(huì)被經(jīng)常用到。所以我們需要更加小粒度的內(nèi)存分割和映射方法捶索,此時(shí)是否會(huì)想到Linux中的Buddy算法和slab內(nèi)存分配機(jī)制呢插掂,哈哈。另一種將虛擬地址轉(zhuǎn)換為物理地址的方法分頁(yè)機(jī)制應(yīng)運(yùn)而生了腥例。

分頁(yè)機(jī)制就是把內(nèi)存地址空間分為若干個(gè)很小的固定大小的頁(yè)辅甥,每一頁(yè)的大小由內(nèi)存決定,就像Linux中ext文件系統(tǒng)將磁盤分成若干個(gè)Block一樣院崇,這樣做是分別是為了提高內(nèi)存和磁盤的利用率肆氓。

Linux中一般頁(yè)的大小是4KB,我們把進(jìn)程的地址空間按頁(yè)分割底瓣,把常用的數(shù)據(jù)和代碼頁(yè)裝載到內(nèi)存中谢揪,不常用的代碼和數(shù)據(jù)保存在磁盤中,我們還是以一個(gè)例子來說明,如下圖:

分頁(yè)機(jī)制

我們可以看到進(jìn)程1和進(jìn)程2的虛擬地址空間都被映射到了不連續(xù)的物理地址空間內(nèi)捐凭。

有一天我們的連續(xù)物理地址空間不夠拨扶,但是不連續(xù)的地址空間很多,如果沒有這種技術(shù)茁肠,我們的程序就沒有辦法運(yùn)行,甚至他們共用了一部分物理地址空間患民,這就是共享內(nèi)存。

進(jìn)程1的虛擬頁(yè)VP2和VP3被交換到了磁盤中垦梆,在程序需要這兩頁(yè)的時(shí)候匹颤,Linux內(nèi)核會(huì)產(chǎn)生一個(gè)缺頁(yè)異常,然后異常管理程序會(huì)將其讀到內(nèi)存中托猩。

分頁(yè)機(jī)制的實(shí)現(xiàn)需要硬件的實(shí)現(xiàn)印蓖,這個(gè)硬件名字叫做MMU(Memory Management Unit),他就是專門負(fù)責(zé)從虛擬地址到物理地址轉(zhuǎn)換的京腥,也就是從虛擬頁(yè)找到物理頁(yè)赦肃。

有的時(shí)候,單個(gè)頁(yè)表無法表示所有內(nèi)存頁(yè)信息,我們還需要多級(jí)頁(yè)表的幫助才行他宛。(后面再講船侧。)

下面繼續(xù)聊聊進(jìn)程地址的概念,當(dāng)然都是基于Linux操作系統(tǒng)厅各。

進(jìn)程內(nèi)部通過分段的方式劃分了:數(shù)據(jù)段镜撩、代碼段。數(shù)據(jù)段又可以分為:靜態(tài)數(shù)據(jù)段队塘、棧琐鲁、堆。

由此有幾個(gè)地址需要講一下:

  • 邏輯地址:段基值確定它所在的段居于整個(gè)存儲(chǔ)空間的位置,偏移量確定它在段內(nèi)的位置,這種地址表示方式稱為邏輯地址人灼。機(jī)器語言指令中出現(xiàn)的內(nèi)存地址(&操作符),都是邏輯地址顾翼。

  • 線性地址:又叫虛擬地址投放,是一個(gè)32位無符號(hào)整數(shù),可以用來表示高達(dá)4GB的地址适贸,跟邏輯地址類似灸芳,它也是一個(gè)不真實(shí)的地址,如果邏輯地址是對(duì)應(yīng)的硬件平臺(tái)段式管理轉(zhuǎn)換前地址的話拜姿,那么線性地址則對(duì)應(yīng)了硬件頁(yè)式內(nèi)存的轉(zhuǎn)換前地址烙样。

  • 物理地址:用于內(nèi)存芯片級(jí)的單元尋址,與處理器和CPU連接的地址總線相對(duì)應(yīng)蕊肥。

CPU將一個(gè)虛擬內(nèi)存空間中的地址轉(zhuǎn)換為物理地址谒获,需要進(jìn)行兩步:首先將給定一個(gè)邏輯地址,CPU要利用其段式內(nèi)存管理單元壁却,先將為個(gè)邏輯地址轉(zhuǎn)換成一個(gè)線性地址批狱,再利用其頁(yè)式內(nèi)存管理單元,轉(zhuǎn)換為最終物理地址展东。

邏輯地址----段式內(nèi)存管理單元----線性地址----頁(yè)式內(nèi)存管理單元----物理地址

Linux中邏輯地址等于線性地址赔硫。為什么這么說呢?因?yàn)長(zhǎng)inux所有的段(用戶代碼段盐肃、用戶數(shù)據(jù)段爪膊、內(nèi)核代碼段、內(nèi)核數(shù)據(jù)段)的線性地址都是從 0x00000000 開始砸王,長(zhǎng)度4G推盛,這樣線性地址=邏輯地址+ 0x00000000,也就是說邏輯地址等于線性地址了处硬。

Linux主要以分頁(yè)的方式實(shí)現(xiàn)內(nèi)存管理小槐。

前面說了Linux中邏輯地址等于線性地址,那么線性地址怎么對(duì)應(yīng)到物理地址呢?這個(gè)大家都知道凿跳,那就是通過分頁(yè)機(jī)制件豌,具體的說,就是通過頁(yè)表查找來對(duì)應(yīng)物理地址控嗜。

準(zhǔn)確的說分頁(yè)是CPU提供的一種機(jī)制茧彤,Linux只是根據(jù)這種機(jī)制的規(guī)則,利用它實(shí)現(xiàn)了內(nèi)存管理疆栏。

分頁(yè)的基本原理是把內(nèi)存劃分成大小固定的若干單元曾掂,每個(gè)單元稱為一頁(yè)(page),每頁(yè)包含4k字節(jié)的地址空間(為簡(jiǎn)化分析壁顶,我們不考慮擴(kuò)展分頁(yè)的情況)珠洗。這樣每一頁(yè)的起始地址都是4k字節(jié)對(duì)齊的。為了能轉(zhuǎn)換成物理地址若专,我們需要給CPU提供當(dāng)前任務(wù)的線性地址轉(zhuǎn)物理地址的查找表许蓖,即頁(yè)表(page table)。注意调衰,為了實(shí)現(xiàn)每個(gè)任務(wù)的平坦的虛擬內(nèi)存膊爪,每個(gè)任務(wù)都有自己的頁(yè)目錄表和頁(yè)表

32位的線性地址被分成3個(gè)部分:最高10位 Directory 頁(yè)目錄表偏移量嚎莉,中間10位 Table是頁(yè)表偏移量米酬,最低12位Offset是物理頁(yè)內(nèi)的字節(jié)偏移量。

頁(yè)目錄表的大小為4k(剛好是一個(gè)頁(yè)的大星髀帷)赃额,包含1024項(xiàng),每個(gè)項(xiàng)4字節(jié)(32位)叫确,項(xiàng)目里存儲(chǔ)的內(nèi)容就是頁(yè)表的物理地址爬早。如果頁(yè)目錄表中的頁(yè)表尚未分配,則物理地址填0启妹。

頁(yè)表的大小也是4k筛严,同樣包含1024項(xiàng),每個(gè)項(xiàng)4字節(jié)饶米,內(nèi)容為最終物理頁(yè)的物理內(nèi)存起始地址桨啃。

每個(gè)活動(dòng)的任務(wù),必須要先分配給它一個(gè)頁(yè)目錄表檬输,并把頁(yè)目錄表的物理地址存入cr3寄存器照瘾。頁(yè)表可以提前分配好,也可以在用到的時(shí)候再分配丧慈。

以 mov 0x80495b0, %eax 中的地址為例分析一下線性地址轉(zhuǎn)物理地址的過程析命。

前面說到Linux中邏輯地址等于線性地址主卫,那么我們要轉(zhuǎn)換的線性地址就是0x80495b0。轉(zhuǎn)換的過程是由CPU自動(dòng)完成的鹃愤,Linux所要做的就是準(zhǔn)備好轉(zhuǎn)換所需的頁(yè)目錄表和頁(yè)表(假設(shè)已經(jīng)準(zhǔn)備好簇搅,給頁(yè)目錄表和頁(yè)表分配物理內(nèi)存的過程很復(fù)雜,后面再分析)软吐。

內(nèi)核先將當(dāng)前任務(wù)的頁(yè)目錄表的物理地址填入cr3寄存器瘩将。

線性地址 0x80495b0 轉(zhuǎn)換成二進(jìn)制后是 0000 1000 0000 0100 1001 0101 1011 0000,最高10位0000 1000 00的十進(jìn)制是32凹耙,CPU查看頁(yè)目錄表第32項(xiàng)姿现,里面存放的是頁(yè)表的物理地址。線性地址中間10位00 0100 1001 的十進(jìn)制是73肖抱,頁(yè)表的第73項(xiàng)存儲(chǔ)的是最終物理頁(yè)的物理起始地址备典。物理頁(yè)基地址加上線性地址中最低12位的偏移量,CPU就找到了線性地址最終對(duì)應(yīng)的物理內(nèi)存單元意述。

我們知道Linux中用戶進(jìn)程線性地址能尋址的范圍是0 - 3G熊经,那么是不是需要提前先把這3G虛擬內(nèi)存的頁(yè)表都建立好呢?一般情況下欲险,物理內(nèi)存是遠(yuǎn)遠(yuǎn)小于3G的,加上同時(shí)有很多進(jìn)程都在運(yùn)行匹涮,根本無法給每個(gè)進(jìn)程提前建立3G的線性地址頁(yè)表天试。Linux利用CPU的一個(gè)機(jī)制解決了這個(gè)問題。進(jìn)程創(chuàng)建后我們可以給頁(yè)目錄表的表項(xiàng)值都填0然低,CPU在查找頁(yè)表時(shí)喜每,如果表項(xiàng)的內(nèi)容為0,則會(huì)引發(fā)一個(gè)缺頁(yè)異常,進(jìn)程暫停執(zhí)行雳攘,Linux內(nèi)核這時(shí)候可以通過一系列復(fù)雜的算法給分配一個(gè)物理頁(yè)带兜,并把物理頁(yè)的地址填入表項(xiàng)中,進(jìn)程再恢復(fù)執(zhí)行吨灭。當(dāng)然進(jìn)程在這個(gè)過程中是被蒙蔽的刚照,它自己的感覺還是正常訪問到了物理內(nèi)存。

線性地址轉(zhuǎn)物理地址
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末喧兄,一起剝皮案震驚了整個(gè)濱河市无畔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌吠冤,老刑警劉巖浑彰,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蒿秦,死亡現(xiàn)場(chǎng)離奇詭異荔燎,居然都是意外死亡挡篓,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門侣肄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人肛度,你說我怎么就攤上這事郑现。” “怎么了循诉?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵横辆,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我茄猫,道長(zhǎng)狈蚤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任划纽,我火速辦了婚禮脆侮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘勇劣。我一直安慰自己靖避,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開白布比默。 她就那樣靜靜地躺著幻捏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪命咐。 梳的紋絲不亂的頭發(fā)上篡九,一...
    開封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音醋奠,去河邊找鬼榛臼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛窜司,可吹牛的內(nèi)容都是我干的沛善。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼塞祈,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼金刁!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起议薪,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤胀葱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后笙蒙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抵屿,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年捅位,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了轧葛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搂抒。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖尿扯,靈堂內(nèi)的尸體忽然破棺而出求晶,到底是詐尸還是另有隱情,我是刑警寧澤衷笋,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布芳杏,位于F島的核電站,受9級(jí)特大地震影響辟宗,放射性物質(zhì)發(fā)生泄漏爵赵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一泊脐、第九天 我趴在偏房一處隱蔽的房頂上張望空幻。 院中可真熱鬧,春花似錦容客、人聲如沸秕铛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)但两。三九已至,卻和暖如春供置,著一層夾襖步出監(jiān)牢的瞬間谨湘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工士袄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谎僻。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓娄柳,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親艘绍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赤拒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

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