layout | title | category | description | tags |
---|---|---|---|---|
post |
slab初始化 |
内存管理 |
slab初始化... |
slab |
slab初始化并不是特别麻烦,因为伙伴系统已经完全启用,内核没有收到其他的特别限制。尽管如此,由于slab分配器的结构所示,这就产生了一个鸡与蛋的问题。
为初始化slab数据结构,内核需要若干远小于一整页的内存块,这些最适合由kmalloc分配,这里的关键所在是,只有slab系统启用之后才能使用kmalloc。
更确切的说,该问题涉及kmalloc的per-CPU缓存的初始化,这些缓存能够初始化之前,kmalloc必须可以用来分配所需的内存空间,而kmalloc自身也处于初始化的过程中,也就是说,kmalloc只能在kmalloc已经初始化之后初始化,这是个不可能的场景,所以内核需要使用一些技巧。
kmalloc_cache_init函数用于初始化slab分配器,它在内核初始化阶段、伙伴系统启用之后调用。但在多处理器系统上,启动CPU此时正在运行,而其他的CPU尚未初始化,所以kmalloc_cache_init采用了一个多步骤过程,逐步激活slab分配器。
{% highlight c++%} void __init kmem_cache_init(void) { size_t left_over; struct cache_sizes *sizes; struct cache_names *names; int i; int order; int node;
if (num_possible_nodes() == 1)
use_alien_caches = 0;
for (i = 0; i < NUM_INIT_LISTS; i++) {
kmem_list3_init(&initkmem_list3[i]);
if (i < MAX_NUMNODES)
cache_cache.nodelists[i] = NULL;
}
set_up_list3s(&cache_cache, CACHE_CACHE);
if (totalram_pages > (32 << 20) >> PAGE_SHIFT)
slab_break_gfp_order = BREAK_GFP_ORDER_HI;
node = numa_node_id();
/* 1) 创建 cache_cache 对象 */
INIT_LIST_HEAD(&cache_chain);
list_add(&cache_cache.next, &cache_chain);
cache_cache.colour_off = cache_line_size();
cache_cache.array[smp_processor_id()] = &initarray_cache.cache;
cache_cache.nodelists[node] =
&initkmem_list3[CACHE_CACHE + node];
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_cache.buffer_size = ALIGN(cache_cache.buffer_size, cache_line_size()); cache_cache.reciprocal_buffer_size = reciprocal_value(cache_cache.buffer_size);
for (order = 0; order < MAX_ORDER; order++) {
cache_estimate(order, cache_cache.buffer_size,
cache_line_size(), 0, &left_over, &cache_cache.num);
if (cache_cache.num)
break;
}
BUG_ON(!cache_cache.num);
cache_cache.gfporder = order;
cache_cache.colour = left_over / cache_cache.colour_off;
cache_cache.slab_size =
ALIGN(cache_cache.num * sizeof(kmem_bufctl_t) +
sizeof(struct slab), cache_line_size());
/* 2+3) 创建 kmalloc 缓存 */
sizes = malloc_sizes;
names = cache_names;
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,
NULL);
if (INDEX_AC != INDEX_L3) {
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);
}
slab_early_init = 0;
while (sizes->cs_size != ULONG_MAX) {
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++; } /* 4) 替换bootstrap head arrays */ { struct array_cache *ptr;
ptr = kmalloc(sizeof(struct arraycache_init), GFP_NOWAIT);
BUG_ON(
cpu_cache_get(&cache_cache) != &initarray_cache.cache
);
memcpy(ptr, cpu_cache_get(&cache_cache),
sizeof(struct arraycache_init));
spin_lock_init(&ptr->lock);
cache_cache.array[smp_processor_id()] = ptr;
ptr = kmalloc(sizeof(struct arraycache_init), GFP_NOWAIT);
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));
spin_lock_init(&ptr->lock);
malloc_sizes[INDEX_AC].cs_cachep->array[smp_processor_id()]
= ptr;
}
/* 5) 替换 bootstrap kmem_list3's */
{
int nid;
for_each_online_node(nid) {
init_list(&cache_cache,
&initkmem_list3[CACHE_CACHE + nid], 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);
}
}
}
g_cpucache_up = EARLY;
} {% endhighlight %}
首先,kmalloc_cache_init创建系统中的第一个slab缓存,以便为kmem_cache的实例提供内存,为此,内核使用的主要是在编译时创建的静态数据,实际上,一个静态数据结构用作per-CPU数组。创建的slab缓存名字为cache_cache。
然后,kmalloc_cache_init接下来初始化一般性的缓存,用作kmalloc内存的来源。为此,针对所需的各个缓存长度,分别调用kmem_cache_create函数。这个函数起初只需要cache_cache缓存已经建立即可,但在初始化per-CPU缓存时,该函数必须借助于kmalloc,在目前这个情况,依旧不可能。
所以,内核使用了g_cpucache_up变量,可以接受NONE、PARTIAL_AC、PARTIAL_L3以及FULL四个值,以反映kmalloc初始化的状态。最初内核的状态时NONE,在最小的kmalloc缓存初始化时,在其将一个静态的变量用于per-CPU的缓存数据。
g_cpucache_up接下来设置为PARTIAL_AC,表示array_cache的实例可以立即分配,如果初始化的长度还足够分配kmem_list3实例,则状态立即转换为PARTIAL_L3,否则只能等下一个更大的缓存初始化之后才能变更。
剩余的kmalloc缓存的per-CPU数据现在可以用kmalloc创建,这是一个arraycache_init实例,只需要最小的kmalloc内存区。
在kmalloc_cache_init的最后一步,把到现在为止一直使用的数据结构的所有静态实例化的成员,用kmalloc动态分配的版本替换。这个时候g_cpucache_up变量的状态时FULL,表示slab分配器已经就绪可以使用。
{% highlight c++%} void __init kmem_cache_init_late(void) { struct kmem_cache *cachep;
/* 重新改变head数组的长度到它们真实分配的长度 */
mutex_lock(&cache_chain_mutex);
list_for_each_entry(cachep, &cache_chain, next)
if (enable_cpucache(cachep, GFP_NOWAIT))
BUG();
mutex_unlock(&cache_chain_mutex);
/* 完成了! */
g_cpucache_up = FULL;
init_lock_keys();
register_cpu_notifier(&cpucache_notifier);
} {% endhighlight %}
处理完成代码如上。