slab原始碼分析--kmalloc函式分析

FreeeLinux發表於2017-01-16

這次來說一個關鍵函式,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初始化說起已經說過了,就不必細說了。

相關文章