進(jin)程(cheng)是內存的使用(yong)者(zhe),進(jin)程(cheng)(process)是一個程(cheng)序(program)的執(zhi)行實例,
程序包含的信息有:
- 二進制格式標識:每個程序文件都包含用于描述可執行文件格式的元信息(metainformation)。
- 機器語言指令:對程序算法進行編碼。
- 程序入口地址:標識程序開始執行時的起始指令位置。
- 數據:程序文件包含的變量初始值和程序使用的字面常量值(比如字符串)。
- 符號表及重定位表:描述程序中函數和變量的位置及名稱。這些表格有多種用途,其中包含調試和運行時的符號解析(動態鏈接)。
- 共享庫和動態鏈接信息:程序文件所包含的一些字段,列出了程序運行時需要使用的共享庫,以及加載共享庫的動態鏈接器的路徑名。
- 其它信息:程序文件還包含許多其它信息,用以描述如何創建進程。
對應(ying)進程(cheng)的使用的內存, 它有多個部分(段)組成, 如下圖

- 文本段(text):包含了進程運行的程序機器語言指令。文本段具有只讀屬性,以防止進程通過錯誤指針意外修改自身指令。因為多個進程可同時運行同一程序,所以又將文本段設為可共享,這樣,一份程序代碼的拷貝可以映射到所有這些進程的虛擬地址空間中。包含const全局變量
- 初始化數據段(data):包含顯示初始化的全局變量和靜態變量。當程序加載到內存時,從可執行文件中讀取這些變量的值。
- 未初始化數據段(bss):包含了未進行顯示初始化的全局變量和靜態變量。程序啟動之前,系統將本段內所有內存初始化為0。出于歷史原因,此段常被稱為BSS段,這源于老版本的匯編語言助記符“block started by symbol”。將經過初始化的全局變量和靜態變量與未初始化的全局變量和靜態變量分開存放,其主要原因在于程序在磁盤上存儲時,沒有必要為未經初始化的變量分配存儲空間。相反,可執行文件只需記錄未初始化數據段的位置及所需大小,直到運行時再由程序加載器來分配空間。
- 堆(heap):是可在運行時(為變量)動態進行內存分配的一塊區域。堆頂端稱為program break。進程申請內存從堆分配的
- 棧(stack):是一個動態增長和收縮的段,有棧幀(stack frames)組成。系統會為每個當前調用的函數分配一個棧幀。棧幀中存儲了函數的局部變量(所謂自動變量)、實參和返回值。
注意: register變(bian)量是放(fang)在寄存器(qi)上的,用完釋放(fang), 變(bian)量默(mo)認都(dou)是auto類型的
虛(xu)擬內存(cun)和物理(li)內存(cun)按(an)統一的頁大小管理(li), 這樣方便映射,通(tong)常一頁大小是4KB,如果使用(yong)了hugepage, 支持2M, 1G的頁。
任(ren)一(yi)時刻,每個(ge)(ge)程(cheng)序僅有部分頁(ye)(ye)需(xu)要(yao)駐(zhu)留(liu)在(zai)(zai)物理內(nei)存(cun)頁(ye)(ye)幀中,內(nei)核需(xu)要(yao)為每個(ge)(ge)進(jin)程(cheng)維(wei)護一(yi)張頁(ye)(ye)表(page table)。該頁(ye)(ye)表描(miao)述了(le)每頁(ye)(ye)在(zai)(zai)進(jin)程(cheng)虛擬(ni)地址空(kong)間(virtual address space)中的(de)(de)位置(可為進(jin)程(cheng)所用(yong)的(de)(de)所有虛擬(ni)內(nei)存(cun)頁(ye)(ye)面(mian)(mian)的(de)(de)集合)。頁(ye)(ye)表中的(de)(de)每個(ge)(ge)條(tiao)目要(yao)么指出(chu)一(yi)個(ge)(ge)虛擬(ni)頁(ye)(ye)面(mian)(mian)在(zai)(zai)RAM中的(de)(de)所在(zai)(zai)位置,要(yao)么表明(ming)其(qi)當前駐(zhu)留(liu)在(zai)(zai)磁(ci)盤上。如(ru)果申請內(nei)存(cun)或(huo)者要(yao)使用(yong)的(de)(de)內(nei)存(cun)不(bu)在(zai)(zai) 頁(ye)(ye)表映射的(de)(de)時候, 會(hui)觸發SIGSEGV或(huo)者頁(ye)(ye)面(mian)(mian)錯誤,內(nei)核掛起進(jin)程(cheng),映射物理內(nei)存(cun),或(huo)者沖sawp磁(ci)盤中將頁(ye)(ye)面(mian)(mian)載入到內(nei)存(cun)
對于4k的(de)page, 使(shi)用(yong)了4級頁(ye)(ye)表(biao)(biao)來節省頁(ye)(ye)表(biao)(biao)空(kong)間(jian)(如(ru)(ru)果使(shi)用(yong)1級頁(ye)(ye)表(biao)(biao),頁(ye)(ye)面大小(xiao)為4KB,整(zheng)個(ge)(ge)虛擬地(di)址空(kong)間(jian)為4GB,則每個(ge)(ge)進程需(xu)要包含1M個(ge)(ge)頁(ye)(ye)表(biao)(biao)項), 2M的(de)大頁(ye)(ye)內存需(xu)要3級頁(ye)(ye)表(biao)(biao), 如(ru)(ru)下圖(tu)是4級頁(ye)(ye)表(biao)(biao)

