全部博文(50)
分类: LINUX
2014-04-04 11:54:46
原文地址:Vi Linux内存 之 Slab分配器(八) 作者:palals
slab系统的初始化。主要是创建general cache。
void __init kmem_cache_init(void)
{
size_t left_over;
struct cache_sizes *sizes;
struct cache_names *names;
int i;
int order;
int node;
/* UMA内存节点数为0 */
if (num_possible_nodes() == 1)
use_alien_caches = 0;
/* 在slab初始化好之前,无法通过kmalloc分配初始化过程中必要的一些对象,只能使用静态的全局变量,待slab初始化后期,再使用kmalloc动态分配的对象替换全局变量 */
/* 如前所述,先借用全局变量initkmem_list3表示的slab三链,每个内存节点对应一组slab三链。initkmem_list3是个slab三链数组,对于每个内存节点,包含三组:struct kmem_cache的slab三链、struct arraycache_init的slab 三链、struct kmem_list3的slab三链。这里循环初始化所有内存节点的所有slab三链 */
for (i = 0; i < NUM_INIT_LISTS; i++) {
kmem_list3_init(&initkmem_list3[i]);
/* 全局变量cache_cache指向的slab cache包含所有struct kmem_cache对象,不包含cache_cache本身。这里初始化所有内存节点的struct kmem_cache的slab三链为空。*/
if (i < MAX_NUMNODES)
cache_cache.nodelists[i] = 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.
*/
/* 获得本内存节点的id,UMA为0 */
node = numa_mem_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[smp_processor_id()] = &initarray_cache.cache;
/* 初始化slab链表 */
cache_cache.nodelists[node] = &initkmem_list3[CACHE_CACHE + node];
/*
* 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
/* obj_size为对象大小,设置其等于buffer_size */
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包含2gfporder个页面 */
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[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);
/* 如果struct kmem_list3和struct arraycache_init对应的kmalloc size索引不同,即大小属于不同的级别,则创建struct kmem_list3所用的cache,否则共用一个cache */
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);
}
/* 创建完上述两个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[INDEX_AC].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[smp_processor_id()] = ptr;
/* 申请malloc_sizes[INDEX_AC].cs_cachep所用local cache的空间 */
ptr = kmalloc(sizeof(struct arraycache_init), GFP_NOWAIT);
BUG_ON(cpu_cache_get(malloc_sizes[INDEX_AC].cs_cachep)
!= &initarray_generic.cache);
/* 复制原local cache到新分配的位置,注意此时local 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_cache的local cache指向新的位置 */
malloc_sizes[INDEX_AC].cs_cachep->array[smp_processor_id()] =
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[CACHE_CACHE + nid], nid);
/* 复制struct arraycache_init的slab三链 */
init_list(malloc_sizes[INDEX_AC].cs_cachep,
&initkmem_list3[SIZE_AC + nid], nid);
/* 复制struct kmem_list3的slab三链 */
if (INDEX_AC != INDEX_L3) {
init_list(malloc_sizes[INDEX_L3].cs_cachep,
&initkmem_list3[SIZE_L3 + nid], nid);
}
}
}
/* 更新slab系统初始化进度 */
g_cpucache_up = EARLY;
}
kmem_cache_init_late
Slab系统初始化分两个部分,先初始化一些基本的,待系统初始化工作进行的差不多时,再配置一些特殊功能。
void __init kmem_cache_init_late(void)
{
struct kmem_cache *cachep;
/* 初始化阶段local cache的大小是固定的,要根据对象大小重新计算 */
/* 6) resize the head arrays to their final sizes */
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);
/* Done! */
/* 大功告成,general cache终于全部建立起来了 */
g_cpucache_up = FULL;
/* Annotate slab for lockdep -- annotate the malloc caches */
init_lock_keys();
/*
* Register a cpu startup notifier callback that initializes
* cpu_cache_get for all new cpus
*/
/* 注册cpu up回调函数,cpu up时配置local cache */
register_cpu_notifier(&cpucache_notifier);
#ifdef CONFIG_NUMA
/*
* Register a memory hotplug callback that initializes and frees
* nodelists.
*/
hotplug_memory_notifier(slab_memory_callback, SLAB_CALLBACK_PRI);
#endif
/*
* The reap timers are started later, with a module init call: That part
* of the kernel is not yet operational.
*/
}