物理虛擬內(nèi)存
物理虛擬內(nèi)存
有網(wǎng)友問(wèn)到小編物理虛擬內(nèi)存是什么,那么物理虛擬內(nèi)存到底是什么呢?學(xué)習(xí)啦小編為大家介紹下。供大家參考!
物理虛擬內(nèi)存
前一段時(shí)間在面試總監(jiān)的時(shí)候,總監(jiān)問(wèn)了我這樣的一個(gè)問(wèn)題:你跟我說(shuō)說(shuō)物理內(nèi)存和虛擬內(nèi)存到底是怎么一回事?
其實(shí)之前我看過(guò)這個(gè)問(wèn)題,據(jù)我理解的,當(dāng)時(shí)是這么回答的“進(jìn)程在運(yùn)行的時(shí)候,操作系統(tǒng)都為其分配一個(gè)4GB的地址空間,即所謂的虛擬地址空間,一般情況下,當(dāng)我們的程序很大的時(shí)候,實(shí)際的物理內(nèi)存根本不能滿足我們的需求的時(shí)候,這個(gè)時(shí)候操作系統(tǒng)就會(huì)借助磁盤(pán)空間來(lái)做虛擬的內(nèi)存空間,把當(dāng)前進(jìn)程不需要的數(shù)據(jù)放在磁盤(pán)上,等到用到的時(shí)候,在利用調(diào)度算法把所需要的數(shù)據(jù)從磁盤(pán)空間上調(diào)度到內(nèi)存,虛擬內(nèi)存就是為了擴(kuò)大內(nèi)存的容量,每當(dāng)我們要運(yùn)行一個(gè)程序的時(shí)候經(jīng)過(guò)編譯以后形成的僅僅是邏輯上的空間,根本不是可以直接運(yùn)行的內(nèi)存空間,所以它還存在一個(gè)地址映射的概念。”當(dāng)時(shí)感覺(jué)回答的很是籠統(tǒng),只見(jiàn)總監(jiān)在最后說(shuō)了一句,你下去還是把這一塊的內(nèi)容在好好看看,所以今天就好好的把這個(gè)概念理一理。
首先我從最基本的概念說(shuō)起,什么是物理內(nèi)存的概念,虛擬內(nèi)存的概念?
物理內(nèi)存,在應(yīng)用中,自然是顧名思義,物理上,真實(shí)的插在板子上的內(nèi)存是多大就是多大了。而在CPU中的概念,物理內(nèi)存就是CPU的地址線可以直接進(jìn)行尋址的內(nèi)存空間大小。比如8086只有20根地址線,那么它的尋址空間就是1MB,我們就說(shuō)8086能支持1MB的物理內(nèi)存,及時(shí)我們安裝了128M的內(nèi)存條在板子上,我們也只能說(shuō)8086擁有1MB的物理內(nèi)存空間。同理我們現(xiàn)在大部分使用的是32位的機(jī)子,32位的386以上CPU就可以支持最大4GB的物理內(nèi)存空間了。
先說(shuō)說(shuō)為什么會(huì)有虛擬內(nèi)存和物理內(nèi)存的區(qū)別。正在運(yùn)行的一個(gè)進(jìn)程,他所需的內(nèi)存是有可能大于內(nèi)存條容量之和的,比如你的內(nèi)存條是256M,你的程序卻要?jiǎng)?chuàng)建一個(gè)2G的數(shù)據(jù)區(qū),那么不是所有數(shù)據(jù)都能一起加載到內(nèi)存(物理內(nèi)存)中,勢(shì)必有一部分?jǐn)?shù)據(jù)要放到其他介質(zhì)中(比如硬盤(pán)),待進(jìn)程需要訪問(wèn)那部分?jǐn)?shù)據(jù)時(shí),在通過(guò)調(diào)度進(jìn)入物理內(nèi)存。所以,虛擬內(nèi)存是進(jìn)程運(yùn)行時(shí)所有內(nèi)存空間的總和,并且可能有一部分不在物理內(nèi)存中,而物理內(nèi)存就是我們平時(shí)所了解的內(nèi)存條。有的地方呢,也叫這個(gè)虛擬內(nèi)存為內(nèi)存交換區(qū)。關(guān)鍵的是不要把虛擬內(nèi)存跟真實(shí)的插在主板上的內(nèi)存條相掛鉤,虛擬內(nèi)存它是“虛擬的”不存在,假的啦,它只是內(nèi)存管理的一種抽象!
那么,什么是虛擬內(nèi)存地址和物理內(nèi)存地址呢。假設(shè)你的計(jì)算機(jī)是32位,那么它的地址總線是32位的,也就是它可以尋址0~0xFFFFFFFF(4G)的地址空間,但如果你的計(jì)算機(jī)只有256M的物理內(nèi)存0x~0x0FFFFFFF(256M),同時(shí)你的進(jìn)程產(chǎn)生了一個(gè)不在這256M地址空間中的地址,那么計(jì)算機(jī)該如何處理呢?回答這個(gè)問(wèn)題前,先說(shuō)明計(jì)算機(jī)的內(nèi)存分頁(yè)機(jī)制。
計(jì)算機(jī)會(huì)對(duì)虛擬內(nèi)存地址空間(32位為4G)分頁(yè)產(chǎn)生頁(yè)(page),對(duì)物理內(nèi)存地址空間(假設(shè)256M)分頁(yè)產(chǎn)生頁(yè)幀(page frame),這個(gè)頁(yè)和頁(yè)幀的大小是一樣大的,所以呢,在這里,虛擬內(nèi)存頁(yè)的個(gè)數(shù)勢(shì)必要大于物理內(nèi)存頁(yè)幀的個(gè)數(shù)。在計(jì)算機(jī)上有一個(gè)頁(yè)表(page table),就是映射虛擬內(nèi)存頁(yè)到物理內(nèi)存頁(yè)的,更確切的說(shuō)是頁(yè)號(hào)到頁(yè)幀號(hào)的映射,而且是一對(duì)一的映射。但是問(wèn)題來(lái)了,虛擬內(nèi)存頁(yè)的個(gè)數(shù) > 物理內(nèi)存頁(yè)幀的個(gè)數(shù),豈不是有些虛擬內(nèi)存頁(yè)的地址永遠(yuǎn)沒(méi)有對(duì)應(yīng)的物理內(nèi)存地址空間?不是的,操作系統(tǒng)是這樣處理的。操作系統(tǒng)有個(gè)頁(yè)面失效(page fault)功能。操作系統(tǒng)找到一個(gè)最少使用的頁(yè)幀,讓他失效,并把它寫(xiě)入磁盤(pán),隨后把需要訪問(wèn)的頁(yè)放到頁(yè)幀中,并修改頁(yè)表中的映射,這樣就保證所有的頁(yè)都有被調(diào)度的可能了。這就是處理虛擬內(nèi)存地址到物理內(nèi)存的步驟。
現(xiàn)在來(lái)回答什么是虛擬內(nèi)存地址和物理內(nèi)存地址。虛擬內(nèi)存地址由頁(yè)號(hào)(與頁(yè)表中的頁(yè)號(hào)關(guān)聯(lián))和偏移量組成。頁(yè)號(hào)就不必解釋了,上面已經(jīng)說(shuō)了,頁(yè)號(hào)對(duì)應(yīng)的映射到一個(gè)頁(yè)幀。那么,說(shuō)說(shuō)偏移量。偏移量就是我上面說(shuō)的頁(yè)(或者頁(yè)幀)的大小,即這個(gè)頁(yè)(或者頁(yè)幀)到底能存多少數(shù)據(jù)。舉個(gè)例子,有一個(gè)虛擬地址它的頁(yè)號(hào)是4,偏移量是20,那么他的尋址過(guò)程是這樣的:首先到頁(yè)表中找到頁(yè)號(hào)4對(duì)應(yīng)的頁(yè)幀號(hào)(比如為8),如果頁(yè)不在內(nèi)存中,則用失效機(jī)制調(diào)入頁(yè),否則把頁(yè)幀號(hào)和偏移量傳給MMU(CPU的內(nèi)存管理單元)組成一個(gè)物理上真正存在的地址,接著就是訪問(wèn)物理內(nèi)存中的數(shù)據(jù)了??偨Y(jié)起來(lái)說(shuō),虛擬內(nèi)存地址的大小是與地址總線位數(shù)相關(guān),物理內(nèi)存地址的大小跟物理內(nèi)存條的容量相關(guān)。
第一層理解
1. 每個(gè)進(jìn)程都有自己獨(dú)立的4G內(nèi)存空間,各個(gè)進(jìn)程的內(nèi)存空間具有類似的結(jié)構(gòu)
2. 一個(gè)新進(jìn)程建立的時(shí)候,將會(huì)建立起自己的內(nèi)存空間,此進(jìn)程的數(shù)據(jù),代碼等從磁盤(pán)拷貝到自己的進(jìn)程空間,哪些數(shù)據(jù)在哪里,都由進(jìn)程控制表中的task_struct記錄,task_struct中記錄中一條鏈表,記錄中內(nèi)存空間的分配情況,哪些地址有數(shù)據(jù),哪些地址無(wú)數(shù)據(jù),哪些可讀,哪些可寫(xiě),都可以通過(guò)這個(gè)鏈表記錄
3. 每個(gè)進(jìn)程已經(jīng)分配的內(nèi)存空間,都與對(duì)應(yīng)的磁盤(pán)空間映射
問(wèn)題:
計(jì)算機(jī)明明沒(méi)有那么多內(nèi)存(n個(gè)進(jìn)程的話就需要n*4G)內(nèi)存
建立一個(gè)進(jìn)程,就要把磁盤(pán)上的程序文件拷貝到進(jìn)程對(duì)應(yīng)的內(nèi)存中去,對(duì)于一個(gè)程序?qū)?yīng)的多個(gè)進(jìn)程這種情況,浪費(fèi)內(nèi)存!
第二層理解
1. 每個(gè)進(jìn)程的4G內(nèi)存空間只是虛擬內(nèi)存空間,每次訪問(wèn)內(nèi)存空間的某個(gè)地址,都需要把地址翻譯為實(shí)際物理內(nèi)存地址
2. 所有進(jìn)程共享同一物理內(nèi)存,每個(gè)進(jìn)程只把自己目前需要的虛擬內(nèi)存空間映射并存儲(chǔ)到物理內(nèi)存上。
3. 進(jìn)程要知道哪些內(nèi)存地址上的數(shù)據(jù)在物理內(nèi)存上,哪些不在,還有在物理內(nèi)存上的哪里,需要用頁(yè)表來(lái)記錄
4. 頁(yè)表的每一個(gè)表項(xiàng)分兩部分,第一部分記錄此頁(yè)是否在物理內(nèi)存上,第二部分記錄物理內(nèi)存頁(yè)的地址(如果在的話)
5. 當(dāng)進(jìn)程訪問(wèn)某個(gè)虛擬地址,去看頁(yè)表,如果發(fā)現(xiàn)對(duì)應(yīng)的數(shù)據(jù)不在物理內(nèi)存中,則缺頁(yè)異常
6. 缺頁(yè)異常的處理過(guò)程,就是把進(jìn)程需要的數(shù)據(jù)從磁盤(pán)上拷貝到物理內(nèi)存中,如果內(nèi)存已經(jīng)滿了,沒(méi)有空地方了,那就找一個(gè)頁(yè)覆蓋,當(dāng)然如果被覆蓋的頁(yè)曾經(jīng)被修改過(guò),需要將此頁(yè)寫(xiě)回磁盤(pán)
總結(jié):
優(yōu)點(diǎn):
1.既然每個(gè)進(jìn)程的內(nèi)存空間都是一致而且固定的,所以鏈接器在鏈接可執(zhí)行文件時(shí),可以設(shè)定內(nèi)存地址,而不用去管這些數(shù)據(jù)最終實(shí)際的內(nèi)存地址,這是有獨(dú)立內(nèi)存空間的好處
2.當(dāng)不同的進(jìn)程使用同樣的代碼時(shí),比如庫(kù)文件中的代碼,物理內(nèi)存中可以只存儲(chǔ)一份這樣的代碼,不同的進(jìn)程只需要把自己的虛擬內(nèi)存映射過(guò)去就可以了,節(jié)省內(nèi)存
3.在程序需要分配連續(xù)的內(nèi)存空間的時(shí)候,只需要在虛擬內(nèi)存空間分配連續(xù)空間,而不需要實(shí)際物理內(nèi)存的連續(xù)空間,可以利用碎片。
另外,事實(shí)上,在每個(gè)進(jìn)程創(chuàng)建加載時(shí),內(nèi)核只是為進(jìn)程“創(chuàng)建”了虛擬內(nèi)存的布局,具體就是初始化進(jìn)程控制表中內(nèi)存相關(guān)的鏈表,實(shí)際上并不立即就把虛擬內(nèi)存對(duì)應(yīng)位置的程序數(shù)據(jù)和代碼(比如.text .data段)拷貝到物理內(nèi)存中,只是建立好虛擬內(nèi)存和磁盤(pán)文件之間的映射就好(叫做存儲(chǔ)器映射),等到運(yùn)行到對(duì)應(yīng)的程序時(shí),才會(huì)通過(guò)缺頁(yè)異常,來(lái)拷貝數(shù)據(jù)。還有進(jìn)程運(yùn)行過(guò)程中,要?jiǎng)討B(tài)分配內(nèi)存,比如malloc時(shí),也只是分配了虛擬內(nèi)存,即為這塊虛擬內(nèi)存對(duì)應(yīng)的頁(yè)表項(xiàng)做相應(yīng)設(shè)置,當(dāng)進(jìn)程真正訪問(wèn)到此數(shù)據(jù)時(shí),才引發(fā)缺頁(yè)異常。
補(bǔ)充理解:
虛擬存儲(chǔ)器涉及三個(gè)概念: 虛擬存儲(chǔ)空間,磁盤(pán)空間,內(nèi)存空間
可以認(rèn)為虛擬空間都被映射到了磁盤(pán)空間中,(事實(shí)上也是按需要映射到磁盤(pán)空間上,通過(guò)mmap),并且由頁(yè)表記錄映射位置,當(dāng)訪問(wèn)到某個(gè)地址的時(shí)候,通過(guò)頁(yè)表中的有效位,可以得知此數(shù)據(jù)是否在內(nèi)存中,如果不是,則通過(guò)缺頁(yè)異常,將磁盤(pán)對(duì)應(yīng)的數(shù)據(jù)拷貝到內(nèi)存中,如果沒(méi)有空閑內(nèi)存,則選擇犧牲頁(yè)面,替換其他頁(yè)面。
mmap是用來(lái)建立從虛擬空間到磁盤(pán)空間的映射的,可以將一個(gè)虛擬空間地址映射到一個(gè)磁盤(pán)文件上,當(dāng)不設(shè)置這個(gè)地址時(shí),則由系統(tǒng)自動(dòng)設(shè)置,函數(shù)返回對(duì)應(yīng)的內(nèi)存地址(虛擬地址),當(dāng)訪問(wèn)這個(gè)地址的時(shí)候,就需要把磁盤(pán)上的內(nèi)容拷貝到內(nèi)存了,然后就可以讀或者寫(xiě),最后通過(guò)manmap可以將內(nèi)存上的數(shù)據(jù)換回到磁盤(pán),也就是解除虛擬空間和內(nèi)存空間的映射,這也是一種讀寫(xiě)磁盤(pán)文件的方法,也是一種進(jìn)程共享數(shù)據(jù)的方法 共享內(nèi)存
接下來(lái)我們來(lái)討論下物理內(nèi)存:在內(nèi)核態(tài)申請(qǐng)內(nèi)存比在用戶態(tài)申請(qǐng)內(nèi)存要更為直接,它沒(méi)有采用用戶態(tài)那種延遲分配內(nèi)存技術(shù)。內(nèi)核認(rèn)為一旦有內(nèi)核函數(shù)申請(qǐng)內(nèi)存,那么就必須立刻滿足該申請(qǐng)內(nèi)存的請(qǐng)求,并且這個(gè)請(qǐng)求一定是正確合理的。相反,對(duì)于用戶態(tài)申請(qǐng)內(nèi)存的請(qǐng)求,內(nèi)核總是盡量延后分配物理內(nèi)存,用戶進(jìn)程總是先獲得一個(gè)虛擬內(nèi)存區(qū)的使用權(quán),最終通過(guò)缺頁(yè)異常獲得一塊真正的物理內(nèi)存。
1.物理內(nèi)存的內(nèi)核映射
IA32架構(gòu)中內(nèi)核虛擬地址空間只有1GB大小(從3GB到4GB),因此可以直接將1GB大小的物理內(nèi)存(即常規(guī)內(nèi)存)映射到內(nèi)核地址空間,但超出1GB大小的物理內(nèi)存(即高端內(nèi)存)就不能映射到內(nèi)核空間。為此,內(nèi)核采取了下面的方法使得內(nèi)核可以使用所有的物理內(nèi)存。
1).高端內(nèi)存不能全部映射到內(nèi)核空間,也就是說(shuō)這些物理內(nèi)存沒(méi)有對(duì)應(yīng)的線性地址。不過(guò),內(nèi)核為每個(gè)物理頁(yè)框都分配了對(duì)應(yīng)的頁(yè)框描述符,所有的頁(yè)框描述符都保存在mem_map數(shù)組中,因此每個(gè)頁(yè)框描述符的線性地址都是固定存在的。內(nèi)核此時(shí)可以使用alloc_pages()和alloc_page()來(lái)分配高端內(nèi)存,因?yàn)檫@些函數(shù)返回頁(yè)框描述符的線性地址。
2).內(nèi)核地址空間的后128MB專門(mén)用于映射高端內(nèi)存,否則,沒(méi)有線性地址的高端內(nèi)存不能被內(nèi)核所訪問(wèn)。這些高端內(nèi)存的內(nèi)核映射顯然是暫時(shí)映射的,否則也只能映射128MB的高端內(nèi)存。當(dāng)內(nèi)核需要訪問(wèn)高端內(nèi)存時(shí)就臨時(shí)在這個(gè)區(qū)域進(jìn)行地址映射,使用完畢之后再用來(lái)進(jìn)行其他高端內(nèi)存的映射。
由于要進(jìn)行高端內(nèi)存的內(nèi)核映射,因此直接能夠映射的物理內(nèi)存大小只有896MB,該值保存在high_memory中。內(nèi)核地址空間的線性地址區(qū)間如下圖所示:
從圖中可以看出,內(nèi)核采用了三種機(jī)制將高端內(nèi)存映射到內(nèi)核空間:永久內(nèi)核映射,固定映射和vmalloc機(jī)制。
2.物理內(nèi)存管理機(jī)制
基于物理內(nèi)存在內(nèi)核空間中的映射原理,物理內(nèi)存的管理方式也有所不同。內(nèi)核中物理內(nèi)存的管理機(jī)制主要有伙伴算法,slab高速緩存和vmalloc機(jī)制。其中伙伴算法和slab高速緩存都在物理內(nèi)存映射區(qū)分配物理內(nèi)存,而vmalloc機(jī)制則在高端內(nèi)存映射區(qū)分配物理內(nèi)存。
伙伴算法
伙伴算法負(fù)責(zé)大塊連續(xù)物理內(nèi)存的分配和釋放,以頁(yè)框?yàn)榛締挝?。該機(jī)制可以避免外部碎片。
per-CPU頁(yè)框高速緩存
內(nèi)核經(jīng)常請(qǐng)求和釋放單個(gè)頁(yè)框,該緩存包含預(yù)先分配的頁(yè)框,用于滿足本地CPU發(fā)出的單一頁(yè)框請(qǐng)求。
slab緩存
slab緩存負(fù)責(zé)小塊物理內(nèi)存的分配,并且它也作為高速緩存,主要針對(duì)內(nèi)核中經(jīng)常分配并釋放的對(duì)象。
vmalloc機(jī)制
vmalloc機(jī)制使得內(nèi)核通過(guò)連續(xù)的線性地址來(lái)訪問(wèn)非連續(xù)的物理頁(yè)框,這樣可以最大限度的使用高端物理內(nèi)存。
3.物理內(nèi)存的分配
內(nèi)核發(fā)出內(nèi)存申請(qǐng)的請(qǐng)求時(shí),根據(jù)內(nèi)核函數(shù)調(diào)用接口將啟用不同的內(nèi)存分配器。
3.1 分區(qū)頁(yè)框分配器
分區(qū)頁(yè)框分配器 (zoned page frame allocator) ,處理對(duì)連續(xù)頁(yè)框的內(nèi)存分配請(qǐng)求。分區(qū)頁(yè)框管理器分為兩大部分:前端的管理區(qū)分配器和伙伴系統(tǒng),如下圖:
管理區(qū)分配器負(fù)責(zé)搜索一個(gè)能滿足請(qǐng)求頁(yè)框塊大小的管理區(qū)。在每個(gè)管理區(qū)中,具體的頁(yè)框分配工作由伙伴系統(tǒng)負(fù)責(zé)。為了達(dá)到更好的系統(tǒng)性能,單個(gè)頁(yè)框的申請(qǐng)工作直接通過(guò)per-CPU頁(yè)框高速緩存完成。
該分配器通過(guò)幾個(gè)函數(shù)和宏來(lái)請(qǐng)求頁(yè)框,它們之間的封裝關(guān)系如下圖所示。
這些函數(shù)和宏將核心的分配函數(shù)__alloc_pages_nodemask()封裝,形成滿足不同分配需求的分配函數(shù)。其中,alloc_pages()系列函數(shù)返回物理內(nèi)存首頁(yè)框描述符,__get_free_pages()系列函數(shù)返回內(nèi)存的線性地址。
3.2 slab分配器
slab 分配器最初是為了解決物理內(nèi)存的內(nèi)部碎片而提出的,它將內(nèi)核中常用的數(shù)據(jù)結(jié)構(gòu)看做對(duì)象。slab分配器為每一種對(duì)象建立高速緩存。內(nèi)核對(duì)該對(duì)象的分配和釋放均是在這塊高速緩存中操作。一種對(duì)象的slab分配器結(jié)構(gòu)圖如下:
可以看到每種對(duì)象的高速緩存是由若干個(gè)slab組成,每個(gè)slab是由若干個(gè)頁(yè)框組成的。雖然slab分配器可以分配比單個(gè)頁(yè)框更小的內(nèi)存塊,但它所需的所有內(nèi)存都是通過(guò)伙伴算法分配的。
slab高速緩存分專用緩存和通用緩存。專用緩存是對(duì)特定的對(duì)象,比如為內(nèi)存描述符創(chuàng)建高速緩存。通用緩存則是針對(duì)一般情況,適合分配任意大小的物理內(nèi)存,其接口即為kmalloc()。
3.3 非連續(xù)內(nèi)存區(qū)內(nèi)存的分配
內(nèi)核通過(guò)vmalloc()來(lái)申請(qǐng)非連續(xù)的物理內(nèi)存,若申請(qǐng)成功,該函數(shù)返回連續(xù)內(nèi)存區(qū)的起始地址,否則,返回NULL。vmalloc()和kmalloc()申請(qǐng)的內(nèi)存有所不同,kmalloc()所申請(qǐng)內(nèi)存的線性地址與物理地址都是連續(xù)的,而vmalloc()所申請(qǐng)的內(nèi)存線性地址連續(xù)而物理地址則是離散的,兩個(gè)地址之間通過(guò)內(nèi)核頁(yè)表進(jìn)行映射。
vmalloc()的工作方式理解起來(lái)很簡(jiǎn)單:
1).尋找一個(gè)新的連續(xù)線性地址空間;
2).依次分配一組非連續(xù)的頁(yè)框;
3).為線性地址空間和非連續(xù)頁(yè)框建立映射關(guān)系,即修改內(nèi)核頁(yè)表;
vmalloc()的內(nèi)存分配原理與用戶態(tài)的內(nèi)存分配相似,都是通過(guò)連續(xù)的虛擬內(nèi)存來(lái)訪問(wèn)離散的物理內(nèi)存,并且虛擬地址和物理地址之間是通過(guò)頁(yè)表進(jìn)行連接的,通過(guò)這種方式可以有效的使用物理內(nèi)存。但是應(yīng)該注意的是,vmalloc()申請(qǐng)物理內(nèi)存時(shí)是立即分配的,因?yàn)閮?nèi)核認(rèn)為這種內(nèi)存分配請(qǐng)求是正當(dāng)而且緊急的;相反,用戶態(tài)有內(nèi)存請(qǐng)求時(shí),內(nèi)核總是盡可能的延后,畢竟用戶態(tài)跟內(nèi)核態(tài)不在一個(gè)特權(quán)級(jí)。
看過(guò)“ 物理虛擬內(nèi)存 ”的人還看了: