一個(gè)典型的計(jì)算機(jī)系統(tǒng)如下圖所示:
直接讓應(yīng)用使用硬件可能會(huì)導(dǎo)致濫用,并且應(yīng)用需要處理復(fù)雜的硬件細(xì)節(jié),容易出錯(cuò)。所以我們引入了操作系統(tǒng)來(lái)管理硬件資源,如下圖所示:
操作系統(tǒng)為了讓應(yīng)用能更好更簡(jiǎn)單地使用硬件資源,對(duì)硬件資源做了進(jìn)一步抽象,如下圖所示:
虛擬存儲(chǔ)器把進(jìn)程訪問的存儲(chǔ)設(shè)備抽象成一個(gè)巨大的字節(jié)數(shù)組,并對(duì)每個(gè)字節(jié)做唯一的地址編碼。它提供了三個(gè)重要的功能:
虛擬存儲(chǔ)器在幕后自動(dòng)地工作,無(wú)需應(yīng)用程序員干涉,既然如此,為什么我們還需要去理解它呢?我想理解它可以帶來(lái)以下幾點(diǎn)好處:
進(jìn)程看到是虛擬地址,但是信息是存在物理內(nèi)存上的,那么系統(tǒng)是如何用虛擬地址來(lái)獲取對(duì)應(yīng)物理內(nèi)存的字節(jié)信息的呢?簡(jiǎn)單來(lái)說(shuō),可以分為三步:
具體過程如下圖:
MMU是如何把虛擬地址翻譯為物理地址的呢?
OS會(huì)把物理內(nèi)存、虛擬內(nèi)存分為同樣大小的塊(linux默認(rèn)為4k),并稱之為頁(yè)。同時(shí)為每個(gè)進(jìn)程分配頁(yè)表,頁(yè)表是一個(gè)頁(yè)表?xiàng)l目(PTE)數(shù)組,其中每個(gè)PTE記錄了虛擬頁(yè)與物理頁(yè)的映射關(guān)系。
一個(gè)虛擬地址可以分為兩部分:虛擬頁(yè)號(hào)×××和虛擬頁(yè)偏移量VPO。由于虛擬頁(yè)與物理頁(yè)是同樣大小,因此虛擬頁(yè)偏移量就是物理頁(yè)偏移量;虛擬頁(yè)號(hào)是頁(yè)表中PTE的索引,對(duì)應(yīng)的PTE中存儲(chǔ)著物理頁(yè)號(hào)和有效位(表示頁(yè)面是否有對(duì)應(yīng)物理頁(yè)),這樣MMU通過查詢PTE就可以找到虛擬頁(yè)對(duì)應(yīng)的物理頁(yè),再加上虛擬頁(yè)偏移量就可以得到物理地址,如下圖:
如果每個(gè)進(jìn)程只有一個(gè)頁(yè)表(假設(shè)物理頁(yè)大小為4k),那么對(duì)于32位系統(tǒng),需要占用4M內(nèi)存(每個(gè)PTE是4字節(jié));對(duì)于64位系統(tǒng)(實(shí)際只用了48位用來(lái)尋址),則需要256G內(nèi)存,實(shí)在是太大了。為了解決這個(gè)問題,我們用多級(jí)頁(yè)表,如下圖:
在多級(jí)頁(yè)表中,所有級(jí)別的頁(yè)表大小是一樣的,我們以linux的4級(jí)頁(yè)表為例,則最少要4個(gè)頁(yè)表,假設(shè)一個(gè)頁(yè)表4k,總共16k;隨著進(jìn)程消耗內(nèi)存的增長(zhǎng),第k級(jí)頁(yè)表數(shù)目隨之線性增長(zhǎng),由于其他級(jí)別的頁(yè)表數(shù)目遠(yuǎn)遠(yuǎn)小于k級(jí)頁(yè)表,因此總頁(yè)表消耗內(nèi)存頁(yè)頁(yè)接近于線性增長(zhǎng)。由于進(jìn)程實(shí)際占用內(nèi)存大小遠(yuǎn)小于256T,因此頁(yè)表消耗內(nèi)存遠(yuǎn)小于一級(jí)頁(yè)表。
從上述小結(jié),我們知道每個(gè)進(jìn)程都有一個(gè)獨(dú)立的虛擬存儲(chǔ)器空間,那么其布局是否有規(guī)律呢?我們以linux下的64位進(jìn)程舉例,見下圖:
linux將用戶虛擬存儲(chǔ)器組織成一些段的集合。一個(gè)段就是已分配的虛擬存儲(chǔ)器的連續(xù)片。只有存在于段的虛擬存儲(chǔ)器頁(yè)是可以被進(jìn)程訪問的。
#include <stdlib.h>
int main()
{
char *p = (char*)malloc(1);
while(1);
return 0;
}
編譯上述代碼并運(yùn)行,通過top獲取此進(jìn)程PID后,我們可以打開/proc/PID/maps文件查看進(jìn)程的內(nèi)存布局:
00400000-00401000 r-xp 00000000 fd:01 723899 /home/wld/test/a.out
00600000-00601000 r--p 00000000 fd:01 723899 /home/wld/test/a.out
00601000-00602000 rw-p 00001000 fd:01 723899 /home/wld/test/a.out
0148c000-014ad000 rw-p 00000000 00:00 0 [heap]
7fb917267000-7fb917425000 r-xp 00000000 fd:01 1731435 /lib/x86_64-linux-gnu/libc-2.19.so
7fb917425000-7fb917625000 ---p 001be000 fd:01 1731435 /lib/x86_64-linux-gnu/libc-2.19.so
7fb917625000-7fb917629000 r--p 001be000 fd:01 1731435 /lib/x86_64-linux-gnu/libc-2.19.so
7fb917629000-7fb91762b000 rw-p 001c2000 fd:01 1731435 /lib/x86_64-linux-gnu/libc-2.19.so
7fb91762b000-7fb917630000 rw-p 00000000 00:00 0
7fb917630000-7fb917653000 r-xp 00000000 fd:01 1731443 /lib/x86_64-linux-gnu/ld-2.19.so
7fb917835000-7fb917838000 rw-p 00000000 00:00 0
7fb917850000-7fb917852000 rw-p 00000000 00:00 0
7fb917852000-7fb917853000 r--p 00022000 fd:01 1731443 /lib/x86_64-linux-gnu/ld-2.19.so
7fb917853000-7fb917854000 rw-p 00023000 fd:01 1731443 /lib/x86_64-linux-gnu/ld-2.19.so
7fb917854000-7fb917855000 rw-p 00000000 00:00 0
7ffe8b3e1000-7ffe8b402000 rw-p 00000000 00:00 0 [stack]
7ffe8b449000-7ffe8b44b000 r--p 00000000 00:00 0 [vvar]
7ffe8b44b000-7ffe8b44d000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
上面每一行表示一個(gè)段,每個(gè)段的有6列,各列含義如下:
假如MMU在嘗試翻譯某個(gè)虛擬地址A時(shí),沒有對(duì)應(yīng)的物理地址,則會(huì)觸發(fā)了一個(gè)缺頁(yè)異常。這個(gè)異常會(huì)導(dǎo)致控制轉(zhuǎn)移到內(nèi)核的缺頁(yè)異常處理程序,處理程序隨后執(zhí)行如下步驟:
通過執(zhí)行以下兩種的任意一種命令可查看某個(gè)進(jìn)程的缺頁(yè)中斷信息
ps -o majflt,minflt -C program_name
ps -o majflt,minflt -p pid
majflt和minor這兩個(gè)數(shù)值表示一個(gè)進(jìn)程自啟動(dòng)以來(lái)所發(fā)生的缺頁(yè)中斷的次數(shù)。
其中majflt與minflt的不同是,majflt表示需要讀寫磁盤,可能是內(nèi)存對(duì)應(yīng)頁(yè)面在磁盤中需要load到物理內(nèi)存中,也可能是此時(shí)物理內(nèi)存不足,需要淘汰部分物理頁(yè)面至磁盤中。
linux通過將虛擬內(nèi)地段與一個(gè)磁盤上的文件關(guān)聯(lián)起來(lái),以初始化這個(gè)虛擬存儲(chǔ)器段的內(nèi)容,這個(gè)過程稱之為內(nèi)存映射(memory mapping)。內(nèi)存映射有兩種:
###6.1 共享對(duì)象
內(nèi)存映射可以讓我們簡(jiǎn)單高效地把程序和數(shù)據(jù)加載到虛擬存儲(chǔ)器空間中。在實(shí)際中,許多進(jìn)程會(huì)映射同一個(gè)文件到內(nèi)存中,比如glic動(dòng)態(tài)庫(kù),如果物理內(nèi)存中存在多份,那就是極端的浪費(fèi)。我們可以通過共享對(duì)象技術(shù)來(lái)消除浪費(fèi)。
對(duì)于私有對(duì)象,我們可以用寫時(shí)拷貝技術(shù)來(lái)共享物理內(nèi)存頁(yè)。
類unix操作系統(tǒng)下的動(dòng)態(tài)內(nèi)存分配器有很多,比如ptmalloc(linux默認(rèn)),tcmalloc(google出品),jemalloc(FreeBSD、NetBSD和firefox默認(rèn))。這三種分配器的詳細(xì)介紹可以參考http://www.360doc.com/content/13/0915/09/8363527_314549128.shtml。
本文以ptmalloc為例介紹動(dòng)態(tài)內(nèi)存分配。在linux下os提供兩種動(dòng)態(tài)內(nèi)存分配brk和mmap。ptmalloc對(duì)于申請(qǐng)內(nèi)存小于128k的采用brk方式,大于128k的采用mmap方式。
對(duì)于大內(nèi)存,malloc會(huì)直接調(diào)用系統(tǒng)函數(shù)mmap分配內(nèi)存,以物理頁(yè)為最小單位做對(duì)齊。free會(huì)直接調(diào)用系統(tǒng)函數(shù)munmap釋放內(nèi)存。
進(jìn)程有一個(gè)指針指向堆的頂部的地址,通過系統(tǒng)函數(shù)brk可以改變這個(gè)指針的位置,從而改變堆的大?。ǘ芽梢詳U(kuò)大也可以收縮)。當(dāng)已有的堆不能分配內(nèi)存時(shí),brk會(huì)擴(kuò)大堆來(lái)分配動(dòng)態(tài)內(nèi)存。當(dāng)頂部的內(nèi)存被釋放,切釋放內(nèi)存大于128k,brk就會(huì)收縮堆,如下圖:
從上面的堆分配釋放方式,我們知道實(shí)際上很多小內(nèi)存申請(qǐng)后是不會(huì)馬上釋放給OS,為了將這些內(nèi)存重復(fù)利用,內(nèi)存分配器需要由一個(gè)算法,下面介紹下ptmalloc是如何處理的。
ptmalloc通過chunk的數(shù)據(jù)結(jié)構(gòu)來(lái)組織每個(gè)內(nèi)存單元。當(dāng)我們使用malloc分配得到一塊內(nèi)存的時(shí)候,這塊內(nèi)存就會(huì)通過chunk的形式被記錄到glibc上并且管理起來(lái)。你可以把它想象成自己寫內(nèi)存池的時(shí)候的一個(gè)內(nèi)存數(shù)據(jù)結(jié)構(gòu)。chunk的結(jié)構(gòu)可以分為使用中的chunk和空閑的chunk。使用中的chunk和空閑的chunk數(shù)據(jù)結(jié)構(gòu)基本項(xiàng)同,但是會(huì)有一些設(shè)計(jì)上的小技巧,巧妙的節(jié)省了內(nèi)存。
使用中的chunk:
空閑的chunk結(jié)構(gòu)會(huì)復(fù)用User data來(lái)保存雙向鏈表指針。
ptmalloc一共維護(hù)了128bin。每個(gè)bins都維護(hù)了大小相近的雙向鏈表的chunk。
通過上圖這個(gè)bins的列表就能看出,當(dāng)用戶調(diào)用malloc的時(shí)候,能很快找到用戶需要分配的內(nèi)存大小是否在維護(hù)的bin上,如果在某一個(gè)bin上,就可以通過雙向鏈表去查找合適的chunk內(nèi)存塊給用戶使用。
造成堆利用率低的主要原因是碎片,當(dāng)雖然有未使用的內(nèi)存但不能用來(lái)滿足分配請(qǐng)求時(shí),就會(huì)發(fā)生這種現(xiàn)象。有兩種形式的碎片:
####提問1:請(qǐng)問下面代碼運(yùn)行后,OS會(huì)立即分配1G物理內(nèi)存嗎?
#include <cstdlib>
int main()
{
char *p = (char*)malloc(1024*1024*1024);
while(1);
return 0;
}
###提問2:請(qǐng)問下面代碼運(yùn)行后,OS會(huì)分配多少物理內(nèi)存?
#include <cstdlib>
#include <cstring>
int main()
{
const size_t MAX_LEN = 1024*1024*1024;
char *p = (char*)malloc(MAX_LEN);
memset(p, 0, MAX_LEN/2);
while(1);
return 0;
}
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。
分享文章:漫游計(jì)算機(jī)系統(tǒng)之虛擬存儲(chǔ)器-創(chuàng)新互聯(lián)
URL分享:http://muchs.cn/article22/ddhgjc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供App設(shè)計(jì)、網(wǎng)頁(yè)設(shè)計(jì)公司、自適應(yīng)網(wǎng)站、電子商務(wù)、ChatGPT、企業(yè)網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容