Linux內存管理之slab機制(初始化)
Linux內存管理之slab機制(初始化)
一、內核啟動早期初始化
start_kernel()->mm_init()->kmem_cache_init()
執行流程:
1,初始化靜態initkmem_list3三鏈;
2,初始化cache_cache的nodelists欄位為1中的三鏈;
3,根據內存情況初始化每個slab佔用的頁面數變數slab_break_gfp_order;
4,將cache_cache加入cache_chain鏈表中,初始化cache_cache;
5,創建kmalloc所用的general cache:
1)cache的名稱和大小存放在兩個數據結構對應的數組中,對應大小的cache可以從size數組中找到;
2)先創建INDEX_AC和INDEX_L3下標的cache;
3)循環創建size數組中各個大小的cache;
6,替換靜態本地cache全局變數:
1) 替換cache_cache中的arry_cache,本來指向靜態變數initarray_cache.cache;
2) 替換malloc_sizes.cs_cachep的local cache,原本指向靜態變數initarray_generic.cache;
7,替換靜態三鏈
1)替換cache_cache三鏈,原本指向靜態變數initkmem_list3;
2)替換malloc_sizes.cs_cachep三鏈,原本指向靜態變數initkmem_list3;
8,更新初始化進度
view plaincopy to clipboardprint?/*
* Initialisation. Called after the page allocator have been initialised and
* before smp_init().
*/
void __init kmem_cache_init(void)
{
size_t left_over;
struct cache_sizes *sizes;
struct cache_names *names;
int i;
int order;
int node;
/* 在slab初始化好之前,無法通過kmalloc分配初始化過程中必要的一些對象
,只能使用靜態的全局變數
,待slab初始化後期,再使用kmalloc動態分配的對象替換全局變數 */
/* 如前所述,先借用全局變數initkmem_list3表示的slab三鏈
,每個內存節點對應一組slab三鏈。initkmem_list3是個slab三鏈數組,對於每個內存節點,包含三組
:struct kmem_cache的slab三鏈、struct arraycache_init的slab 三鏈、struct kmem_list3的slab三鏈
。這裡循環初始化所有內存節點的所有slab三鏈 */
if (num_possible_nodes() == 1)
use_alien_caches = 0;
/*初始化所有node的所有slab中的三個鏈表*/
for (i = 0; i < NUM_INIT_LISTS; i++) {
kmem_list3_init(&initkmem_list3);
/* 全局變數cache_cache指向的slab cache包含所有struct kmem_cache對象,不包含cache_cache本身
。這裡初始化所有內存節點的struct kmem_cache的slab三鏈為空。*/
if (i < MAX_NUMNODES)
cache_cache.nodelists = NULL;
}
/* 設置struct kmem_cache的slab三鏈指向initkmem_list3中的一組slab三鏈,
CACHE_CACHE為cache在內核cache鏈表中的索引,
struct kmem_cache對應的cache是內核中創建的第一個cache
,故CACHE_CACHE為0 */
set_up_list3s(&cache_cache, CACHE_CACHE);
/*
* Fragmentation resistance on low memory - only use bigger
* page orders on machines with more than 32MB of memory.
*/
/* 全局變數slab_break_gfp_order為每個slab最多佔用幾個頁面
,用來抑制碎片,比如大小為3360的對象
,如果其slab只佔一個頁面,碎片為736
,slab佔用兩個頁面,則碎片大小也翻倍
。只有當對象很大
,以至於slab中連一個對象都放不下時
,才可以超過這個值
。有兩個可能的取值
:當可用內存大於32MB時
,BREAK_GFP_ORDER_HI為1
,即每個slab最多佔用2個頁面
,只有當對象大小大於8192時
,才可以突破slab_break_gfp_order的限制
。小於等於32MB時BREAK_GFP_ORDER_LO為0。*/
if (totalram_pages > (32 << 20) >> PAGE_SHIFT)
slab_break_gfp_order = BREAK_GFP_ORDER_HI;
/* 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();
/* 1) create the cache_cache */
/* 第一步,創建struct kmem_cache所在的cache,由全局變數cache_cache指向
,這裡只是初始化數據結構
,並未真正創建這些對象,要待分配時才創建。*/
/* 全局變數cache_chain是內核slab cache鏈表的表頭 */
INIT_LIST_HEAD(&cache_chain);
/* 將cache_cache加入到slab cache鏈表 */
list_add(&cache_cache.next, &cache_chain);
/* 設置cache著色基本單位為cache line的大小:32位元組 */
cache_cache.colour_off = cache_line_size();
/* 初始化cache_cache的local cache,同樣這裡也不能使用kmalloc
,需要使用靜態分配的全局變數initarray_cache */
cache_cache.array = &initarray_cache.cache;
/* 初始化slab鏈表 ,用全局變數*/
cache_cache.nodelists = &initkmem_list3;
/*
* struct kmem_cache size depends on nr_node_ids, which
* can be less than MAX_NUMNODES.
*/
/* buffer_size保存slab中對象的大小,這裡是計算struct kmem_cache的大小
, nodelists是最後一個成員
,nr_node_ids保存內存節點個數,UMA為1
,所以nodelists偏移加上1個struct kmem_list3 的大小即為struct kmem_cache的大小 */
cache_cache.buffer_size = offsetof(struct kmem_cache, nodelists) +
nr_node_ids * sizeof(struct kmem_list3 *);
#if DEBUG
cache_cache.obj_size = cache_cache.buffer_size;
#endif
/* 將對象大小與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);
for (order = 0; order < MAX_ORDER; order++) {
/* 計算cache_cache中的對象數目 */
cache_estimate(order, cache_cache.buffer_size,
cache_line_size(), 0, &left_over, &cache_cache.num);
/* num不為0意味著創建struct kmem_cache對象成功,退出 */
if (cache_cache.num)
break;
}
BUG_ON(!cache_cache.num);
/* gfporder表示本slab包含2^gfporder個頁面 */
cache_cache.gfporder = order;
/* 著色區的大小,以colour_off為單位 */
cache_cache.colour = left_over / cache_cache.colour_off;
/* slab管理對象的大小 */
cache_cache.slab_size = ALIGN(cache_cache.num * sizeof(kmem_bufctl_t) +
sizeof(struct slab), cache_line_size());
/* 2+3) create the kmalloc caches */
/* 第二步,創建kmalloc所用的general cache
,kmalloc所用的對象按大小分級
,malloc_sizes保存大小,cache_names保存cache名 */
sizes = malloc_sizes;
names = cache_names;
/*
* 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.cs_cachep = kmem_cache_create(names.name,
sizes.cs_size,
ARCH_KMALLOC_MINALIGN,
ARCH_KMALLOC_FLAGS|SLAB_PANIC,
NULL);
/* 如果struct kmem_list3和struct arraycache_init對應的kmalloc size索引不同
,即大小屬於不同的級別
,則創建struct kmem_list3所用的cache,否則共用一個cache */
if (INDEX_AC != INDEX_L3) {
sizes.cs_cachep =
kmem_cache_create(names.name,
sizes.cs_size,
ARCH_KMALLOC_MINALIGN,
ARCH_KMALLOC_FLAGS|SLAB_PANIC,
NULL);
}
/* 創建完上述兩個general cache后,slab early init階段結束,在此之前
,不允許創建外置式slab */
slab_early_init = 0;
/* 循環創建kmalloc各級別的general cache */
while (sizes->cs_size != 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.
*/
/* 某級別的kmalloc cache還未創建,創建之,struct kmem_list3和
struct arraycache_init對應的cache已經創建過了 */
if (!sizes->cs_cachep) {
sizes->cs_cachep = kmem_cache_create(names->name,
sizes->cs_size,
ARCH_KMALLOC_MINALIGN,
ARCH_KMALLOC_FLAGS|SLAB_PANIC,
NULL);
}
#ifdef CONFIG_ZONE_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);
#endif
sizes++;
names++;
}
/* 至此,kmalloc general cache已經創建完畢,可以拿來使用了 */
/* 4) Replace the bootstrap head arrays */
/* 第四步,用kmalloc對象替換靜態分配的全局變數
。到目前為止一共使用了兩個全局local cache
,一個是cache_cache的local cache指向initarray_cache.cache
,另一個是malloc_sizes.cs_cachep的local cache指向initarray_generic.cache
,參見setup_cpu_cache函數。這裡替換它們。*/
{
struct array_cache *ptr;
/* 申請cache_cache所用local cache的空間 */
ptr = kmalloc(sizeof(struct arraycache_init), GFP_NOWAIT);
BUG_ON(cpu_cache_get(&cache_cache) != &initarray_cache.cache);
/* 複製原cache_cache的local cache,即initarray_cache,到新的位置 */
memcpy(ptr, cpu_cache_get(&cache_cache),
sizeof(struct arraycache_init));
/*
* Do not assume that spinlocks can be initialized via memcpy:
*/
spin_lock_init(&ptr->lock);
/* cache_cache的local cache指向新的位置 */
cache_cache.array = ptr;
/* 申請malloc_sizes.cs_cachep所用local cache的空間 */
ptr = kmalloc(sizeof(struct arraycache_init), GFP_NOWAIT);
BUG_ON(cpu_cache_get(malloc_sizes.cs_cachep)
!= &initarray_generic.cache);
/* 複製原local cache到新分配的位置,注意此時local cache的大小是固定的 */
memcpy(ptr, cpu_cache_get(malloc_sizes.cs_cachep),
sizeof(struct arraycache_init));
/*
* Do not assume that spinlocks can be initialized via memcpy:
*/
spin_lock_init(&ptr->lock);
malloc_sizes.cs_cachep->array =
ptr;
}
/* 5) Replace the bootstrap kmem_list3's */
/* 第五步,與第四步類似,用kmalloc的空間替換靜態分配的slab三鏈 */
{
int nid;
/* UMA只有一個節點 */
for_each_online_node(nid) {
/* 複製struct kmem_cache的slab三鏈 */
init_list(&cache_cache, &initkmem_list3, nid);
/* 複製struct arraycache_init的slab三鏈 */
init_list(malloc_sizes.cs_cachep,
&initkmem_list3, nid);
/* 複製struct kmem_list3的slab三鏈 */
if (INDEX_AC != INDEX_L3) {
init_list(malloc_sizes.cs_cachep,
&initkmem_list3, nid);
}
}
}
/* 更新slab系統初始化進度 */
g_cpucache_up = EARLY;
}
/*
* Initialisation. Called after the page allocator have been initialised and
* before smp_init().
*/
void __init kmem_cache_init(void)
{
size_t left_over;
struct cache_sizes *sizes;
struct cache_names *names;
int i;
int order;
int node;
/* 在slab初始化好之前,無法通過kmalloc分配初始化過程中必要的一些對象
,只能使用靜態的全局變數
,待slab初始化後期,再使用kmalloc動態分配的對象替換全局變數 */
/* 如前所述,先借用全局變數initkmem_list3表示的slab三鏈
,每個內存節點對應一組slab三鏈。initkmem_list3是個slab三鏈數組,對於每個內存節點,包含三組
:struct kmem_cache的slab三鏈、struct arraycache_init的slab 三鏈、struct kmem_list3的slab三鏈
。這裡循環初始化所有內存節點的所有slab三鏈 */
if (num_possible_nodes() == 1)
use_alien_caches = 0;
/*初始化所有node的所有slab中的三個鏈表*/
for (i = 0; i < NUM_INIT_LISTS; i++) {
kmem_list3_init(&initkmem_list3);
/* 全局變數cache_cache指向的slab cache包含所有struct kmem_cache對象,不包含cache_cache本身
。這裡初始化所有內存節點的struct kmem_cache的slab三鏈為空。*/
if (i < MAX_NUMNODES)
cache_cache.nodelists = NULL;
}
/* 設置struct kmem_cache的slab三鏈指向initkmem_list3中的一組slab三鏈,
CACHE_CACHE為cache在內核cache鏈表中的索引,
struct kmem_cache對應的cache是內核中創建的第一個cache
,故CACHE_CACHE為0 */
set_up_list3s(&cache_cache, CACHE_CACHE);
/*
* Fragmentation resistance on low memory - only use bigger
* page orders on machines with more than 32MB of memory.
*/
/* 全局變數slab_break_gfp_order為每個slab最多佔用幾個頁面
,用來抑制碎片,比如大小為3360的對象
,如果其slab只佔一個頁面,碎片為736
,slab佔用兩個頁面,則碎片大小也翻倍
。只有當對象很大
,以至於slab中連一個對象都放不下時
,才可以超過這個值
。有兩個可能的取值
:當可用內存大於32MB時
,BREAK_GFP_ORDER_HI為1
,即每個slab最多佔用2個頁面
,只有當對象大小大於8192時
,才可以突破slab_break_gfp_order的限制
。小於等於32MB時BREAK_GFP_ORDER_LO為0。*/
if (totalram_pages > (32 << 20) >> PAGE_SHIFT)
slab_break_gfp_order = BREAK_GFP_ORDER_HI;
/* 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();
/* 1) create the cache_cache */
/* 第一步,創建struct kmem_cache所在的cache,由全局變數cache_cache指向
,這裡只是初始化數據結構
,並未真正創建這些對象,要待分配時才創建。*/
/* 全局變數cache_chain是內核slab cache鏈表的表頭 */
INIT_LIST_HEAD(&cache_chain);
/* 將cache_cache加入到slab cache鏈表 */
list_add(&cache_cache.next, &cache_chain);
/* 設置cache著色基本單位為cache line的大小:32位元組 */
cache_cache.colour_off = cache_line_size();
/* 初始化cache_cache的local cache,同樣這裡也不能使用kmalloc
,需要使用靜態分配的全局變數initarray_cache */
cache_cache.array = &initarray_cache.cache;
/* 初始化slab鏈表 ,用全局變數*/
cache_cache.nodelists = &initkmem_list3;
/*
* struct kmem_cache size depends on nr_node_ids, which
* can be less than MAX_NUMNODES.
*/
/* buffer_size保存slab中對象的大小,這裡是計算struct kmem_cache的大小
, nodelists是最後一個成員
,nr_node_ids保存內存節點個數,UMA為1
,所以nodelists偏移加上1個struct kmem_list3 的大小即為struct kmem_cache的大小 */
cache_cache.buffer_size = offsetof(struct kmem_cache, nodelists) +
nr_node_ids * sizeof(struct kmem_list3 *);
#if DEBUG
cache_cache.obj_size = cache_cache.buffer_size;
#endif
/* 將對象大小與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);
for (order = 0; order < MAX_ORDER; order++) {
/* 計算cache_cache中的對象數目 */
cache_estimate(order, cache_cache.buffer_size,
cache_line_size(), 0, &left_over, &cache_cache.num);
/* num不為0意味著創建struct kmem_cache對象成功,退出 */
if (cache_cache.num)
break;
}
BUG_ON(!cache_cache.num);
/* gfporder表示本slab包含2^gfporder個頁面 */
cache_cache.gfporder = order;
/* 著色區的大小,以colour_off為單位 */
cache_cache.colour = left_over / cache_cache.colour_off;
/* slab管理對象的大小 */
cache_cache.slab_size = ALIGN(cache_cache.num * sizeof(kmem_bufctl_t) +
sizeof(struct slab), cache_line_size());
/* 2+3) create the kmalloc caches */
/* 第二步,創建kmalloc所用的general cache
,kmalloc所用的對象按大小分級
,malloc_sizes保存大小,cache_names保存cache名 */
sizes = malloc_sizes;
names = cache_names;
/*
* 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.cs_cachep = kmem_cache_create(names.name,
sizes.cs_size,
ARCH_KMALLOC_MINALIGN,
ARCH_KMALLOC_FLAGS|SLAB_PANIC,
NULL);
/* 如果struct kmem_list3和struct arraycache_init對應的kmalloc size索引不同
,即大小屬於不同的級別
,則創建struct kmem_list3所用的cache,否則共用一個cache */
if (INDEX_AC != INDEX_L3) {
sizes.cs_cachep =
kmem_cache_create(names.name,
sizes.cs_size,
ARCH_KMALLOC_MINALIGN,
ARCH_KMALLOC_FLAGS|SLAB_PANIC,
NULL);
}
/* 創建完上述兩個general cache后,slab early init階段結束,在此之前
,不允許創建外置式slab */
slab_early_init = 0;
/* 循環創建kmalloc各級別的general cache */
while (sizes->cs_size != 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.
*/
/* 某級別的kmalloc cache還未創建,創建之,struct kmem_list3和
struct arraycache_init對應的cache已經創建過了 */
if (!sizes->cs_cachep) {
sizes->cs_cachep = kmem_cache_create(names->name,
sizes->cs_size,
ARCH_KMALLOC_MINALIGN,
ARCH_KMALLOC_FLAGS|SLAB_PANIC,
NULL);
}
《解決方案》
謝謝分享