在 start_kernel 內核初始化函數中,一共調用 86 個函數去初始化,其中有一個 mm_init 函數,用以初始化內存。
start_kernel
|--- >mm_init
|--- >mem_init
linux4.14/init/main.c
在 mem_init 函數中會初始化伙伴系統和 slab 分配器。
先說兩個概念:
外部碎片 :有一段小內存,夾在兩個大內存中間,兩個大內存已經被分配給進程,這一段小內存由于過小,不夠申請者使用,就一直空閑。
內部碎片 :一個進程申請了一段內存,可是這個進程從來沒有全部使用,一直有最后的一段內存沒有使用。
為了解決這兩個問題,就出現了伙伴系統和 slab 分配器。伙伴系統解決外部碎片問題,slab 分配器解決內部碎片問題。
1、伙伴系統基于頁分配,一次分配多頁,這樣就不會出現夾在中間的小內存。
2、slab 分配器基于字節來分配,特別適用于需要頻繁分配幾十個字節的結構體,我們經常使用的 kmalloc 就是基于 slab 分配器。
3、其實所有的分配方式最底層都是伙伴系統,它先分配好一段大的內存,然后 slab 再從其中分配小的內存。
其中最常用的就是 malloc 和 kmalloc,區別在于一個在用戶空間,一個在內核空間,并且 kmalloc 的使用需要注意競爭,需要指明 flag 。
void *kmalloc(size_t size, int flags);
內核編程(驅動編程)一定要注意競爭問題,重要的數據或者內存使用前后一定要加鎖。
在 kmalloc 的使用過程中,常用標志位:GFP_KERNEL、GFP_ATOMIC、GFP_USER、GFP_HIGHUSER、GFP_NOIO、GFP_NOFS。
前兩個最常用,GFP_KERNEL 代表在使用 kmalloc 分配內存時,如果內存準備不足,會等待,也就是會睡眠。GFP_ATOMIC 代表使用 kmalloc 分配內存時,如果內存準備不足,會立刻返回,不會引起睡眠,適合在中斷上下文或者進程上下文中使用。
補充:
1、基于 slab 分配器,出現了 slob 和 slub 分配器。在多核大系統大內存中,一般使用 slub 分配器,在極小的嵌入式系統中,一般使用 slob 分配器(只有600多行代碼)。
2、有的人可能知道 Linux 有一個 bootmem 分配器,這個是在Linux初始化過程中的一個臨時分配器,他會在 setup_arch 函數中初始化,然后在 mm_init 中關掉,只是在伙伴系統出現之前的臨時使用。
bootmem 分配器按塊進行分配,顆粒度很大,不夠精細,比較浪費內存。bootmem 分配器只會在 start_kernel 函數和mm_init 函數之前存在,中間的函數會調用它進行內存分配。
start_kernel
|--- >setup_arch
|--- >paging_init
|--- >bootmem_init
-
嵌入式
+關注
關注
5082文章
19104瀏覽量
304817 -
內核
+關注
關注
3文章
1372瀏覽量
40280 -
Linux
+關注
關注
87文章
11292瀏覽量
209331 -
內存
+關注
關注
8文章
3019瀏覽量
74005 -
分配器
+關注
關注
0文章
193瀏覽量
25747
發布評論請先 登錄
相關推薦
評論