slab原始碼分析--從slab初始化說起
上次說了 slab 的主要資料結構,這次從初始化開始進行原始碼剖析。
slab 的初始化,當然是從核心啟動就開始了。核心啟動的 start_kernel() 函式:
//核心的啟動程式啊:)
asmlinkage void __init start_kernel(void)
{
...
mem_init(); //記憶體相關初始化
kmem_cache_init(); //slab 初始化
...
}
其中呼叫 kmem_cache_init() 函式,這就是要進行 slab 的初始化。
/*
* Initialisation. Called after the page allocator have been initialised and
* before smp_init().
*/
//這個函式用來建立通用快取器,否則我們無法使用kmalloc
void __init kmem_cache_init(void)
{
size_t left_over;
struct cache_sizes *sizes;
struct cache_names *names;
int i;
int order;
int node;
//如果節點數目只有1個,那麼就不能使用其他節點的shared cache
if (num_possible_nodes() == 1) //貌似當前這個版本還不支援這個,參見#define num_possible_nodes() 1
use_alien_caches = 0; //後續linux版本會支援
//在slab初始化好之前,無法通過kmalloc分配初始化過程中的一些必要物件,只能使用靜態的全域性變數
//待slab初始化後期,再使用kmalloc動態分配物件替換全域性變數!!
//如前所述,先借用 initkem_list3 代替要用到的三鏈,每個節點對應一組三鏈
//initkmem_list3 是個三鏈陣列,這裡迴圈初始化每個節點的三鏈
for (i = 0; i < NUM_INIT_LISTS; i++) { //FIXME: NUM_INIT_LIST 是怎麼求的,為什麼等於2 * MAX_NODES+1 ? 有的版本是 3 * MAX_NODES
kmem_list3_init(&initkmem_list3[i]);
if (i < MAX_NUMNODES)
//全域性變數cache_cache指向的slab cache包含所有的kmem_cache物件,不包含cache_cache本身
//這裡初始化所有記憶體節點kmem_cache的slab三鏈為空
cache_cache.nodelists[i] = NULL;
}
/*
* Fragmentation resistance on low memory - only use bigger
* page orders on machines with more than 32MB of memory.
*/
//全部變數slab_break_gfp_order為每個slab最多佔用幾個頁面,用來減少碎片。
//總結起來意思就是是: (1)如果物理可用記憶體大於32MB,也就是可用記憶體充裕的時候,BREAK_GFP_ORDER_HI這個巨集的值是1,
//這個時候每個slab最多佔用兩個頁面,不過此時不能橫跨3個頁面,除非物件大小大於8192K時才可以(一個頁面大小是4K,也就是4096);
//(2)如果可用記憶體不大於32MB,那麼BREAK_GFP_ORDER_HI值為0,最多也就是允許一個頁面,除非物件超了,否則不能橫跨
if (num_physpages > (32 << 20) >> PAGE_SHIFT)
slab_break_gfp_order = BREAK_GFP_ORDER_HI; //用來確定slab的最大大小
/* Bootstrap is tricky, because several objects are allocated
* from caches that do not exist yet:
* 1) initialize the cache_cache cache: it contains the struct
* kmem_cache structures of all caches, except cache_cache itself:
* cache_cache is statically allocated.
* Initially an __init data area is used for the head array and the
* kmem_list3 structures, it's replaced with a kmalloc allocated
* array at the end of the bootstrap.
* 2) Create the first kmalloc cache.
* The struct kmem_cache for the new cache is allocated normally.
* An __init data area is used for the head array.
* 3) Create the remaining kmalloc caches, with minimally sized
* head arrays.
* 4) Replace the __init data head arrays for cache_cache and the first
* kmalloc cache with kmalloc allocated arrays.
* 5) Replace the __init data for kmem_list3 for cache_cache and
* the other cache's with kmalloc allocated memory.
* 6) Resize the head arrays of the kmalloc caches to their final sizes.
*/
node = numa_node_id(); //獲取節點id,取得的值為0,因為初始化程式單CPU執行,且是0號
/* 1) create the cache_cache */
//初始化cache_chain 為 kmem_cache 連結串列頭部
INIT_LIST_HEAD(&cache_chain);
list_add(&cache_cache.next, &cache_chain);
//設定cache著色的偏移量基本值,也就是L1快取行的大小
cache_cache.colour_off = cache_line_size(); //巨集定義L1快取行的大小 #define cache_line_size() L1_CACHE_BYTES
//初始化cache_cache的per-CPU cache,同樣這裡也不能使用kmalloc,需要使用靜態分配的全域性變數initarray_cache
cache_cache.array[smp_processor_id()] = &initarray_cache.cache;
//初始化slab連結串列,用全域性變數,這裡CACHE_CACHE值為0,是因為cache_cache就是系統第一個的快取器
cache_cache.nodelists[node] = &initkmem_list3[CACHE_CACHE];
/*
* struct kmem_cache size depends on nr_node_ids, which
* can be less than MAX_NUMNODES.
*/
//buffer_size原指用來分配的物件大小,由於cache_cache是用來做 kmem_cache 的分配器的,所以 buffer_size 的大小就是 kmem_cache 的大小
//注意柔性陣列的計算方法,nodelists不佔據 kmem_cache的大小,所以要分開計算,並且注意nodelists陣列在UMA架構只有一個節點,所以只有1個kmem_list3的指標
cache_cache.buffer_size = offsetof(struct kmem_cache, nodelists) +
nr_node_ids * sizeof(struct kmem_list3 *);
#if 0
#if DEBUG
cache_cache.obj_size = cache_cache.buffer_size;
#endif
#endif
//注意這裡又一次計算了 buffer_size 的大小,通過這次計算將buffer_size以 快取行 為單位進行上邊界對齊
//計算分配的物件與cache line的大小對齊後的大小
cache_cache.buffer_size = ALIGN(cache_cache.buffer_size,
cache_line_size());
//計算物件大小的倒數,用於計算物件在slab中的索引
cache_cache.reciprocal_buffer_size =
reciprocal_value(cache_cache.buffer_size);
//計算cache_cache的剩餘空間以及slab中物件的數目,order決定了slab的大小(PAGE_SIZEE<<order)
for (order = 0; order < MAX_ORDER; order++) { //#define MAX_ORDER 11
cache_estimate(order, cache_cache.buffer_size, //buffer_size已經和cache line對齊過
cache_line_size(), 0, &left_over, &cache_cache.num); //計算cache_cache的物件數目
if (cache_cache.num) //num不為0意味著struct kmem_cache物件建立成功,退出,所以在此處就會確定order的大小,以賦值給gfporder
break;
}
BUG_ON(!cache_cache.num); //斷言
cache_cache.gfporder = order; //gfporder表示本slab包含2^gfproder個頁面,注意這裡就從上面的order賦值給 gfporder
//colour_off 就是上面初始化的 L1_CACHE_BYTES,既然已經知道 L1 快取行的大小,我們上步又計算出了浪費空間的大小
//那麼用浪費空間的大小 / L1 快取行的大小,就得出當前可用 colour 的數目,這個數目是累加且迴圈的,可以為0
cache_cache.colour = left_over / cache_cache.colour_off; //確定可用 colour 的數目,單位是 colour_off
//確定slab描述符以及kmem_bufctl_t陣列 針對快取行對齊後的大小
cache_cache.slab_size = ALIGN(cache_cache.num * sizeof(kmem_bufctl_t) +
sizeof(struct slab), cache_line_size());
/* 2+3) create the kmalloc caches */
sizes = malloc_sizes; //malloc_sizes陣列儲存著要分配的大小
names = cache_names; //cache_name儲存cache名
/*
* Initialize the caches that provide memory for the array cache and the
* kmem_list3 structures first. Without this, further allocations will
* bug.
*/
//首先建立struct array_cache 和 struct kmem_list3 所用的通用快取器general cache,它們是後續初始化動作的基礎
//INDEX_AC是計算local cache所用的struct arraycache_init物件在kmalloc size中的索引,即屬於哪一級別大小的general cache,建立此大小級別的cache為local cache所用
sizes[INDEX_AC].cs_cachep = kmem_cache_create(names[INDEX_AC].name,
sizes[INDEX_AC].cs_size,
ARCH_KMALLOC_MINALIGN,
ARCH_KMALLOC_FLAGS|SLAB_PANIC, //#define ARCH_KMALLOC_FLAGS SLAB_HWCACHE_ALIGN,已經對齊過的標記
NULL, NULL);
if (INDEX_AC != INDEX_L3) {
//如果struct kmem_list3 和 struct arraycache_init對應的kmalloc size索引不同,即大小屬於不同的級別,
//則建立struct kmem_list3所用的cache,否則共用一個cache
sizes[INDEX_L3].cs_cachep =
kmem_cache_create(names[INDEX_L3].name,
sizes[INDEX_L3].cs_size,
ARCH_KMALLOC_MINALIGN,
ARCH_KMALLOC_FLAGS|SLAB_PANIC,
NULL, NULL);
}
slab_early_init = 0;//建立完上述兩個通用快取器後,slab early init階段結束,在此之前,不允許建立外接式slab
//sizes->cs_size 初值為是malloc_sizes[0],值應該是從32開始
while (sizes->cs_size != ULONG_MAX) { //迴圈建立kmalloc各級別的通用快取器,ULONG_MAX 是最大索引值,
/*
* For performance, all the general caches are L1 aligned.
* This should be particularly beneficial on SMP boxes, as it
* eliminates(消除) "false sharing".
* Note for systems short on memory removing the alignment will
* allow tighter(緊的) packing of the smaller caches.
*/
if (!sizes->cs_cachep) {
sizes->cs_cachep = kmem_cache_create(names->name,
sizes->cs_size,
ARCH_KMALLOC_MINALIGN,
ARCH_KMALLOC_FLAGS|SLAB_PANIC,
NULL, NULL);
}
#ifdef CONFIG_ZONE_DMA //如果配置DMA,那麼為每個kmem_cache 分配兩個,一個DMA,一個常規
sizes->cs_dmacachep = kmem_cache_create(
names->name_dma,
sizes->cs_size,
ARCH_KMALLOC_MINALIGN,
ARCH_KMALLOC_FLAGS|SLAB_CACHE_DMA|
SLAB_PANIC,
NULL, NULL);
#endif
sizes++; //都是陣列名,直接++,進行迴圈迭代,由小到大分配各個大小的general caches,最大為ULONG_MAX
names++;
}
/* 4) Replace the bootstrap head arrays */
{
struct array_cache *ptr;
//現在要申請arraycache替換之前的initarray_cache
ptr = kmalloc(sizeof(struct arraycache_init), GFP_KERNEL); //GFP_KERNEL 可睡眠申請
//關中斷
local_irq_disable();
BUG_ON(cpu_cache_get(&cache_cache) != &initarray_cache.cache);
memcpy(ptr, cpu_cache_get(&cache_cache),
sizeof(struct arraycache_init)); //將cache_cache中per-cpu對應的array_cache拷貝到ptr
/*
* Do not assume that spinlocks can be initialized via memcpy:
*/
spin_lock_init(&ptr->lock);
cache_cache.array[smp_processor_id()] = ptr; //再讓它指向ptr?
local_irq_enable();
ptr = kmalloc(sizeof(struct arraycache_init), GFP_KERNEL);
local_irq_disable();
BUG_ON(cpu_cache_get(malloc_sizes[INDEX_AC].cs_cachep)
!= &initarray_generic.cache);
memcpy(ptr, cpu_cache_get(malloc_sizes[INDEX_AC].cs_cachep),
sizeof(struct arraycache_init));
/*
* Do not assume that spinlocks can be initialized via memcpy:
*/
spin_lock_init(&ptr->lock);
malloc_sizes[INDEX_AC].cs_cachep->array[smp_processor_id()] =
ptr;
local_irq_enable();
}
/* 5) Replace the bootstrap kmem_list3's */
{
int nid;
/* Replace the static kmem_list3 structures for the boot cpu */
init_list(&cache_cache, &initkmem_list3[CACHE_CACHE], node);
for_each_online_node(nid) {
init_list(malloc_sizes[INDEX_AC].cs_cachep,
&initkmem_list3[SIZE_AC + nid], nid);
if (INDEX_AC != INDEX_L3) {
init_list(malloc_sizes[INDEX_L3].cs_cachep,
&initkmem_list3[SIZE_L3 + nid], nid);
}
}
}
/* 6) resize the head arrays to their final sizes */
{
struct kmem_cache *cachep;
mutex_lock(&cache_chain_mutex);
list_for_each_entry(cachep, &cache_chain, next)
if (enable_cpucache(cachep)) //這個函式先暫時不剖析,是對本地快取的處理
BUG();
mutex_unlock(&cache_chain_mutex);
}
/* Annotate slab for lockdep -- annotate the malloc caches */
init_lock_keys();
/* Done! */
g_cpucache_up = FULL;
/*
* Register a cpu startup notifier callback that initializes
* cpu_cache_get for all new cpus
*/
register_cpu_notifier(&cpucache_notifier);
/*
* The reap timers are started later, with a module init call: That part
* of the kernel is not yet operational.
*/
}
這個函式就是 slab 初始化的主幹。
執行流程:
- 使用 initkmem_list3 和 intarray_cache 兩個靜態量,再加上手動填充cache_cache 的其他變數 ,完成對 cache_cache 的初始化。這是用來快取 kmem_cache 的,相當於是快取器的快取器:)
- 建立 kmalloc 所用的其他通用快取器:
- cache 的名稱和大小放在 names[] 和 malloc_sizes[] 兩個陣列中,對應大小的 cache 可以從 malloc_sizes[] 中找到,索引就是要分配物件的大小。
- 先建立 INDEX_AC 和 INDEX_L3 下標的 cache(array_cache 和 三鏈,如果大小一樣,只建立一次)。
- 迴圈建立 size 陣列中各個大小的 cache。
- 替換靜態本地 cache 全域性變數:
- 替換 malloc_sizes[INDEX_AC].cs_cachep 的 local cache,原本指向靜態變數 initarray_cache.cache。
- 替換 malloc_sizes[INDEX_AC].cx_cachep 的 local cache,原本指向靜態變數 initarray_generic.cache。 ( kmem_cache_create() 中呼叫 setup_cpu_cache() 函式會使用該靜態變數)。
- 替換靜態三鏈:
- 替換 cache_cache 三鏈,原本指向靜態變數的 initkmem_list3。
- 替換 malloc_sizes[INDEX_AC].cs_cachep 三鏈,原本指向靜態變數 initkmem_list3。
- 更新初始化進度
靜態初始化的結構比如:
/* internal cache of cache description objs */
//這就是靜態定義了第一個即通用快取器
static struct kmem_cache cache_cache = {
.batchcount = 1,
.limit = BOOT_CPUCACHE_ENTRIES,
.shared = 1,
.buffer_size = sizeof(struct kmem_cache),
.name = "kmem_cache", //臥槽,名字就叫 kmem_cache
};
或者
static struct arraycache_init initarray_cache /*__initdata*/ =
{ {0, BOOT_CPUCACHE_ENTRIES, 1, 0} };
static struct arraycache_init initarray_generic =
{ {0, BOOT_CPUCACHE_ENTRIES, 1, 0} };
該函式其他輔助函式有:
1. 三鏈初始化函式
static void kmem_list3_init(struct kmem_list3 *parent)
{
INIT_LIST_HEAD(&parent->slabs_full);
INIT_LIST_HEAD(&parent->slabs_partial);
INIT_LIST_HEAD(&parent->slabs_free);
parent->shared = NULL;
parent->alien = NULL;
parent->colour_next = 0;
spin_lock_init(&parent->list_lock);
parent->free_objects = 0;
parent->free_touched = 0;
}
這個沒什麼卵用,就只是初始化。
2. 對齊巨集
#define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1)
#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask))
這個巨集用來控制記憶體以 a 的大小為基本單位對齊,0~7為一組,以此類推。
關於這個巨集詳細解釋可參照:核心巨集ALIGN的含義
3. cache_estimate()函式
這個函式用來計算每個 slab 中物件的數目以及浪費空間的大小,註釋中的管理物件就是前面所說的管理者。
/*
* Calculate the number of objects and left-over bytes for a given buffer size.
*/
static void cache_estimate(unsigned long gfporder, size_t buffer_size,
size_t align, int flags, size_t *left_over,
unsigned int *num)
{
int nr_objs;
size_t mgmt_size;
//slab 大小為 2^gfporder 個頁面
size_t slab_size = PAGE_SIZE << gfporder; //#define PAGE_SIZE 0x400 (即1024),FIXME: 難道一個頁面是 1K ,怎麼可能?
/*
* The slab management structure can be either off the slab or //有off-slab和on-slab兩種方式
* on it. For the latter case, the memory allocated for a
* slab is used for: //這段記憶體被用來儲存:
*
* - The struct slab //slab結構體
* - One kmem_bufctl_t for each object //每個物件的kmem_bufctl_t
* - Padding to respect alignment of @align //對齊的大小
* - @buffer_size bytes for each object //每個物件的大小
*
* If the slab management structure is off the slab, then the
* alignment will already be calculated into the size. Because //如果是off-slab,align早已被計算出來
* the slabs are all pages aligned, the objects will be at the //因為所有的頁面對齊過了,物件申請時會處在正確的位置
* correct alignment when allocated.
*/
//對於外接slab,沒有slab管理物件問題,直接用申請空間除以物件大小就是物件個數
if (flags & CFLGS_OFF_SLAB) {
//外接slab不存在管理物件,全部用於儲存slab物件
mgmt_size = 0;
//所以物件個數 = slab大小 / 物件大小
nr_objs = slab_size / buffer_size; //注意buffer_size已經和cache line對齊過了
//物件個數不許超限
if (nr_objs > SLAB_LIMIT)
nr_objs = SLAB_LIMIT;
} else {
/*
* Ignore padding for the initial guess. The padding
* is at most @align-1 bytes, and @buffer_size is at
* least @align. In the worst case, this result will
* be one greater than the number of objects that fit
* into the memory allocation when taking the padding
* into account.
*/
//內建式slab,slab管理物件與slab物件都在一片記憶體中,此時slab頁面包含:
//一個struct slab 物件,一個kmem_bufctl_t 型別陣列(kmem_bufctl_t 陣列的項數和slab物件數目相同)
//slab大小需要減去管理物件大小,所以物件個數為 剩餘大小 / (每個物件大小 + sizeof(kmem_bufctl_t)), 它們是一一匹配的關係
nr_objs = (slab_size - sizeof(struct slab)) /
(buffer_size + sizeof(kmem_bufctl_t));
/*
* This calculated number will be either the right
* amount, or one greater than what we want.
*/
//如果對齊後超過slab 總大小 ,需要減去一個物件
if (slab_mgmt_size(nr_objs, align) + nr_objs*buffer_size
> slab_size)
nr_objs--;
//物件個數不許超限
if (nr_objs > SLAB_LIMIT)
nr_objs = SLAB_LIMIT;
//計算 管理物件以快取行 對齊後的總大小
mgmt_size = slab_mgmt_size(nr_objs, align);
}
//得出slab最終物件個數
*num = nr_objs;
//前面已經得到了slab管理物件大小(外接為0,內建也已計算),這樣就可以最終的出slab最終浪費空間大小
*left_over = slab_size - nr_objs*buffer_size - mgmt_size;
}
5. malloc_sizes[]表
我們的需要通過 kmem_cache_create() 函式建立通用快取器,最終實際會向夥伴系統申請。那建立多大呢?這個有一個很有意思的地方:
首先看 INDEX_AC:
#define INDEX_AC index_of(sizeof(struct arraycache_init))
而 index_of 是這樣的:
/*
* This function must be completely optimized away if a constant is passed to
* it. Mostly the same as what is in linux/slab.h except it returns an index.
*/
static __always_inline int index_of(const size_t size) //這個函式用來選擇大小的,可作為mallloc_size的引數
{
extern void __bad_size(void);
if (__builtin_constant_p(size)) {
int i = 0;
#define CACHE(x) \
if (size <=x) \ //適配一個剛足夠容納size的大小
return i; \
else \
i++; //不成功,增大繼續適配
#include "linux/kmalloc_sizes.h"
#undef CACHE
__bad_size();
} else
__bad_size();
return 0;
}
為什麼說 index_of 能確定大小呢,這真是有點玄幻了。注意這一句
#include "linux/kmalloc_sizes.h"
而 linux/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 < 128
CACHE(192)
#endif
CACHE(256)
CACHE(512)
CACHE(1024)
CACHE(2048)
CACHE(4096)
CACHE(8192)
CACHE(16384)
CACHE(32768)
CACHE(65536)
CACHE(131072)
#if KMALLOC_MAX_SIZE >= 262144
CACHE(262144)
#endif
#if KMALLOC_MAX_SIZE >= 524288
CACHE(524288)
#endif
#if KMALLOC_MAX_SIZE >= 1048576
CACHE(1048576)
#endif
#if KMALLOC_MAX_SIZE >= 2097152
CACHE(2097152)
#endif
#if KMALLOC_MAX_SIZE >= 4194304
CACHE(4194304)
#endif
#if KMALLOC_MAX_SIZE >= 8388608
CACHE(8388608)
#endif
#if KMALLOC_MAX_SIZE >= 16777216
CACHE(16777216)
#endif
#if KMALLOC_MAX_SIZE >= 33554432
CACHE(33554432)
#endif
在 index_of 函式中區域性定義了 CACHE(x) 巨集,並把這個標頭檔案引入,你可以理解為把標頭檔案中所有的內容都加入到了 index_of 函式的那一句所在的位置。那麼,程式接下來會不停地執行者個區域性巨集 CACHE(X),而這個巨集又是有函式意義的,當匹配到正確的正確的size,會直接返回 i 值(真是神一樣的技巧,相當於用巨集來迴圈,我算是服了)。通過這種方式就確定了要申請的大小對應在 malloc_sizes 表中下標 i 的值。
那麼只知道下標 i 的值可不行,malloc_sizes[]表是什麼時候初始化的呢? 答案是它是靜態初始化的。
/*
* These are the default caches for kmalloc. Custom caches can have other sizes.
*/
struct cache_sizes malloc_sizes[] = { //通用快取器的大小由malloc_size表決定
#define CACHE(x) { .cs_size = (x) },
#include <linux/kmalloc_sizes.h> //終於明白這是什麼用法了
CACHE(ULONG_MAX)
#undef CACHE
};
在上面的程式碼中,同樣定義了 CACHE(X),不過這個可和之前的那個作用不一樣,它們倆都是區域性巨集,使用完即 undef,不會產生影響。
在這裡注意,這個巨集被定義為 “{ .cs_size = (x) },”,注意到這個逗號了沒有?下面會同樣引入 kmalloc_size 標頭檔案中的一大堆 CACHE(x),每個都是這種形式,這不正是陣列的初始化方式嗎?只不過每個資料型別都是 cache_sizes 型別罷了。相當於int array[] = { {1}, {2}, …},大概就是這樣。cache_names[] 表同理。
cache_sizes 型別如下:
/* Size description struct for general caches. */
struct cache_sizes {
size_t cs_size; //通用快取器的大小
struct kmem_cache *cs_cachep; //通用快取器的描述符指標
#ifdef CONFIG_ZONE_DMA
struct kmem_cache *cs_dmacachep;
#endif
};
於是,通過上面這種初始化方式,就把 malloc_sizes[] 表中按照由小到大順序將全部 cache_sizes的 cs_size 成員初始化了。所以 malloc_size[i] 對應的就是描述大小為 i 的快取器的 cache_sizes 結構,通過 .cs_cachep 就可以得到對應大小的通用快取器!
如果是 DMA,那麼會配置兩種通用快取器。
6. kmem_cache_create()函式
這個函式是核心,對該函式的詳細分析會在下一篇部落格出現,很長的:)
7. kmalloc()函式
也是重點,以後會單獨剖析。
8. init_list()函式
/*
* swap the static kmem_list3 with kmalloced memory
*/
static void init_list(struct kmem_cache *cachep, struct kmem_list3 *list,
int nodeid)
{
struct kmem_list3 *ptr;
ptr = kmalloc_node(sizeof(struct kmem_list3), GFP_KERNEL, nodeid);
BUG_ON(!ptr);
local_irq_disable();
memcpy(ptr, list, sizeof(struct kmem_list3));
/*
* Do not assume that spinlocks can be initialized via memcpy:
*/
spin_lock_init(&ptr->list_lock);
MAKE_ALL_LISTS(cachep, ptr, nodeid);
cachep->nodelists[nodeid] = ptr;
local_irq_enable();
}
這是利用 kmalloc 申請三鏈和靜態三鏈交換的函式,為什麼說是利用 kmalloc 呢,且看它呼叫的 kmalloc_node() 函式:
static inline void *kmalloc_node(size_t size, gfp_t flags, int node)
{
return kmalloc(size, flags);
}
就是這樣,我們為了解決“雞與蛋”的問題,首先設定幾個靜態變數,比如三鏈 initkmem_list3,先用它合成我們的 cache_cache,然後有了 cache_cache,這是為快取器制定規則的快取器,通過它我們就可以隨意合成大小其他快取器了。有了各種大小的快取器,在 kmem_cache_init() 函式後半部分就可以用快取器來 kmalloc 分配物件,替換之前所有用來輔助 cache_cache 的靜態資料結構(如 initarray_cache, initkmem_list3),以後這些靜態資料結構就不再使用了。
cache_cache 為快取器大小制定了一種規則,這說明所有快取器自身大小也是一樣的,只不過它們的 buffer_size 欄位描述的用來分配的物件大小是不一樣的,以後不同大小的物件就靠這個找不同的快取器分配就行了。
參考:
PS: 最近是怎麼了,有點慌,覺得學了幾天才學了這麼一點東西,感覺好沒有成就感。嗯,沒慌,搞技術的要靜的下心來。
相關文章
- slab原始碼分析--kmalloc函式分析原始碼函式
- slab原始碼分析--銷燬函式原始碼函式
- slab原始碼分析--主要資料結構分析原始碼資料結構
- slab原始碼分析--快取器的建立原始碼快取
- 從核心原始碼看 slab 記憶體池的建立初始化流程原始碼記憶體
- slab原始碼分析--setup_cpu_cache函式原始碼函式
- JUnit原始碼分析(四)——從Decorator模式說起原始碼模式
- slab機制總結篇
- 簡單理解Memcached的Slab Allocation
- Linux 記憶體區管理 slabLinux記憶體
- Slab: 保證JVM的堆記憶體對齊JVM記憶體
- SpringMVC原始碼剖析(一)- 從抽象和介面說起SpringMVC原始碼抽象
- 從程式碼生成說起,帶你深入理解 mybatis generator 原始碼MyBatis原始碼
- Koa原始碼閱讀(一)從搭建Web伺服器說起原始碼Web伺服器
- zanphp原始碼解讀 – MVC說起PHP原始碼MVC
- Dubbo原始碼分析(二)Dubbo是從哪裡初始化的?原始碼
- nginx原始碼分析——初始化logNginx原始碼
- 深入理解 slab cache 記憶體分配全鏈路實現記憶體
- 故障分析 | 從 data_free 異常說起
- 從原始碼分析IntentService原始碼Intent
- 從 JSON 說起JSON
- 從Promise的Then說起Promise
- 從原始碼分析Axios原始碼iOS
- Deno原理詳解,讓我們一起從原始碼分析開始原始碼
- TreeMap原始碼分析,看了都說好原始碼
- iOS逆向——從RSA說起iOS
- 從程式猿入行說起
- Spring原始碼分析(四)SpringMVC初始化原始碼SpringMVC
- MapReduce job在JobTracker初始化原始碼級分析原始碼
- NEO從原始碼分析看NEOVM原始碼
- 從面試角度分析ArrayList原始碼面試原始碼
- kube-scheduler原始碼分析(1)-初始化與啟動分析原始碼
- 從一個埋點日誌上報指令碼說起指令碼
- 從事件系統說起,更好的組織程式碼事件
- k8s client-go原始碼分析 informer原始碼分析(2)-初始化與啟動分析K8SclientGo原始碼ORM
- 從兩道面試題說起面試題
- 從CSS盒子模型說起CSS模型
- 動畫篇-從UIView動畫說起動畫UIView