slab原始碼分析--kmalloc函式分析
這次來說一個關鍵函式,kmalloc()函式。
函式原型
#include Linux/slab.h>
void *kmalloc(size_t size, int flags);
size是要分配記憶體的大小,不過核心會對大小進行適配,比如取32,64等等,是和快取行等體系結構有關係的,總之可能會比你要申請的記憶體大一些。
flags是與夥伴系統互動的標記,雖說kmalloc()是從slab分配記憶體,不過底層還是要和夥伴系統互動的。
我們來看一下這些標記:
flags | 含義 |
---|---|
GFP_ATOMIC | 用來從中斷處理和程式上下文之外的其他程式碼中分配記憶體,從不睡眠 |
GFP_KERNEL | 核心記憶體的正常分配,可能睡眠 |
GFP_USER | 用來為使用者空間頁分配記憶體,可能睡眠 |
GFP_HIGHUSER | 類似GPR_USER,但是是從高階記憶體分配。 |
GFP_NOIO | 類似GFP_KERNEL,禁止任何檔案系統呼叫 |
GFP_NOFD | 類似GFP_KERNEL,禁止任何I/O初始化 |
上面這些標誌底層是組合了下面這些標誌實現的:
__GFP_DMA
這個標誌要求分配在能夠 DMA 的記憶體區. 確切的含義是平臺依賴的並且在下面章節來解釋.
__GFP_HIGHMEM
這個標誌指示分配的記憶體可以位於高階記憶體.
__GFP_COLD
正常地, 記憶體分配器盡力返回”緩衝熱”的頁 – 可能在處理器緩衝中找到的頁. 相反, 這個標誌請求一個”冷”頁, 它在一段時間沒被使用. 它對分配頁作 DMA 讀是有用的, 此時在處理器緩衝中出現是無用的. 一個完整的對如何分配 DMA 快取的討論看”直接記憶體存取”一節在第 1 章.
__GFP_NOWARN
這個很少用到的標誌阻止核心來發出警告(使用 printk ), 當一個分配無法滿足.
__GFP_HIGH
這個標誌標識了一個高優先順序請求, 它被允許來消耗甚至被核心保留給緊急狀況的最後的記憶體頁.
__GFP_REPEAT
__GFP_NOFAIL
__GFP_NORETRY
這些標誌修改分配器如何動作, 當它有困難滿足一個分配. __GFP_REPEAT 意思是” 更盡力些嘗試” 通過重複嘗試 – 但是分配可能仍然失敗. __GFP_NOFAIL 標誌告訴分配器不要失敗; 它盡最大努力來滿足要求. 使用 __GFP_NOFAIL 是強烈不推薦的; 可能從不會有有效的理由在一個裝置驅動中使用它. 最後, __GFP_NORETRY 告知分配器立即放棄如果得不到請求的記憶體.
原始碼剖析
直接看kmalloc函式:
static inline void *kmalloc(size_t size, gfp_t flags)
{
if (__builtin_constant_p(size)) {
int i = 0;
#define CACHE(x) \
if (size <= x) \
goto found; \
else \
i++;
#include "kmalloc_sizes.h"
#undef CACHE
{
extern void __you_cannot_kmalloc_that_much(void);
__you_cannot_kmalloc_that_much();
}
found:
#ifdef CONFIG_ZONE_DMA
if (flags & GFP_DMA)
return kmem_cache_alloc(malloc_sizes[i].cs_dmacachep,
flags);
#endif
return kmem_cache_alloc(malloc_sizes[i].cs_cachep, flags);
}
return __kmalloc(size, flags);
}
首先在本函式中手動確定要申請的記憶體大小,(1) 如果成功就跳至found,執行kmem_cache_alloc(),因為它的大小已經找到。kmem_cache_alloc()底層轉呼叫__cache_alloc()函式。(2) 如果不成功,執行__kmalloc()函式,而__kmalloc()函式底層也是會適配大小的,它的呼叫關係為__kmalloc()->__do_kmalloc(),然後在__do_kmalloc()函式中會執行__find_general_cachep(),這個函式也是用來手動確定大小的函式,大小確定後呼叫__cache_alloc()函式。所以兩種情況最終都會確定要分配記憶體的大小,交由__cache_alloc()函式執行分配工作。
大小是怎麼確定的?參見我先前的部落格: slab原始碼分析–從slab初始化說起
不過這裡還是要提一下,關於大小的一點小知識,首先看kmalloc_sizes.h中的定義:
#if (PAGE_SIZE == 4096)
CACHE(32)
#endif
CACHE(64)
#if L1_CACHE_BYTES < 64
CACHE(96)
#endif
CACHE(128)
注意這一句,#IF L1_CACHE_BYTES < 64,CACHE(96),也就是說如果快取行大小大於等於64,就不會分配96位元組的記憶體,而是直接到128。因為96-64=32小於快取行的大小64。從這裡可以看出,如果一頁大小是4K,那麼最小分配的記憶體大小是32位元組,其他允許分配的記憶體大小還可能取決於快取行的大小。
由於kmalloc()底層呼叫__cache_alloc()函式,這是它和kmem_cache_create()函式公共的介面,我在之前部落格 slab原始碼分析–從slab初始化說起已經說過了,就不必細說了。
相關文章
- slab原始碼分析--銷燬函式原始碼函式
- slab原始碼分析--setup_cpu_cache函式原始碼函式
- slab原始碼分析--主要資料結構分析原始碼資料結構
- slab原始碼分析--從slab初始化說起原始碼
- count 函式原始碼分析函式原始碼
- slab原始碼分析--快取器的建立原始碼快取
- 核心堆分配函式brk()原始碼分析函式原始碼
- redux原始碼分析之四:compose函式Redux原始碼函式
- Vue中之nextTick函式原始碼分析Vue函式原始碼
- PHP原始碼分析-函式array_merge的”BUG”PHP原始碼函式
- Oracle分析函式七——分析函式案例Oracle函式
- 分析函式函式
- Retrofit原始碼分析三 原始碼分析原始碼
- webpack 4.0 Tapable 類中的常用鉤子函式原始碼分析Web函式原始碼
- Zepto 原始碼分析 3 - qsa 實現與工具函式設計原始碼函式
- LiteOS-任務篇-原始碼分析-任務排程函式原始碼函式
- LiteOS-任務篇-原始碼分析-系統啟動函式原始碼函式
- STL原始碼之rotate函式結合圖和例項分析原始碼函式
- Oracle聚合函式/分析函式Oracle函式
- 集合原始碼分析[2]-AbstractList 原始碼分析原始碼
- 集合原始碼分析[1]-Collection 原始碼分析原始碼
- 集合原始碼分析[3]-ArrayList 原始碼分析原始碼
- Guava 原始碼分析之 EventBus 原始碼分析Guava原始碼
- 【分析函式】Oracle分析函式之LAG和LEAD函式Oracle
- 分析函式概述函式
- 分析函式 over函式
- Oracle 分析函式Oracle函式
- 分析函式 - LAG函式
- Oracle分析函式Oracle函式
- Android 原始碼分析之 AsyncTask 原始碼分析Android原始碼
- 【JDK原始碼分析系列】ArrayBlockingQueue原始碼分析JDK原始碼BloC
- 以太坊原始碼分析(36)ethdb原始碼分析原始碼
- 以太坊原始碼分析(38)event原始碼分析原始碼
- 以太坊原始碼分析(41)hashimoto原始碼分析原始碼
- 以太坊原始碼分析(43)node原始碼分析原始碼
- 以太坊原始碼分析(52)trie原始碼分析原始碼
- Java HashMap原始碼分析(含雜湊表、紅黑樹、擾動函式等重點問題分析)JavaHashMap原始碼函式
- 深度 Mybatis 3 原始碼分析(一)SqlSessionFactoryBuilder原始碼分析MyBatis原始碼SQLSessionUI