圖中CR3保存著進(jin)程頁(ye)目(mu)錄PGD的地址,不同(tong)的進(jin)程有(you)不同(tong)的頁(ye)目(mu)錄地址。進(jin)程切換時,操作(zuo)系統負責把頁(ye)目(mu)錄地址裝入(ru)CR3寄存器。
地址翻譯過程如下
- 對于給定的線性地址,根據線性地址的bit22~bit31作為頁目錄項索引值,在CR3所指向的頁目錄中找到一個頁目錄項。(1024項)
- 找到的頁目錄項對應著頁表,根據線性地址的bit12~bit21作為頁表項索引值,在頁表中找到一個頁表項。
- 找到的頁表項中包含著一個頁面的地址,線性地址的bit0~bit11作為頁內偏移值和找到的頁確定線性地址對應的物理地址。
TLB
CPU的(de)Memory management unit(MMU)cache了最近使用的(de)頁(ye)面(mian)映射。我(wo)們稱(cheng)之為translation lookaside buffer(TLB)。TLB是一個(ge)組相連的(de)cache。當一個(ge)虛擬地址需要轉換成物(wu)理地址時,首先搜索(suo)TLB。如(ru)果(guo)發現了匹(pi)配(pei)(TLB命中),那么直接返回物(wu)理地址并訪問(wen)。然而,如(ru)果(guo)沒有匹(pi)配(pei)項(TLB miss),那么就要從頁(ye)表中查找(zhao)匹(pi)配(pei)項,如(ru)果(guo)存在也要把結果(guo)寫回 TLB
虛擬(ni)內存管理是使進程的虛擬(ni)地址(zhi)空間(jian)與(yu)RAM物(wu)理地址(zhi)空間(jian)隔離開來,這帶來許多優點(dian):
- 進程與進程、進程與內核相互隔離,所以一個進程不能讀取或修改另一個進程或內核的內存。這是因為每個進程的頁表條目指向RAM(或交換區)中截然不同的物理頁面集合。
- 適當情況下,兩個或更多進程能夠共享內存。這是由于內核可以使不同進程的頁表條目指向相同的RAM頁。內存共享常發生于如下兩種場景:
- 執行同一程序的多個進程,可共享一份(只讀的)程序代碼副本。當多個程序執行相同的程序文件(或加載相同的共享庫)時,會隱式地實現這一類型的共享。
- 進程可以使用shmget()和mmap()系統調用顯示地請求與其他進程共享內存區。這么做是出于進程間通信的目的。
- 便于實現內存保護機制:也就是說,可以對頁表條目進行標記,以表示相關頁面內容是可讀、可寫、可執行亦或是這些保護措施的組合。多個進程共享RAM頁面時,允許每個進程對內存采取不同的保護措施。例如:一個進程可能以只讀方式訪問某頁面,而另一進程則以讀寫方式訪問同一頁面。
- 程序員和編譯器、鏈接器之類的工具無需關注程序在RAM中的物理布局。
- 因為需要駐留在內存中的僅是程序的一部分,所以程序的加載和運行都很快。而且,一個進程所占用的內存(即虛擬內存大小)能夠超出RAM的容量。
參考資料