Linux總是以Lazy的方式給應(yīng)用程序分配內(nèi)存,包括堆、棧(函數(shù)調(diào)用越深,用的棧越多,最終發(fā)生page fault才得到棧)、代碼段、數(shù)據(jù)段。那么,這些已經(jīng)獲得到內(nèi)存的段會一直占用著內(nèi)存嗎?
1.page cache
Linux下讀寫文件,主要有兩種方式:
調(diào)用read讀文件,Linux內(nèi)核會申請一個page cache,然后把文件讀到page cache中,再將內(nèi)核空間的page cache拷貝到用戶空間的buf。
調(diào)用write寫文件,則將用戶空間buf拷貝到內(nèi)核空間page cache。
mmap
mmap可以避免buf從用戶空間到內(nèi)核空間的拷貝過程。
直接把文件映射成一個虛擬地址指針,這個指針指向內(nèi)核申請的page cache。內(nèi)核知道page cache與硬盤中文件的對應(yīng)關(guān)系。
使用mmap讀寫文件
注:讀寫權(quán)限需要對應(yīng),否則觸發(fā)page fault。
編譯執(zhí)行:
mmap看起來是由一個虛擬地址對應(yīng)一個文件(可以直接用指針訪問文件),本質(zhì)上是把進(jìn)程的虛擬地址空間映射到DRAM(內(nèi)核從這片區(qū)域申請內(nèi)存做page cache),而這個page cache對應(yīng)磁盤中的某個文件,且Linux內(nèi)核會維護(hù)page cache和磁盤中文件的交換關(guān)系。詳見下圖:
page cache可以看作內(nèi)存針對磁盤的一個緩存,應(yīng)用程序在寫文件時,其實只是將內(nèi)容寫入了page cache,使用sync才能真的寫入文件。
ELF可執(zhí)行程序頭部會記錄代碼段的位置,代碼段的本質(zhì)就將ELF文件中的代碼段直接mmap映射到一個虛擬地址,且權(quán)限為R+X。
page cache可以極大的提高系統(tǒng)整體性能。如,進(jìn)程A讀一個文件,內(nèi)核空間會申請page cache與此文件對應(yīng),并記錄對應(yīng)關(guān)系,進(jìn)程B再次讀同樣的文件就會直接命中上一次的page cache,讀寫速度顯著提升。但注意,page cache會根據(jù)LRU算法(最近最少使用)進(jìn)行替換。
演示:page cache對程序執(zhí)行時間的影響
第一次多出很多硬盤io操作;第二次python的很多環(huán)境都在內(nèi)存中命中了,速度提升顯著。用\time -v命令再次對比:
附注:
i.swap:
動詞:swapping,內(nèi)存與磁盤的顛簸行為
名字:swap分區(qū)
ii.cache可以通過/proc/sys/vm/drop_caches強(qiáng)行釋放,寫1釋放page cache,2釋放dentries和inode,3釋放兩者。
2.free命令的詳細(xì)解釋
上圖中,buffers與cached都是文件系統(tǒng)的緩存,沒有本質(zhì)區(qū)別,唯一區(qū)別是背景不同:
i.當(dāng)以文件系統(tǒng)(ext4,xfs等)的形式去訪問文件系統(tǒng)中的文件,如mount /dev/sda1 /mnt后,/mnt目錄下會有很多文件,訪問這類文件所產(chǎn)生的cache就對應(yīng)free命令顯示的cached列。
ii.直接訪問/dev/sda1時,如用戶程序直接打開open(“dev/sda1…)或執(zhí)行dd命令,以及文件系統(tǒng)本身去訪問裸分區(qū),所產(chǎn)生的cache對應(yīng)free命令顯示的buffers列。
參考下圖所示:
演示:讀硬盤裸分區(qū)導(dǎo)致free命令顯示內(nèi)容變化
linux kernel 3.14版本以后,已經(jīng)采用新的free命令,如下圖:
老版本free中-/+buffers/cache的含義如下圖:
新版本free中多出available,即是評估出現(xiàn)在還有多少內(nèi)存可供應(yīng)用程序使用。
3.file-backed的頁面和匿名頁
page cache和CPU內(nèi)部cache一樣,是可以被替換出去的。有文件背景的頁面可以swap到磁盤。EG. 啟動firefox,跑一個oom的程序,前后對比firefox的smaps文件??梢钥闯鰂irefox在內(nèi)存緊張的情況下,代碼段、mmap的字體文件等都被替換出去而不駐留內(nèi)存了。
那么,沒有文件背景的匿名頁是如何交換回收的呢?是否常住內(nèi)存?詳見下圖:
有文件背景的頁面和匿名頁都需要swap,有文件背景的頁面向自己的文件背景中交換,匿名頁向swap分區(qū)和swapfile中交換。即使編譯內(nèi)核時將CONFIG_SWAP關(guān)閉(只是關(guān)閉了匿名頁的交換),linux內(nèi)核中kswapd的線程還是會swap有文件背景的頁面。
Linux有三個水位:min,low,high。一旦內(nèi)存達(dá)到低水位時,后臺自動回收直到回收到高水位。當(dāng)內(nèi)存到達(dá)min水位時,直接堵住進(jìn)程進(jìn)行回收。
匿名頁和有文件背景的頁面都有可能被回收,/proc/sys/vm/swappiness值比較大時,傾向回收匿名頁;swappiness值比較小時傾向回收有文件背景的頁面?;厥账惴ń詾長RU。
附注:
數(shù)據(jù)段比較特殊,在沒有寫的情況是有文件背景的,但被寫后就變?yōu)槟涿摗?/p>
Windows中的虛擬內(nèi)存就相當(dāng)于Linux的swapfile。
4.頁面回收和LRU
如上圖,運行到第4列時,第1頁最不活躍。運行到第5列時又把第1頁踏了一次,此時第2頁變?yōu)樽畈换钴S的。運行到第6列時又把第2頁踏了一次,此時第3頁變?yōu)樽畈换钴S的,所以在第7列時,由于要訪問一個新的第5頁,3就被替換出去。
5.swap以及zRAM
嵌入式系統(tǒng)受flash限制,很少使用swap分區(qū),一般都swapoff。所以嵌入式系統(tǒng)引入zRAM技術(shù)。
zRAM直接把一塊內(nèi)存模擬成一個硬盤分區(qū),當(dāng)作swap分區(qū)使用,此分區(qū)自帶透明壓縮功能,當(dāng)匿名頁向zRAM分區(qū)寫時,Linux內(nèi)核使CPU自動對匿名頁進(jìn)行壓縮。接下來,當(dāng)應(yīng)用程序又執(zhí)行到剛才的匿名頁時,由于此頁已經(jīng)被swap到zRAM中,內(nèi)存中沒有命中,頁表也沒有命中,所以此時再去訪問這塊內(nèi)存時再次發(fā)生page fault,Linux就從zRAM分區(qū)中將匿名頁透明的解壓出來還到內(nèi)存中。
zRAM的特點是用內(nèi)存來做swap分區(qū),透明壓(兩頁匿名頁有可能被壓縮成一頁),透明解(一頁解壓成兩頁),這樣其實相當(dāng)于擴(kuò)大了內(nèi)存,但會多損耗一些CPU。
-
Linux
+關(guān)注
關(guān)注
87文章
11304瀏覽量
209538 -
內(nèi)存管理
+關(guān)注
關(guān)注
0文章
168瀏覽量
14142
原文標(biāo)題:郝健: Linux內(nèi)存管理學(xué)習(xí)筆記-第4節(jié)課
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論