內核空間 和用戶空間申請的內存最終和buddy怎么交互?以及在頁表映射上的區別?虛擬地址到物理地址,什么時候開始映射?
Buddy的問題
分配的力度太大 buddy算法把空閑頁面分成1,2,4頁,buddy算法會明確知道哪一頁內存空閑還是被占用?
4k,8k,16k
無論是在應用還是內核,都需要申請很小的內存。
從buddy要到的內存,會進行slab切割。
slab原理:
比如在內核中申請8字節的內存,buddy分配4K,分成很多個小的8個字節,每個都是一個object。
slab,slub,slob 是slab機制的三種不同實現算法。
linux 會針對一些常規的小的內存申請,數據結構,會做slab申請。
cat /proc/slabinfo 可以看到內核空間小塊內存的申請情況,也是slab分配的情況。
<num_objs>:每個slab一共可以分出多少個obj, <active_objs> :還可以分配多少個obj, < pagesperslab>:每個slab對應多少個pages, < objperslab>:每個slab可以分出多少個object, < objsize>:每個obj多大,
slab主要分為兩類:
一、常用數據結構像 nfsd_drc, UDPv6,TCPv6 ,這些經常申請和釋放的數據結構。比如,存在TCPv6的slab,之后申請 TCPv6 數據結構時,會通過這個slab來申請。
二、常規的小內存申請,做的slab。例如 kmalloc-32,kmalloc-64, kmalloc-96, kmalloc-128
注意,slab申請和分配的都是只針對內核空間,與用戶空間申請分配內存無關。用戶空間的malloc和free調用的是libc。
更多Linux內核視頻教程文檔資料免費領取后臺私信【內核大禮包】自行獲取。
slab和buddy的關系?
1、slab的內存來自于buddy。slab相當于二級管理器。
2、slab和buddy在算法上,級別是對等的。
兩者都是內存分配器,buddy是把內存條分成多個Zone來管理分配,slab是把從buddy拿到的內存,進行管理分配。
同理,malloc 和free也不找buddy拿內存。 malloc 和free不是系統調用,只是c庫中的函數。
mallopt
在C庫中有一個api是mallopt,可以控制一系列的選項。
M_TRIM_THRESHOLD:控制c庫把內存還給內核的閾值。
-1UL 代表最大的正整數。
此處代表應用程序把內存還給c庫后,c庫并不把內存還給內核。
<do your RT-thing>
程序在此處申請內存,都不需要再和內核交互了,此時程序的實時性比較高。
kmalloc vs. vmalloc/ioremap
內存空間: 內存+寄存器
register --> LDR/STR
所有內存空間的東西,CPU去訪問,都要通過虛擬地址。 CPU --> virt --> mmu --> phys
cpu請求虛擬地址,mmu根據cpu請求的虛擬地址,查頁表的物理地址。
buddy算法,管理這一頁的使用情況。
兩個虛擬地址可以映射到同一個物理地址。
頁表 -> 數組,任何一個虛擬地址,都可以用地址的高20位,作為頁表的行號去讀對應的頁表項。而第12位,是指頁面內偏移。(由于一頁是4K,2^12 足夠描述)
kmalloc 和 vmalloc 申請的內存,有什么區別? 答:申請之后,是否還要去改頁表。一般情況,kmalloc申請內存,不需要再去改頁表。同一張頁表,幾個虛擬地址可以同時映射到同一個物理地址。
寄存器,通過ioremap往vmalloc區域,進行映射。然后改進程的虛擬地址頁表。
總結:所有的頁,最底層都是用buddy算法進行管理,用虛擬地址找物理地址。理解內存分配和映射的區別,無論是lowmem還是highmem 都可以被vmalloc拿走,也可能被用戶拿走,只不過拿走之后,還要把虛擬地址往物理地址再映射一遍。但如果是被kmalloc拿走,一般指低端內存,就不需要再改進程的頁表。因為這部分低端內存,已經做好了虛實映射。
cat /proc/vmallocinfo |grep ioremap
可以看到寄存器中的哪個區域,被映射到哪個虛擬地址。
vmalloc區域主要用來,vmalloc申請的內存從這里找虛擬地址 和 寄存器的ioremap映射。
Linux內存分配的lazy行為
Linux總是以最lazy的方式,給應用程序分配內存。
malloc100M內存成功時,其實并沒有真實拿到。只有當100M內存中的任何一頁,被寫一次的時候,才成功。vss:虛擬地址空間。 rss:常駐內存空間malloc 100M內存成功時,Linux把100M內存全部以只讀的形式,映射到一個全部清0的頁面。
當應用程序寫100M中每一頁任意字節時,會發出page fault。 linux 內核收到缺頁中斷后,從硬件寄存器中讀取到,包括缺頁中斷發生的原因和虛擬地址。Linux從內存條申請一頁內存,執行cow,把頁面重新拷貝到新申請的頁表,再把進程頁表中的虛擬地址,指向一個新的物理地址,權限也被改成R+W。
調用brk 把8k變成 16k。
針對應用程序的堆、代碼、棧、等,會使用lazy分配機制,只有當寫內存頁時,才會真實請求內存分配頁表。但,當內核使用kmalloc申請內存時,就真實地分配相應的內存,不使用lazy機制。
內存OOM
當真實去寫內存時,應用程序并不能拿到真實的內存時。Linux啟動OOM,linux在運行時,會對每一個進程進行out-of-memory打分。大部分主要基于,耗費的內存。耗費的內存越多,打分越高。
cat /proc/<pid>/oom_score
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
int max = -1;
int mb = 0;
char *buffer;
int i;
#define SIZE 2000
unsigned int *p = malloc(1024 * 1024 * SIZE);
printf("malloc buffer: %pn", p);
for (i = 0; i < 1024 * 1024 * (SIZE/sizeof(int)); i++) {
p[i] = 123;
if ((i & 0xFFFFF) == 0) {
printf("%dMB writtenn", i >> 18);
usleep(100000);
}
}
pause();
return 0;
}
定條件:
總內存1G
1、swapoff -a 關掉swap交換
2、echo 1 >
/proc/sys/vm/overcommit_memory
3、內核不去評估系統還有多少空閑內存
Linux進行OOM打分,主要是看耗費內存情況,此外還會參考用戶權限,比如root權限,打分會減少30分。
還有OOM打分因子:/proc/pid/oom_score_adj (加減)和 /proc/pid/oom_adj (乘除)。
總結:
1、slab的作用,針對在內核空間小內存分配,和常用數據結構的申請。
2、同樣的二次分配器,在用戶空間是C庫。malloc和free的時候,內存不一定從buddy分配和還給buddy。
3、kmalloc,vmalloc 和malloc的區別
- kmalloc:申請內存,一般在低端內存區。申請到時,內存已經映射過了,不需要再去改進程的頁表。所以,申請到的物理頁是連續的。
- vmalloc:申請內存,申請到就拿到內存,并且已經修改了進程頁表的虛擬地址到物理地址的映射。vmalloc()申請的內存并不保證物理地址的連續。
- 用戶空間的malloc:申請內存,申請到并沒有拿到,寫的時候才去拿到。拿到之后,才去改頁表。申請成功,頁表只讀,只有到寫時,發生page fault,才去buddy拿內存。
- kmalloc和vmalloc針對內核空間,malloc針對用戶空間。這些內存,可以來自任何一個Zone。
- 無論是kmalloc,vmalloc還是用戶空間的malloc,都可以使用內存條的不同Zone,無論是highmem zone、lowmem zone 和 DMA zone。
4、如果在從buddy拿不到內存時,會觸發Linux對所有進程進行OOM打分。當Linux出現內存耗盡,就kill一個oom score 最高的是那個進程。oom_score,可以根據 oom_adj (-17~25)。
Android/ target=_blank class=infotextkey>安卓的程序,不停地調整前臺和后臺進程oom_score,當被切換到后臺時,oom_score會被調整得比較大。以保證前臺的進程不容易因為oom而kill掉。