前言
在互聯(lián)網(wǎng)時(shí)代人乓,大部分的應(yīng)用程序基本都是IO密集型,而IO密集型的程序運(yùn)行效率的關(guān)鍵在于內(nèi)存管理祸憋,因此充分理解操作系統(tǒng)中內(nèi)存管理是一個(gè)優(yōu)秀程序員的必備知識(shí)蹂楣。Linux是目前互聯(lián)網(wǎng)服務(wù)器主流操作系統(tǒng),基于Linux來具體化的分析優(yōu)秀內(nèi)存管理是如何設(shè)計(jì)的志笼,不僅可以幫助我們更好的設(shè)計(jì)系統(tǒng)沿盅,也能讓我們對(duì)我們寫的程序是如何在Linux中運(yùn)行的有個(gè)比較全面的理解。
操作系統(tǒng)簡介
現(xiàn)代計(jì)算機(jī)基礎(chǔ)架構(gòu)起源并發(fā)展于馮·諾依曼體系結(jié)構(gòu)纫溃,其核心架構(gòu)抽象為:計(jì)算器腰涧,控制器,存儲(chǔ)器紊浩,輸入設(shè)備窖铡,輸出設(shè)備五大基礎(chǔ)組件,其實(shí)這里沒有提到的另外一個(gè)組件就是總線坊谁,貫穿各個(gè)組件之間费彼,是計(jì)算機(jī)系統(tǒng)中的血管。對(duì)應(yīng)于計(jì)算機(jī)CPU口芍,內(nèi)存箍铲,硬盤,鼠標(biāo)鬓椭,鍵盤虹钮,顯示器等等聋庵。
操作系統(tǒng)的本質(zhì)就是管理計(jì)算機(jī)架構(gòu)中的各個(gè)組件,將各個(gè)組件融匯貫通芙粱,形成一個(gè)整體,讓計(jì)算機(jī)成為一個(gè)完全可用的獨(dú)立設(shè)備氧映。優(yōu)秀的設(shè)計(jì)模式就是要進(jìn)行很好的抽象春畔,劃分出職責(zé)獨(dú)立的慕課,所以操作系統(tǒng)抽象了很多概念及接口以便于更加清晰明確的管理好各個(gè)設(shè)備岛都,也就有了所謂的進(jìn)程管理律姨,內(nèi)存管理,文件系統(tǒng)臼疫,設(shè)備管理等等择份。在Linux操作系統(tǒng)中每個(gè)模塊的設(shè)計(jì)都可以說的上非常精妙,是我們值得我們學(xué)習(xí)的優(yōu)秀設(shè)計(jì)烫堤。
操作系統(tǒng)設(shè)計(jì)荣赶,往往會(huì)有這么一些需要考慮的地方:吞吐率,交互性鸽斟,安全性拔创,健壯性/穩(wěn)定性,靈活性富蓄。 吞吐率是指一定時(shí)間內(nèi)能處理的指令數(shù)剩燥;交互性主要包括響應(yīng)延時(shí),交互模式等立倍;安全性是指對(duì)于計(jì)算機(jī)中的資源(內(nèi)存灭红,CPU,磁盤等等口注,一般稱為資源)管理要保證一些安全策略变擒,比如不能被惡意篡改資源內(nèi)容,劃分資源安全等級(jí)等疆导;健壯性是指要能處理各種亂七八糟的情況赁项,比如突然斷電,用戶瞎輸入指令等等依然能夠運(yùn)行穩(wěn)定或者能夠不要讓系統(tǒng)崩壞而無法繼續(xù)使用澈段;靈活性則是指能夠方便的滿足用戶的一些額外需求悠菜,比如能靈活的支持各種新的外接設(shè)備。
吞吐率败富,交互性悔醋,靈活性的重要性我們暫且不做討論兽叮,總所周知芬骄,Linux是最安全的操作系統(tǒng)之一猾愿。這里其實(shí)主要是想要講一下linux的健壯性,健壯性是操作系統(tǒng)的根基账阻,一切拋開穩(wěn)定之外的性能提升蒂秘,資源優(yōu)化都是耍流氓,可是如何設(shè)計(jì)一個(gè)優(yōu)雅的模式來保證操作系統(tǒng)的健壯性來處理各種意想不到的外界干擾淘太?
為了很好的設(shè)計(jì)健壯穩(wěn)定的系統(tǒng)姻僧,Linux引入核心態(tài)和用戶態(tài)的狀態(tài)設(shè)計(jì)概念,所有危險(xiǎn)的動(dòng)作只能在核心態(tài)下執(zhí)行蒲牧,而核心態(tài)的指令是由專業(yè)人員編寫的撇贺,他們都是大神熟悉與硬件打交道的各個(gè)細(xì)節(jié)。有了這一層約定之后冰抢,就大大提升了Linux的穩(wěn)定性松嘶,只要核心態(tài)的代碼寫的沒有bug,操作系統(tǒng)就不會(huì)崩潰??挎扰。那么什么是危險(xiǎn)的動(dòng)作翠订?那就是一切與硬件打交道的操作,因?yàn)橹灰话延布O(shè)備里面的數(shù)據(jù)搞壞了鼓鲁,那操作系統(tǒng)都能自己恢復(fù)蕴轨,但是硬件里面的數(shù)據(jù)被弄出問題來了,那操作系統(tǒng)可能就一臉懵逼骇吭,免疫系統(tǒng)癱瘓橙弱,需要多喝熱水,重啟試試了燥狰〖辏看到這里有人會(huì)說:”JJYY說這么多,和內(nèi)存管理有毛的關(guān)系龙致?“ 其實(shí)就是騷為科普一下Linux核心態(tài)和用戶態(tài) :)
內(nèi)存管理
內(nèi)存管理劃分
既然知道操作系統(tǒng)對(duì)所有硬件資源的操作都會(huì)區(qū)分核心態(tài)和用戶態(tài)蛀缝,那么內(nèi)存管理作為需要和內(nèi)存設(shè)備打交道的模塊自然而然也就劃分成了兩大塊:虛擬內(nèi)存管理(用戶態(tài))及物理內(nèi)存管理(核心態(tài)),隨之將地址空間劃分為虛擬地址空間和物理地址空間目代。
對(duì)于不熟悉LInux內(nèi)核的同學(xué)或者只學(xué)過操作系統(tǒng)理論的同學(xué)屈梁,應(yīng)該對(duì)虛擬地址空間比較熟悉,但是對(duì)于物理內(nèi)存管理應(yīng)該比較陌生榛了,因?yàn)閷W(xué)校課程里面基本上講的那些知識(shí)都是介紹的虛擬地址空間在讶,所以讓人誤解為虛擬地址空間就是內(nèi)存管理的全部,我反正是有很長一段時(shí)間是這么認(rèn)為的霜大,直到我閉關(guān)修煉了一年 :)构哺。
重要概念
虛擬內(nèi)存與物理內(nèi)存
虛擬內(nèi)存是指用戶空間分配或者回收的內(nèi)存塊,是一個(gè)邏輯概念战坤。
物理內(nèi)存是指內(nèi)核實(shí)際管理的硬件設(shè)備中的實(shí)際內(nèi)存曙强,是一個(gè)實(shí)體概念残拐。
頁與頁幀
頁是Linux用戶空間下,內(nèi)存管理模式中的最小單位碟嘴,是一個(gè)邏輯概念溪食。
頁幀是物理內(nèi)存中的一塊內(nèi)存大小,一般是4KB娜扇,是一個(gè)實(shí)體概念眠菇。
用戶只能看到以及操作邏輯層面的概念。
虛擬地址空間
虛擬地址空間是一個(gè)很基礎(chǔ)的概念袱衷,其描述了一個(gè)獨(dú)立的Linux進(jìn)程地址空間,從而規(guī)劃出了進(jìn)程運(yùn)行過程中的內(nèi)存管理模式笑窜,以一種通用的邏輯的視圖來統(tǒng)一不同進(jìn)程的內(nèi)存分配方式致燥。祭出一張爛大街的Linux進(jìn)程虛擬地址空間劃分圖:(這是經(jīng)典32位操作系統(tǒng)的地址結(jié)構(gòu),雖然現(xiàn)在基本都是64位系統(tǒng)了排截,但是除了數(shù)值不一樣嫌蚤,整體結(jié)構(gòu)是基本一樣的,所以后續(xù)的分析都是基于32位系統(tǒng)的描述)
其實(shí)從這張圖就可以看出核心態(tài)和用戶態(tài)的劃分断傲,Linux內(nèi)核一開始就將地址空間劃出了界線脱吱,表示不愿跟用戶空間的這些渣渣程序員們?yōu)槲椋凶约邯?dú)立的隱私空間认罩,防止被他們坑箱蝠,成為這個(gè)混沌的操作系統(tǒng)中最后一片凈土。
虛擬地址空間: 內(nèi)核空間(kernel Space)
首先要指出的是垦垂,所有用戶進(jìn)程共享的都是同一個(gè)內(nèi)核內(nèi)存宦搬,也就是說雖然每個(gè)用戶進(jìn)程的虛擬地址空間看上去都有自己的Kernel Space,其實(shí)這些用戶進(jìn)程的kernel Space都是一樣一樣的劫拗。而這些kernel Space就承擔(dān)著所有與硬件打交道的工作间校,諸如內(nèi)存管理,進(jìn)程調(diào)度页慷,磁盤管理等等憔足。
那么用戶進(jìn)程的虛擬地址空間的kernel Space是如何做到共享的呢?那么答案就是直接映射:物理地址空間和虛擬地址空間整塊內(nèi)存直接映射(不借助外部設(shè)備酒繁,通過虛擬地址就能計(jì)算物理地址)滓彰,而不是我們大學(xué)課程中說的什么段式,頁式欲逃,段頁式之內(nèi)的非連續(xù)分配需要借助MMU才能計(jì)算物理地址找蜜。有了直接映射后,所有用戶進(jìn)程的內(nèi)核空間就都映射到同一個(gè)物理地址完成共享稳析。
內(nèi)核空間里面住著一些什么妖魔鬼怪洗做?其實(shí)存儲(chǔ)著各個(gè)管理進(jìn)程的指令及數(shù)據(jù)弓叛,指令包括:核心調(diào)度器,內(nèi)存管理诚纸,文件管理撰筷,數(shù)據(jù)包括:文件目錄項(xiàng)緩存,打開文件inode緩存等等畦徘。
內(nèi)核地址空間的劃分
內(nèi)核空間又劃分為兩個(gè)部分:normal和high毕籽,normal是內(nèi)核分配內(nèi)存的主體,大小為896M井辆,這塊地址的分配是完全與物理地址直接一一映射的关筒,其起始16M地址存儲(chǔ)著內(nèi)核BIOS,存儲(chǔ)著內(nèi)核指令和數(shù)據(jù)杯缺,這個(gè)部分的分配策略比較簡單高效蒸播,一般來說就是采用大小最合適分配策略(從內(nèi)存區(qū)域找一塊大小合適的內(nèi)存分配),但是這樣會(huì)產(chǎn)生大量的碎片萍肆,high是為了處理一些碎片情況留的口子袍榆,因?yàn)檫@一塊的內(nèi)存可以不連續(xù)分配,當(dāng)內(nèi)核normal中沒有一整塊可供連續(xù)分配的內(nèi)存塘揣,那么就只能在high中通過一些機(jī)制拼湊不連續(xù)的內(nèi)存頁來分配一整塊內(nèi)存包雀,這些機(jī)制包括vmalloc,持久映射以及固定映射亲铡。其中vmalloc不知道物理地址才写,持久映射和固定映射需要指定物理地址,這個(gè)比較偏底層奴愉,不做過多介紹琅摩。
虛擬地址空間: 用戶空間(user Space)
在上圖中,可以看到32位4GB虛擬地址空間的低3G是分配給用戶空間的锭硼,而虛擬地址又主要?jiǎng)澐殖蛇@么三大塊:程序初始化段房资,運(yùn)行時(shí)段以及環(huán)境變量段。
程序初始化段是從持久化存儲(chǔ)介質(zhì)中直接映射到內(nèi)存的段檀头,這一部分的地址在進(jìn)程運(yùn)行過程中不會(huì)變轰异,除了里面的數(shù)據(jù)有可能變化,但是地址中的相對(duì)結(jié)構(gòu)已經(jīng)固定暑始。包括:代碼段搭独,數(shù)據(jù)段,未初始化的數(shù)據(jù)段等廊镜。
運(yùn)行時(shí)段是在進(jìn)程運(yùn)行過程中牙肝,隨著用戶交互的輸入不同而會(huì)變化的數(shù)據(jù)區(qū)域,包括:棧區(qū),堆區(qū)以及內(nèi)存映射區(qū)配椭。這一部分是虛擬內(nèi)存管理的核心區(qū)域虫溜,后面會(huì)重點(diǎn)介紹。
環(huán)境變量區(qū)會(huì)記錄一些進(jìn)程啟動(dòng)時(shí)設(shè)置的系統(tǒng)環(huán)境變量以及啟動(dòng)命令行傳入的參數(shù)等股缸。
虛擬內(nèi)存管理
學(xué)過操作系統(tǒng)的同學(xué)對(duì)于虛擬內(nèi)存管理都不會(huì)陌生衡楞,所以將虛擬內(nèi)存管理的介紹放在前面,可能更容易被理解敦姻,而且很多同學(xué)對(duì)物理內(nèi)存的管理也并不關(guān)心瘾境。
虛擬地址空間
棧
對(duì)于棧的概念,應(yīng)該都不陌生镰惦,該部分地址從上往下增長迷守,存儲(chǔ)的是深度函數(shù)指令,但需要結(jié)合進(jìn)程和線程來重點(diǎn)提一下的是旺入,虛擬地址空間中的棧地址只描述頂級(jí)父進(jìn)程的椇杏蹋空間,而子線程的椪R担空間是維護(hù)在mmap區(qū)域的。
堆
堆是用戶空間分配內(nèi)存的主要區(qū)域沮协,該部分地址從下往上增長龄捡。對(duì)于堆區(qū)域的操作主要包括兩個(gè)操作:1. brk伸展堆區(qū)域;2. do_munmap收縮堆區(qū)域慷暂。brk操作會(huì)申請(qǐng)一塊虛擬內(nèi)存區(qū)域
當(dāng)用戶申請(qǐng)指定大小的內(nèi)存后聘殖,如果堆內(nèi)沒有可用的連續(xù)可分配虛擬地址則會(huì)調(diào)用brk將對(duì)頂?shù)刂飞弦疲敝劣锌捎玫膬?nèi)存塊使用行瑞,如果堆頂?shù)奶摂M內(nèi)存區(qū)域沒被使用時(shí)就會(huì)調(diào)用do_munmap收縮堆頂?shù)刂贰?/p>
mmap區(qū)域
mmap區(qū)域主要維護(hù)兩個(gè)類型的內(nèi)存映射:1. 文件映射奸腺; 2. 線程棧。而文件映射其實(shí)就是我們常逞茫看到的cached:
[test@host ~]$ free -m
total used free shared buffers cached
Mem: 31706 15522 16183 0 222 4670
-/+ buffers/cache: 10628 21077
Swap: 0 0 0
經(jīng)過前面的介紹突照,應(yīng)該知道了虛擬內(nèi)存和物理內(nèi)存的基本概念,那么虛擬內(nèi)存和物理內(nèi)存是如何關(guān)聯(lián)起來呢氧吐?當(dāng)用戶向操作系統(tǒng)申請(qǐng)1MB內(nèi)存時(shí)讹蘑,操作系統(tǒng)是怎么真正的從硬件設(shè)備分配指定大小的內(nèi)存?完成這個(gè)復(fù)雜工作的模塊叫做虛擬內(nèi)存管理筑舅,其中主要包括兩塊:1. 管理虛擬地址空間分配:管理所有被分配的連續(xù)的虛擬地址座慰;2.管理虛擬地址到物理地址的映射。其中模塊1就叫做內(nèi)存映射翠拣;模塊2存儲(chǔ)物理內(nèi)存到虛擬內(nèi)存的映射關(guān)系的實(shí)體叫做頁表版仔。
內(nèi)存映射
內(nèi)存映射的函數(shù)原型其實(shí)就是mmap,如下圖:
內(nèi)存映射就是將虛擬內(nèi)存地址劃分成一個(gè)一個(gè)的區(qū)域這個(gè)區(qū)域的數(shù)據(jù)結(jié)構(gòu)叫做vm_area_struct,其中存儲(chǔ)著一些元信息,最重要的就是存儲(chǔ)了一個(gè)區(qū)域的首尾地址蛮粮,并將所有的vm_area_struct由一個(gè)紅黑樹管理起來益缎,這樣當(dāng)訪問一個(gè)虛擬地址時(shí)就能比較高效的判斷這個(gè)虛擬地址是否是合法的(已經(jīng)被映射管理)。
匿名映射和有名映射
[root@host ~]# cat /proc/1/maps
563d2e990000-563d2e9b3000 r-xp 00000000 103:04 262815 /sbin/init
563d2ebb2000-563d2ebb4000 r--p 00022000 103:04 262815 /sbin/init
563d2ebb4000-563d2ebb5000 rw-p 00024000 103:04 262815 /sbin/init
563d30287000-563d302e6000 rw-p 00000000 00:00 0 [heap]
7f9f8cddc000-7f9f8cfdb000 ---p 00017000 103:04 2335 /lib64/libpthread-2.17.so
7f9f8cfdb000-7f9f8cfdc000 r--p 00016000 103:04 2335 /lib64/libpthread-2.17.so
7f9f8cfdc000-7f9f8cfdd000 rw-p 00017000 103:04 2335 /lib64/libpthread-2.17.so
7f9f8cfdd000-7f9f8cfe1000 rw-p 00000000 00:00 0
7f9f8cfe1000-7f9f8d1a3000 r-xp 00000000 103:04 2309 /lib64/libc-2.17.so
7f9f8d1a3000-7f9f8d3a3000 ---p 001c2000 103:04 2309 /lib64/libc-2.17.so
7f9f8da1a000-7f9f8dc19000 ---p 00009000 103:04 4333 /lib64/libnih-dbus.so.1.0.0
7f9f8dc19000-7f9f8dc1a000 r--p 00008000 103:04 4333 /lib64/libnih-dbus.so.1.0.0
7f9f8de32000-7f9f8de33000 r--p 00017000 103:04 4335 /lib64/libnih.so.1.0.0
7f9f8de33000-7f9f8de34000 rw-p 00018000 103:04 4335 /lib64/libnih.so.1.0.0
7f9f8de34000-7f9f8de56000 r-xp 00000000 103:04 2264 /lib64/ld-2.17.so
7f9f8e047000-7f9f8e04c000 rw-p 00000000 00:00 0
7f9f8e054000-7f9f8e055000 rw-p 00000000 00:00 0
7f9f8e055000-7f9f8e056000 r--p 00021000 103:04 2264 /lib64/ld-2.17.so
7f9f8e056000-7f9f8e057000 rw-p 00022000 103:04 2264 /lib64/ld-2.17.so
7f9f8e057000-7f9f8e058000 rw-p 00000000 00:00 0
7ffecc776000-7ffecc797000 rw-p 00000000 00:00 0 [stack]
7ffecc7d9000-7ffecc7dc000 r--p 00000000 00:00 0 [vvar]
7ffecc7dc000-7ffecc7de000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
通過cat /proc/1/smaps
可以看到更為詳細(xì)每個(gè)映射區(qū)域的信息蝉揍,我們拿一個(gè)area簡單分析一下:
...
7f482e039000-7f482ed30000 r-xp 00000000 103:04 524415 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-0.50.amzn1.x86_64/jre/lib/amd64/server/libjvm.so
Size: 13276 kB // 映射區(qū)域內(nèi)存大小
KernelPageSize: 4 kB // 頁大小
MMUPageSize: 4 kB
Rss: 10780 kB // 實(shí)際加載到內(nèi)存的大小
Pss: 609 kB // 該有名映射與其他進(jìn)程共享链峭,這是除以共享進(jìn)程后的值
Shared_Clean: 10780 kB // 全部都是共享的,且沒有被修改過
Shared_Dirty: 0 kB //
Private_Clean: 0 kB
Private_Dirty: 0 kB
Referenced: 10780 kB // 用于swap標(biāo)記又沾,具體可以見頁置換
Anonymous: 0 kB // 匿名映射大小
LazyFree: 0 kB
AnonHugePages: 0 kB
ShmemPmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
Locked: 0 kB // 鎖住時(shí)弊仪,不能被置換
ProtectionKey: 0
VmFlags: rd ex mr mw me
...
我們到一臺(tái)Linux機(jī)器上敲出上面命令就可以看到任意一個(gè)進(jìn)程的內(nèi)存映射信息,最后一列有名字的是有名映射杖刷,無名字的則是匿名映射励饵,那么什么是有名映射和匿名映射是什么關(guān)系呢?
有名映射就是指該塊內(nèi)存會(huì)關(guān)聯(lián)到一定的外部持久化存儲(chǔ)滑燃,在內(nèi)存回收時(shí)需判斷內(nèi)存是否是臟的役听,也就是判斷是否需要寫回,有名映射主要操作的是mmap區(qū)域的內(nèi)存表窘,也就是我們常說的mmap典予,打開讀寫文件時(shí)會(huì)用,其中最常見的就是動(dòng)態(tài)鏈接庫乐严,可執(zhí)行文件的加載以及讀寫文件瘤袖;而匿名映射在內(nèi)存被回收時(shí)可以直接回收,無需額外的操作昂验,匿名映射主要操作堆區(qū)的內(nèi)存捂敌,也就是我們最常見的內(nèi)存分配(申請(qǐng)數(shù)組,字符串等等)既琴。
頁表TLB 與 缺頁中斷
如上圖所示占婉,頁表中存儲(chǔ)了虛擬地址到物理地址的映射,而其實(shí)現(xiàn)方式就是一個(gè)多級(jí)索引甫恩,記錄著虛擬地址到物理地址的映射關(guān)系逆济,而管理頁表的設(shè)備叫做MMU,所以每次訪問內(nèi)存時(shí)都需要先訪問MMU來獲取到物理地址磺箕,然后才能真正的訪問到物理設(shè)備纹腌,只不過后面這個(gè)操作是內(nèi)核實(shí)現(xiàn)的,用戶感受不到這個(gè)過程滞磺。具體訪問過程如下:(由于內(nèi)存訪問是高頻操作升薯,所以其性能很關(guān)鍵,所以為了減少頁表的訪問頻率击困,內(nèi)核會(huì)將頁表緩存到CPU cache中涎劈,由于計(jì)算機(jī)訪問局部性原理广凸,cache機(jī)制可以大大提高查詢效率)
當(dāng)用戶訪問某個(gè)虛擬內(nèi)存地址時(shí),CPU會(huì)先訪問MMU獲取物理地址然后再訪問到真實(shí)的物理地址蛛枚。這里面你是否有一個(gè)疑問:既然每次訪問內(nèi)存都要經(jīng)過MMU這樣一個(gè)硬件設(shè)備谅海,而之前上面又提到過所有訪問硬件設(shè)備的操作都是危險(xiǎn)的需要進(jìn)入核心態(tài)保證安全性,那么每次訪問內(nèi)存都要陷入內(nèi)核態(tài)蹦浦?這樣性能不得爆炸了扭吁?那么這里就需要說明一下:CPU這個(gè)硬件是一個(gè)特殊情況,由于其不存儲(chǔ)狀態(tài)盲镶,且CPU指令集是固定并遵循標(biāo)準(zhǔn)協(xié)議的侥袜,內(nèi)核協(xié)議CPU的執(zhí)行的所有指令都是健壯的,而MMU是CPU的組成部分溉贿,且不被用戶感知枫吧,所以操作MMU就不需要陷入內(nèi)核態(tài),如果對(duì)應(yīng)的指令需要訪問額外的設(shè)備才會(huì)陷入內(nèi)核態(tài)宇色。
這里需要說明的一點(diǎn)是:當(dāng)用戶申請(qǐng)一塊空間時(shí)九杂,只是分配了對(duì)應(yīng)的虛擬地址空間,但是也表里面并沒有建立映射宣蠕,而真正建立頁表映射的是在訪問對(duì)應(yīng)的虛擬地址空間的時(shí)候進(jìn)行的例隆。當(dāng)用戶空間訪問某個(gè)虛擬地址,而恰巧這個(gè)地址在頁表中不存在時(shí)抢蚀,就會(huì)觸發(fā)缺頁中斷異常裳擎,之后就會(huì)調(diào)用缺頁中斷處理邏輯。異常處理邏輯其實(shí)很簡單就是通過伙伴系統(tǒng)申請(qǐng)一個(gè)物理頁然后寫入頁表思币。
物理內(nèi)存管理
物理內(nèi)存的管理比較偏底層,可能你并不關(guān)心羡微,可以跳過這部分谷饿。
物理內(nèi)存的管理主要需要考慮這么幾個(gè)問題:
- 性能,計(jì)算機(jī)硬件發(fā)展迅猛妈倔,內(nèi)存容量從最開始的幾M到現(xiàn)在的幾百G博投,CPU從單核到現(xiàn)在的幾百核,如何能夠在如此龐大的內(nèi)存下依舊保證內(nèi)存訪問的高效性盯蝴。
- 分配的通用性毅哗,能夠應(yīng)對(duì)不同類型的內(nèi)存需求,比如IO緩存捧挺,進(jìn)程內(nèi)存等等虑绵。
- 內(nèi)存管理的高效性,既能夠高效的分配內(nèi)存闽烙,又不至于過分復(fù)雜翅睛。
- 靈活性声搁,能夠分配幾Byte到幾GB不同大小的內(nèi)存區(qū)域。
內(nèi)存組織
總所周知捕发,CPU是計(jì)算的核心疏旨,從而也是內(nèi)存訪問的主要入口,現(xiàn)代服務(wù)器及計(jì)算機(jī)內(nèi)存塊和CPU核數(shù)都能達(dá)到百級(jí)別扎酷,這樣就需要一個(gè)不錯(cuò)的機(jī)制來做好CPU與內(nèi)存之間的總線映射管理檐涝,以此來保證內(nèi)存訪問的高效性。
NUMA vs UMA
在舊版的計(jì)算機(jī)中法挨,CPU核數(shù)和內(nèi)存塊都很少谁榜,所以會(huì)將內(nèi)存串聯(lián)成一個(gè)整體,CPU通過單一的總線來訪問內(nèi)存坷剧,顯而易見cpu訪問內(nèi)存時(shí)單一總線成為了內(nèi)存訪問的瓶頸惰爬。
隨著計(jì)算機(jī)的發(fā)展這種單一總線模型已經(jīng)不能滿足大量內(nèi)存塊及CPU核數(shù)的需求,所以就提出了NUMA總線模型來更好的保證內(nèi)存訪問的并行性惫企,大大提高了多內(nèi)存多核情況下的內(nèi)存訪問性能撕瞧。
內(nèi)存域
為了更好的更好的應(yīng)對(duì)不同的內(nèi)容訪問要求及特殊的性能優(yōu)化,將內(nèi)存劃分為幾個(gè)內(nèi)存域:
ZONE_DMA標(biāo)記適合DMA的內(nèi)存域狞尔,在IA-32計(jì)算機(jī)上丛版,一般的現(xiàn)在是16MB,該區(qū)域供I/O設(shè)備直接訪問偏序,不需要通過MMU管理页畦,連續(xù)分配,具有更高的性能研儒。
· ZONE_DMA32豫缨,標(biāo)記了使用32位地址可尋址、適合DMA的內(nèi)存域端朵,顯然只有64位系統(tǒng)上好芭,才會(huì)有該內(nèi)存域。
· ZONE_NORMAL冲呢,可以直接映射到內(nèi)核段的普通內(nèi)存域舍败,這是所有體系機(jī)構(gòu)上保證都會(huì)存在的唯一內(nèi)存域,在IA-32系統(tǒng)上敬拓,該域可訪問的最大內(nèi)存不超過896MiB邻薯,超過該值的內(nèi)存只能能通過高端內(nèi)存尋址訪問ZONE_HIGHMEM中的內(nèi)存。
· ZONE_HIGHMEM乘凸,超出了內(nèi)核段的物理內(nèi)存厕诡。只有在可用物理內(nèi)存多余可映射的內(nèi)核內(nèi)存時(shí),才會(huì)訪問該域营勤,顯然一般只有32位系統(tǒng)上才會(huì)有可能有該區(qū)域木人。通過kmap及kunmap將該域內(nèi)存映射到內(nèi)核虛擬地址空間信柿。
· ZONE_MOVEABLE,這個(gè)區(qū)域主要是給用戶空間分配使用醒第。
前三個(gè)zone主要為內(nèi)核所用到渔嚷,最后一個(gè)主要被用戶空間用到,內(nèi)核空間內(nèi)存域具體劃分見下圖:
伙伴系統(tǒng)
前面說到通過內(nèi)存映射建立了虛擬地址空間到物理地址空間的一一對(duì)應(yīng)關(guān)系稠曼,那么當(dāng)用戶或者內(nèi)核申請(qǐng)一個(gè)16M的內(nèi)存的話形病,物理內(nèi)存管理應(yīng)該如何劃分并維護(hù)著12M內(nèi)存的分配呢?總所周知常用計(jì)算機(jī)的分配最小單位頁幀的大小為4KB霞幅,那么一個(gè)12M的內(nèi)存分配需要41024=4096個(gè)頁幀漠吻,如果一個(gè)頁一個(gè)頁的映射那么,性能也太低了司恳。那么有什么好的方式能快速的分配一整塊3072頁的內(nèi)存呢途乃?所以內(nèi)核就設(shè)計(jì)了伙伴系統(tǒng)*這么一個(gè)管理簡單高效的分配策略。
伙伴系統(tǒng)承擔(dān)內(nèi)核初始完后的物理內(nèi)存管理工作扔傅,負(fù)責(zé)管理各個(gè)內(nèi)存域zone中的物理內(nèi)存分配耍共,釋放。其基本工作原理如下:
- 把內(nèi)存按照頁劃分成很多階猎塞,最大階為MAX_ORDER试读,一般設(shè)置為11,每個(gè)階內(nèi)存區(qū)的內(nèi)存塊數(shù)為2^n荠耽,我們稱之為內(nèi)存區(qū)钩骇。
- 當(dāng)進(jìn)程申請(qǐng)一段內(nèi)存時(shí),總是從適合大小的階中分配指定內(nèi)存區(qū)铝量,比如當(dāng)分配7k(4k * 2^1倘屹,7k離8k最近)內(nèi)存的時(shí)候,會(huì)從第1階分配對(duì)應(yīng)的內(nèi)存區(qū)慢叨。
- 當(dāng)?shù)趉個(gè)階的內(nèi)存區(qū)全部被分完纽匙,沒有可分配的k階內(nèi)存區(qū)時(shí),會(huì)從第K+1階劃分出來兩個(gè)新的內(nèi)存區(qū)插爹,供第K階使用。比如第4階的內(nèi)存內(nèi)存都分配完了请梢,從第5階分裂出來兩個(gè)四階的內(nèi)存區(qū)赠尾。
- 當(dāng)從高階內(nèi)存區(qū)劃分出來的兩個(gè)區(qū)都被釋放時(shí),該兩個(gè)區(qū)的兩個(gè)內(nèi)存區(qū)會(huì)重新合并回高階內(nèi)存區(qū)毅弧。
image.png
當(dāng)當(dāng)前內(nèi)存域沒有可供分配的一整塊連續(xù)內(nèi)存時(shí)气嫁,就會(huì)向下一級(jí)內(nèi)存域申請(qǐng)分配,如果下一級(jí)也沒有就會(huì)到下一個(gè)備用節(jié)點(diǎn)(NUMA系統(tǒng)中的離當(dāng)前CPU較遠(yuǎn)內(nèi)存節(jié)點(diǎn))分配
slab 緩存
對(duì)于malloc這個(gè)函數(shù)够坐,大家來說應(yīng)該都不陌生寸宵,這是系統(tǒng)庫給我們提供了申請(qǐng)指定大小內(nèi)存的函數(shù)崖面,之前介紹的伙伴系統(tǒng),只能以頁的方式申請(qǐng)內(nèi)存梯影,對(duì)于小塊(小于一頁)內(nèi)存的申請(qǐng)我們就得通過自定義的庫函數(shù)來實(shí)現(xiàn)相關(guān)需求,所以在用戶空間層面誕生了諸如ptmalloc(glibc),tcmalloc(google)桐玻,jemalloc(facebook)等優(yōu)秀的內(nèi)存分配庫拔妥。但是這些庫內(nèi)核沒法使用,且內(nèi)核也有大量申請(qǐng)小塊內(nèi)存的需求感猛,諸如管理dentry七扰,inode,fs_struct陪白,page颈走,task_struct等等一系列內(nèi)核對(duì)象。所以內(nèi)核提出了slab分配器咱士,用來管理內(nèi)核中小塊內(nèi)存分配立由,而cpu cache也是配合slab使用的,有時(shí)候也把slab稱為緩存司致。
slab緩存由兩部分組成:保存管理性數(shù)據(jù)的緩存結(jié)構(gòu)和保存被管理對(duì)象的各個(gè)slab結(jié)構(gòu),
一個(gè)slab就是一個(gè)頁幀拆吆,而一個(gè)緩存下面維護(hù)著很多個(gè)頁幀的slab,且一個(gè)slab中存儲(chǔ)著很多個(gè)一樣大小的slab對(duì)象脂矫。
其中緩存對(duì)象里面主要存儲(chǔ)的信息包括:從內(nèi)存加載至cpu_cache中的slab緩存對(duì)象數(shù)量限制枣耀;該緩存中slab緩存對(duì)象的大小(一個(gè)緩存中對(duì)象的slab對(duì)象大小都是一樣大小的);該緩存結(jié)構(gòu)占用的內(nèi)存頁數(shù)量庭再;每個(gè)slab有多少個(gè)slab緩存對(duì)象捞奕;并維護(hù)著三個(gè)slab數(shù)組列表(全部空閑,全部滿拄轻,部分空閑)颅围。
當(dāng)一個(gè)slab剛被申請(qǐng)時(shí)是放入全部空閑列表中的,等到開始分配時(shí)會(huì)分到部分空閑列表恨搓,最終沒有可分配空間是被移到全部滿列表院促。
一個(gè)slab同樣也需要一些元信息來描述這個(gè)頁幀里面的slab對(duì)象,一個(gè)slab也是一個(gè)page斧抱,所以其管理數(shù)據(jù)結(jié)構(gòu)其實(shí)就是存在page這個(gè)數(shù)據(jù)結(jié)構(gòu)中常拓,其中的元信息主要的是一個(gè)freelist用來記錄當(dāng)前頁幀中還有哪些地址對(duì)應(yīng)的slab對(duì)象尚未被使用。具體見下圖:
緩存映射辉浦,在大學(xué)知識(shí)里面應(yīng)該有提到弄抬,直接映射,組相連宪郊,全相連這樣的概念掂恕,而用的比較多的是組相連拖陆,我們就以組相連為例簡單介紹一下這個(gè)映射關(guān)系:其中一個(gè)緩存行可能存儲(chǔ)多個(gè)slab對(duì)象,一個(gè)slab對(duì)象也可能跨越多個(gè)緩存行懊亡。
比如一個(gè)地址0X1234依啰,對(duì)應(yīng)的頁大小為4位,緩存為2位斋配,虛擬地址是16位孔飒,物理地址是8位,cpu緩存為2路組相連艰争。
- 那么前12位就是頁表映射找到對(duì)應(yīng)的頁幀坏瞄,也就是在頁表中查找0X123對(duì)應(yīng)的是哪個(gè)物理頁。
- 通過頁表找到物理頁幀后甩卓,就會(huì)查找對(duì)應(yīng)地址的slab對(duì)象是否在cpucache中鸠匀。
- 這時(shí)候0B0100中的地三位就會(huì)定位到是組映射中的哪一組。
- 找到對(duì)應(yīng)的cache組后逾柿,比對(duì)cache組中所有緩存行的tag是否==0x123缀棍,如果有相等則表示命中,否則不命中机错。
這里面有個(gè)slab cache coloring的性能優(yōu)化技巧爬范,什么是緩存著色呢?即在slab頁幀的起始處加上一定的偏移這個(gè)偏移是緩存行的倍數(shù)弱匪。如果沒有著色偏移的話青瀑,那么每次對(duì)應(yīng)的緩存行一般都是從一個(gè)固定值開始的,假如我們忽略slab head的大小萧诫,每次slab對(duì)象都從頁的起始位置開始分配斥难,由于slab對(duì)象與cache line 一般來說是不等的,那么每次映射的緩存行一般是0 ~ 61或者0 ~ 62這種帘饶,所以62哑诊,63這種緩存行就會(huì)有較低訪問概率,所以就需要這個(gè)著色偏移及刻,讓首尾緩存訪問的概率趨于相同
文件緩存镀裤、有名映射
在前面的free 命令中可以看到內(nèi)存會(huì)分為total,used缴饭,shared暑劝,buffers以及cached。
對(duì)于used和free這個(gè)比較熟悉就是真實(shí)進(jìn)程自己使用的內(nèi)存茴扁,那么shared铃岔,buffers汪疮,cached則是什么呢峭火?
shared:是用于shared共享映射的內(nèi)存毁习,一般是通過shm調(diào)用共享內(nèi)存或者mmap with MAP_SHARED。
buffers:是用于緩存磁盤塊設(shè)備對(duì)象的內(nèi)存卖丸,linux中磁盤塊也是需要一些元數(shù)據(jù)進(jìn)行管理的纺且。
cached:則是緩存磁盤數(shù)據(jù)的內(nèi)存。
那么什么是磁盤數(shù)據(jù)內(nèi)存緩存呢稍浆?前面說到對(duì)于操作物理硬件設(shè)備的操作都是危險(xiǎn)操作载碌,都必須在內(nèi)核態(tài)進(jìn)行,所以磁盤設(shè)備的操作也需要在內(nèi)核態(tài)執(zhí)行衅枫,而Linux文件在操作系統(tǒng)中是全局共享的嫁艇,所以對(duì)于磁盤文件可供所有程序共享使用,而且對(duì)于磁盤文件的讀寫都是以page頁幀為單位的弦撩,因此每次open一個(gè)文件的時(shí)候步咪,目標(biāo)文件就會(huì)以cache的形式緩存在內(nèi)核內(nèi)存中,也就是free命令中的cached益楼。
那么什么是有名映射猾漫?前面說到通過open打開的文件,其需要先緩存在內(nèi)核態(tài)的內(nèi)存中感凤,所以用戶態(tài)需要讀數(shù)據(jù)時(shí)需要從內(nèi)核內(nèi)存拷貝到用戶內(nèi)存悯周,這樣多了一個(gè)拷貝操作,所以內(nèi)核就開放了一個(gè)叫mmap的函數(shù)可以直接將文件映射到用戶內(nèi)存陪竿,這樣操作用戶內(nèi)存最終會(huì)同步到磁盤文件禽翼,而且mmap支持shared及private兩種模式,其中shared模式會(huì)寫回磁盤文件萨惑,而private不會(huì)寫回磁盤文件捐康,所以c++動(dòng)態(tài)鏈接文件一般是通過private mmap加載的。
用戶內(nèi)存管理(匿名映射)
其實(shí)對(duì)于大部分普通程序員來說庸蔼,接觸到最多的就是普通的內(nèi)存申請(qǐng)解总,諸如new String, new Object,malloc之類的姐仅,這些對(duì)象在系統(tǒng)內(nèi)存中是怎樣被分配存儲(chǔ)的呢花枫?當(dāng)調(diào)用delete操作釋放內(nèi)存時(shí)又是如何從系統(tǒng)中釋放空間的?
也許很多c++程序員剛開始畢業(yè)找工作的時(shí)候掏膏,都會(huì)遇到一道面試題new和malloc的區(qū)別是啥劳翰?然后就會(huì)balabala的說一些什么new會(huì)調(diào)用構(gòu)造函數(shù)之類的,new的底層其實(shí)也是由malloc實(shí)現(xiàn)的馒疹,那么malloc是怎么分配內(nèi)存的呢佳簸?這里需要說明一點(diǎn)的是其實(shí)malloc也是一個(gè)抽象接口,其具體實(shí)現(xiàn)由好多種,諸如glibc的ptmalloc生均,Google的tcmalloc听想,F(xiàn)acebook的jemalloc;任何人都遵循malloc的標(biāo)準(zhǔn)自定義寫一個(gè)malloc马胧。這些malloc都有各自的特點(diǎn)汉买,就不一一介紹了,有興趣的同學(xué)可以自行百度佩脊。這里需要提到的一點(diǎn)就是蛙粘,萬變不離其宗,這些malloc最終都要涉及到怎么向操作系統(tǒng)申請(qǐng)和釋放空間威彰。那么操作系統(tǒng)暴露出來的兩個(gè)個(gè)系統(tǒng)調(diào)用就是sys_brk以及sys_mmap出牧,各個(gè)不同的malloc的區(qū)別就是調(diào)用這兩個(gè)系統(tǒng)調(diào)用的時(shí)間及規(guī)則。但是這里需要重點(diǎn)說明一下的是:這里面的內(nèi)存分配及回收都只涉及到虛擬地址空間歇盼,而物理地址空間都由內(nèi)核單獨(dú)管理崔列。
內(nèi)存申請(qǐng)及訪問
前面介紹了用戶空間的內(nèi)存管理是通過內(nèi)存映射來做的。memory mapping那張圖上面當(dāng)用戶空間想要申請(qǐng)分配一塊內(nèi)存時(shí)旺遮,會(huì)先在已有內(nèi)存映射中查找是否有指定大小的內(nèi)存空間供可供使用赵讯,有則將對(duì)應(yīng)的地址生成vm_area_struct對(duì)象并放入紅黑樹中;否則就會(huì)調(diào)用do_brk擴(kuò)展對(duì)頂?shù)刂窂亩诙褍?nèi)能夠找到一塊適合分配的虛擬地址空間耿眉。
內(nèi)存釋放
虛擬地址空間釋放時(shí)調(diào)用do_unmap釋放一個(gè)vm_area_struct對(duì)象边翼,這樣在堆內(nèi)就會(huì)形成空洞,也就是我們所說的內(nèi)存碎片鸣剪,如果釋放的vm_area_struct對(duì)象處于堆頂组底,則會(huì)將堆頂往下移。
見一個(gè)進(jìn)程的內(nèi)存使用
#shell$ top -p 1
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 19808 2756 2296 S 0.0 0.0 1:03.46 init
其中VIRT是虛擬空間堆頂?shù)刂房鸷В琑ES是真正在頁表中有記錄的物理內(nèi)存债鸡,SHR通過共享mmap以及shm分配的共享內(nèi)存。
內(nèi)存回收與交換
前面說到在調(diào)用malloc分配完內(nèi)存時(shí)铛纬,其實(shí)只是申請(qǐng)了虛擬地址空間厌均,而真正的內(nèi)存分配是在缺頁中斷發(fā)生時(shí)。物理內(nèi)存的管理完全在內(nèi)核態(tài)告唆,所以內(nèi)核對(duì)物理內(nèi)存又做了優(yōu)化棺弊,這個(gè)優(yōu)化就是內(nèi)存回收與交換,以此來增大可用內(nèi)存數(shù)量擒悬,也就是我們熟知的swap模她。
swap的策略就是通過將物理內(nèi)存頁劃分隊(duì)列active和inactive兩個(gè),當(dāng)內(nèi)存比較吃緊時(shí)就會(huì)將inactive中的內(nèi)存回收或者放入交換區(qū)懂牧。那么active和inactive兩個(gè)隊(duì)列的中的內(nèi)存移動(dòng)我們叫做交換策略侈净,具體如下圖:
swap的策略大家都應(yīng)該很熟悉,就是我們面試經(jīng)常會(huì)考的LRU最近最久未使用策略,但是這個(gè)策略性能不高畜侦,所以內(nèi)核簡化了其思想运怖,通過維護(hù)頁的一個(gè)引用標(biāo)志位來實(shí)現(xiàn),具體見上圖夏伊,這里不詳細(xì)介紹了。
需要強(qiáng)調(diào)一點(diǎn):內(nèi)存的換入換出是一個(gè)很耗時(shí)的操作吻氧,如果操作過于頻繁會(huì)影響進(jìn)程的性能和響應(yīng)溺忧,而現(xiàn)代計(jì)算機(jī)基本內(nèi)存充足,不希望由于置換帶來的進(jìn)程響應(yīng)慢的后果盯孙,所以一般會(huì)關(guān)閉swap鲁森,但是一般的關(guān)閉swap操作只是關(guān)閉了必須寫到swap分區(qū)的匿名映射,對(duì)于有名映射沒法關(guān)閉swap振惰,否則緩存cache將不可用歌溉。
具體可以看一下機(jī)器的內(nèi)存詳細(xì)信息
[root@host ~]# cat /proc/meminfo
MemTotal: 32122940 kB // 機(jī)器內(nèi)存總大小
MemFree: 12271688 kB // 空閑內(nèi)存(完全為分配)
MemAvailable: 16046584 kB // 可用內(nèi)存 free + cache + buffer
Buffers: 228000 kB
Cached: 3494896 kB
SwapCached: 0 kB
Active: 16426752 kB // 總的活躍內(nèi)存
Inactive: 2593772 kB // 總的不活躍內(nèi)存
Active(anon): 15297648 kB // 匿名映射中處于活躍的內(nèi)存
Inactive(anon): 56 kB // 匿名映射中處于不活躍的內(nèi)存
Active(file): 1129104 kB // 有名映射中處于活躍的內(nèi)存(一般是mmap,加載的so)
Inactive(file): 2593716 kB // 有名映射中處于不活躍的內(nèi)存(一般來說是文件緩存)
...
AnonPages: 15297620 kB
Mapped: 131552 kB
Shmem: 84 kB
Slab: 596432 kB // 內(nèi)核使用
SReclaimable: 498372 kB // 可被回收的骑晶,一般是一些緩存對(duì)象痛垛,諸如:dentry,inode
SUnreclaim: 98060 kB // 不可被回收的桶蛔,一般是一些內(nèi)核數(shù)據(jù)結(jié)構(gòu)對(duì)象
KernelStack: 31216 kB //內(nèi)核棧
PageTables: 64936 kB // 頁表
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 16061468 kB
Committed_AS: 24740580 kB
VmallocTotal: 34359738367 kB // Vmalloc相關(guān)的
VmallocUsed: 0 kB
VmallocChunk: 0 kB
AnonHugePages: 0 kB
ShmemHugePages: 0 kB
ShmemPmdMapped: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 81892 kB // DMA內(nèi)存大小
DirectMap2M: 3280896 kB
DirectMap1G: 30408704 kB
總結(jié)
Linux 對(duì)于內(nèi)存管理主要包括兩部分:虛擬地址空間管理及物理內(nèi)存管理匙头,虛擬地址空間主要包括內(nèi)核空間及用戶空間。內(nèi)核空間主要存儲(chǔ)內(nèi)核對(duì)象仔雷、文件緩存蹂析,其中文件緩存包括磁盤文件,設(shè)備文件等的內(nèi)容及其元信息等碟婆,內(nèi)核對(duì)象的分配主要通過kmalloc分配电抚,其底層實(shí)現(xiàn)是slab緩存。用戶空間主要管理代碼段竖共,靜態(tài)數(shù)據(jù)段蝙叛,堆、棧公给、mmap及環(huán)境變量段甥温。管理虛擬地址空間的方式是內(nèi)存映射,內(nèi)存映射用來表示連續(xù)的整塊虛擬內(nèi)存分配妓布,可以劃分為有名映射和匿名映射姻蚓,判斷標(biāo)準(zhǔn)就是這塊虛擬地址否關(guān)聯(lián)到了一個(gè)文件。物理內(nèi)存與虛擬內(nèi)存的映射是通過通過頁表映射來管理的匣沼,當(dāng)用戶空間訪問某個(gè)虛擬地址時(shí)需要通過頁表映射來找到對(duì)應(yīng)的物理地址狰挡,這樣就完成虛擬地址到物理地址的尋址。而內(nèi)核虛擬地址與物理地址的尋址方式是直接映射,不需要依賴于頁表映射加叁。為了更好的管理和分配頁倦沧,提出了伙伴系統(tǒng)這樣一個(gè)思想來申請(qǐng)和分配一個(gè)或多個(gè)頁幀,伙伴系統(tǒng)的思想非常簡潔高效它匕。內(nèi)核為了保證更充分的內(nèi)存利用展融,就提出了內(nèi)存置換和回收機(jī)制,當(dāng)內(nèi)存不足時(shí)能夠回收或者換出一些最近沒怎么使用的內(nèi)存豫柬,這樣能夠更好的提高內(nèi)存使用率告希,這樣對(duì)于文件緩存的管理也變得更加容易,不需要額外的工作烧给,當(dāng)內(nèi)存不足時(shí)燕偶,文件緩存內(nèi)存大概率是可以被回收和置換的。
具體相關(guān)關(guān)系見下圖:
擴(kuò)展閱讀
大家想必對(duì)于docker和lxc容器都有過了解础嫡,那么lxc容器是怎么管理內(nèi)存的呢指么?眾所周知,lxc是由namespace及cgroup組成的榴鼎,而lxc相關(guān)的內(nèi)存管理其也是借助于cgroup的來管理的伯诬,但是內(nèi)存管理沒有獨(dú)立的namespace。
那么cgroup是怎么限制一個(gè)memory group的內(nèi)存訪問上線的呢巫财?內(nèi)核在2.6之后提出了mem group的概念姑廉,一個(gè)group可以指定一些限制,諸如最大內(nèi)存使用上限等翁涤。每個(gè)頁幀在分配時(shí)都會(huì)關(guān)聯(lián)到所在的group桥言,并進(jìn)行相關(guān)統(tǒng)計(jì),當(dāng)group內(nèi)的內(nèi)存達(dá)到一定的內(nèi)存水位時(shí)葵礼,就會(huì)觸發(fā)內(nèi)存回收和置換号阿,保證group內(nèi)內(nèi)存處于水位線下,如果經(jīng)過多次回收和置換都達(dá)不到水位線下時(shí)就會(huì)觸發(fā)OOM killer鸳粉。