上次说了 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的大小对齐后的大小
c