linux 內存管理 二

疫情憋在家,繼續寫下筆記,Buddy 算法把空閒頁面管理起來,一個已經被佔用的頁面,其他人肯定不能申請到的,這些頁面都是4k 8k 16k .... ,無論是在內核還是在應用,有時候都要申請很小的內存。比如說我們在內核中需要申請16Bytes,Buddy 還是申請4K的內存,這樣就很亂費,所以在內核裏面會進行內存的二次管理,在Buddy中申請的內存進行切割,內核中這種機制叫做slab。

Slab 原理:

Slab 會把從buddy分配的內存分配爲很多小的object,當所有小的object分配完了,再次從buddy 申請頁拆分,類似於現在疫情期間,媽媽去超市買一個大塊的牛肉,分成很多小塊,每天喫一塊,當所有的牛肉喫完了,就再次去超市買一大塊。Slab ,slub, slob 都是slab的算法,這是這種機制的三種不同的實現。

Slab主要分成兩類,一類是內核中常用的數據結構,內核會給他們做一個slab,比如下面圖中的TCPv6 的slab ,內核中經常要分配使用,所以這個專門用slab專門用於TCPv6的內存分配。針對第二幅圖中kmalloc16 kmalloc-8 等體現出來針對的常規的小內存神奇,可以看到沒法直接申請48個字節,所以如果我們申請48Bytes,實際申請的也是64Bytes。Slabinfo的頭部顯示了slab的詳細信息。

不過slab指針對內核空間的,跟應用是沒有任何關係的,用戶空間調用的malloc是調用libc的

Slab的內存來自於buddy,他們在算法級別上是對等的,buddy是把你的內存條當作一個池子管理,slab 是把從buddy申請的內存當作池子管理,他是一個二級管理,針對的是內存空間的kmalloc。不僅僅內核空間有個二級管理器,用戶空間同樣存在一個二級管理器,當你在用戶空間調用malloc ,malloc不是系統調用,他是從libc中獲取的,所以應用的內存申請和釋放,並不一定對應內核的內存申請和釋放,libc不一定會釋放給內核。

所以應用程序開發,特別是針對RT程序,有一些技巧,下劃線的配置告訴C庫,無論應用釋放多少內存都不需要還給內核。這個接口是設置c庫釋放給內核的閥值,-1 就是永遠不還。所以不管這個應用程序申請和釋放內存,都是在用戶態進行的,所以他的實時性就會好。

下面是內存映射和申請的描述,不管是低端內核,還是高端內存都有可能被kmalloc vmalloc, 用戶空間申請走。 被映射不等於被申請,完全存在兩個虛擬地址對應一個物理地址。用戶空間和vmalloc 申請跟kmalloc申請的區別是,需要更改相應的頁表,kmalloc 申請的不需要改頁表,因爲開機已經一一映射了

下面這張圖更詳細描述他們之間的關係,這個是理解linux 內存非常重要的概念

Vmalloc 這個映射去,除了使用vmalloc 映射外,所有的寄存器通過ioremap也是映射到這裏,vmalloc 申請的內存虛擬連續,物理不一定連續,kmalloc申請的內存物理和虛擬都是連續的。

用戶空間申請的內存的時候都採用lazy的機制,比如用戶空間申請100M空間返回的時候,其實一頁都沒有拿到。內核欺騙用戶,這100M的虛擬地址都指向同一物理地址0頁,並且頁表中標記爲只讀。只有當你一頁一頁寫的時候,每寫一頁就發生page fault,內核再給應用申請對應的頁,更改頁表爲新申請的內存,並且爲可讀寫。

VSS 是你的虛擬地址空間,RSS 是真正在內存裏面駐留的內存,當你malloc 100M時,VSS是100M ,這個時候RSS並沒有,只有你一頁頁的寫,RSS 纔會一頁頁增加。所以page fault 是應用程序真正拿到內存的一個觸發源,所以完全可能出現一種情況,是後面真正寫的時候,內存不夠了,這時候linux就會啓動著名的oom 機制,oom機制會找到一個最該殺掉的內存kill掉,從而騰出內存使用。

Linux 會對每個進程進行oom socre打分,把最高分數的優先殺掉,一般內存消耗越多,分數會越高,也會根據不同用戶有差別,比如root用戶會減去30,也可以通過oom_score_adj 進行調整,打分因子如下:

Android 整個生命週期的管理完全是靠OOM的,Android 是前臺執行時,內存不夠纔會去殺後臺進程,這個過程也是通過調高oom_adj 的值來實現的。但是對於一個嵌入式系統,內存是預期的,所以出現oom是有問題的,所以對於嵌入式系統一般會使能一個參數,/proc/sys/vm/panic_on_oom ,一旦系統出現內存耗盡,就會讓內核崩潰,系統重啓。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章