linux 源代码分析11:Slab初始化

  1. Slab初始化
    1. kmem_cache_init函数
    2. setup_cpu_cache函数

Slab初始化

Slab的初始化由kmem_cache_init和kmem_cache_init_late两个函数完成kmem_cache_init_late在init/main.c:start_kernel中调用。kmem_cache_init的调用路径是:start_kernel->mm_init_owner->mm_init->kmem_cache_init。

kmem_cache_init函数

1497 void __init kmem_cache_init(void)
1498 {
1499        size_t left_over;
1500        struct cache_sizes *sizes;
1501        struct cache_names *names;
1502        int i;
1503        int order;
1504        int node;
1505
1506        if (num_possible_nodes() == 1)
1507                 use_alien_caches = 0;
1508
1509        for (i = 0; i < NUM_INIT_LISTS; i++) {
1510                kmem_list3_init(&initkmem_list3[i]);
1511                 if (i < MAX_NUMNODES)
1512                        cache_cache.nodelists[i] = NULL;
1513        }
1514        set_up_list3s(&cache_cache, CACHE_CACHE);
1515
1516        /*
1517         * Fragmentation resistance onlow memory - only use bigger
1518          * page orders on machines with morethan 32MB of memory if
1519          * not overridden on the command line.
1520          */
1521        if (!slab_max_order_set && totalram_pages > (32 << 20)>> PAGE_SHIFT)
1522                 slab_max_order =SLAB_MAX_ORDER_HI;
1523
1524        /* Bootstrap is tricky, because several objects are allocated
1525          * from caches that do not exist yet:
1526          * 1) initialize the cache_cachecache: it contains the struct
1527          *   kmem_cache structures of all caches, except cache_cache itself:
1528          *   cache_cache is statically allocated.
1529          *   Initially an __init data area is used for the head array and the
1530          *   kmem_list3 structures, it's replaced with a kmalloc allocated
1531          *   array at the end of the bootstrap.
1532          * 2) Create the first kmalloc cache.
1533          *   The struct kmem_cache for the new cache is allocated normally.
1534          *   An __init data area is used for the head array.
1535          * 3) Create the remaining kmalloccaches, with minimally sized
1536          *   head arrays.
1537          * 4) Replace the __init data headarrays for cache_cache and the first
1538          *   kmalloc cache with kmalloc allocated arrays.
1539          * 5) Replace the __init data forkmem_list3 for cache_cache and
1540          *   the other cache's with kmalloc allocated memory.
1541          * 6) Resize the head arrays of thekmalloc caches to their final sizes.
1542          */
1543
1544        node = numa_mem_id();
1545
1546        /* 1) create the cache_cache */
1547        INIT_LIST_HEAD(&cache_chain);
1548        list_add(&cache_cache.next, &cache_chain);
1549        cache_cache.colour_off = cache_line_size();
1550        cache_cache.array[smp_processor_id()] = &initarray_cache.cache;
1551        cache_cache.nodelists[node] = &initkmem_list3[CACHE_CACHE + node];
1552
1553        /*
1554          * struct kmem_cache size depends onnr_node_ids & nr_cpu_ids
1555          */
1556        cache_cache.buffer_size = offsetof(struct kmem_cache, array[nr_cpu_ids])+
1557                                   nr_node_ids* sizeof(struct kmem_list3 *);
1558 #if DEBUG
1559        cache_cache.obj_size = cache_cache.buffer_size;
1560 #endif
1561        cache_cache.buffer_size = ALIGN(cache_cache.buffer_size,
1562                                        cache_line_size());
1563        cache_cache.reciprocal_buffer_size =
1564                reciprocal_value(cache_cache.buffer_size);
1565
1566        for (order = 0; order < MAX_ORDER; order++) {
1567                 cache_estimate(order,cache_cache.buffer_size,
1568                         cache_line_size(), 0,&left_over, &cache_cache.num);
1569                 if (cache_cache.num)
1570                         break;
1571        }
1572        BUG_ON(!cache_cache.num);
1573        cache_cache.gfporder = order;
1574        cache_cache.colour = left_over / cache_cache.colour_off;
1575        cache_cache.slab_size = ALIGN(cache_cache.num * sizeof(kmem_bufctl_t) +
1576                                      sizeof(struct slab), cache_line_size());
1577
1578        /* 2+3) create the kmalloc caches */
1579        sizes = malloc_sizes;
1580        names = cache_names;
1581
1582        /*
1583          * Initialize the caches that providememory for the array cache and the
1584          * kmem_list3 structures first.  Without this, further allocations will
1585          * bug.
1586          */
1587
1588        sizes[INDEX_AC].cs_cachep = kmem_cache_create(names[INDEX_AC].name,
1589                                        sizes[INDEX_AC].cs_size,
1590                                        ARCH_KMALLOC_MINALIGN,
1591                                        ARCH_KMALLOC_FLAGS|SLAB_PANIC,
1592                                         NULL);
1593
1594        if (INDEX_AC != INDEX_L3) {
1595                 sizes[INDEX_L3].cs_cachep =
1596                         kmem_cache_create(names[INDEX_L3].name,
1597                                sizes[INDEX_L3].cs_size,
1598                                ARCH_KMALLOC_MINALIGN,
1599                                ARCH_KMALLOC_FLAGS|SLAB_PANIC,
1600                                 NULL);
1601        }
1602
1603        slab_early_init = 0;
1604
1605        while (sizes->cs_size != ULONG_MAX) {
1606                 /*
1607                  * For performance, all thegeneral caches are L1 aligned.
1608                 * This should beparticularly beneficial on SMP boxes, as it
1609                  * eliminates "falsesharing".
1610                  * Note for systems short onmemory removing the alignment will
1611                  * allow tighter packing ofthe smaller caches.
1612                  */
1613                 if (!sizes->cs_cachep) {
1614                         sizes->cs_cachep =kmem_cache_create(names->name,
1615                                        sizes->cs_size,
1616                                         ARCH_KMALLOC_MINALIGN,
1617                                        ARCH_KMALLOC_FLAGS|SLAB_PANIC,
1618                                         NULL);
1619                 }
1620 #ifdef CONFIG_ZONE_DMA
1621                 sizes->cs_dmacachep =kmem_cache_create(
1622                                        names->name_dma,
1623                                        sizes->cs_size,
1624                                        ARCH_KMALLOC_MINALIGN,
1625                                         ARCH_KMALLOC_FLAGS|SLAB_CACHE_DMA|
1626                                                SLAB_PANIC,
1627                                         NULL);
1628 #endif
1629                 sizes++;
1630                 names++;
1631        }
1632        /* 4) Replace the bootstraphead arrays */
1633        {
1634                 struct array_cache *ptr;
1635
1636                 ptr = kmalloc(sizeof(structarraycache_init), GFP_NOWAIT);
1637
1638                BUG_ON(cpu_cache_get(&cache_cache) != &initarray_cache.cache);
1639                 memcpy(ptr,cpu_cache_get(&cache_cache),
1640                        sizeof(structarraycache_init));
1641                 /*
1642                  * Do not assume thatspinlocks can be initialized via memcpy:
1643                  */
1644                spin_lock_init(&ptr->lock);
1645
1646                cache_cache.array[smp_processor_id()] = ptr;
1647
1648                 ptr = kmalloc(sizeof(structarraycache_init), GFP_NOWAIT);
1649
1650                 BUG_ON(cpu_cache_get(malloc_sizes[INDEX_AC].cs_cachep)
1651                        !=&initarray_generic.cache);
1652                 memcpy(ptr,cpu_cache_get(malloc_sizes[INDEX_AC].cs_cachep),
1653                        sizeof(structarraycache_init));
1654                 /*
1655                  * Do not assume thatspinlocks can be initialized via memcpy:
1656                  */
1657                spin_lock_init(&ptr->lock);
1658
1659                malloc_sizes[INDEX_AC].cs_cachep->array[smp_processor_id()] =
1660                     ptr;
1661        }
1662        /* 5) Replace the bootstrap kmem_list3's */
1663        {
1664                 int nid;
1665
1666                 for_each_online_node(nid) {
1667                         init_list(&cache_cache,&initkmem_list3[CACHE_CACHE + nid], nid);
1668
1669                        init_list(malloc_sizes[INDEX_AC].cs_cachep,
1670                                  &initkmem_list3[SIZE_AC + nid], nid);
1671
1672                         if (INDEX_AC !=INDEX_L3) {
1673                                init_list(malloc_sizes[INDEX_L3].cs_cachep,
1674                                          &initkmem_list3[SIZE_L3 + nid], nid);
1675                         }
1676                 }
1677        }
1678
1679        g_cpucache_up = EARLY;
1680 }

kmem_cache_init函数的实质工作是创建一系列的slab缓存,这里包含用来分配structkmem_cache结构的缓存和通用长度缓存。关键问题是创建缓存时需要分配内存,这些内存从哪里分配呢?如果直接从伙伴系统分配,因为伙伴系统只能分配若干页,这样会造成浪费,还有一个方法就是静态分配,在系统中定义了四个有个slab的静态变量cache_cache,initkmem_list3,initarray_generic和initarray_cache。initkmem_list3定义了足以包含3个三链表数组的空间。
创建slab缓存要为struct kmem_cache结构,三链表数组和对象缓存数组分配空间。struct kmem_cache,三链表数组和对象缓存数组都是从一个slab缓存中分配空间。问题是刚开始的时候没有任何slab缓存,那这些结构的空间是从哪里来的?实际上创建第一个slab缓存的时候所有这些结构都是静态分配空间的,实际上第一个创建的是分配struct kmem_cache的slab缓存,这个slab缓存的struct kmem_cache结构用的是全局结构cache_cache,三链表使用的是initkmem_list3中的空间,对象缓存数组使用的是initarray_cache的空间。这个时候g_cpucache_up变量的值是NONE,第二个创建的是为对象缓存分配空间的slab缓存,这个时候为struct kmem_cache结构分配空间的slab缓存以及创建好,对象缓存数组使用的是initarray_generic,三链表使用的是initkmem_list3中从索引SIZE_AC开始的一段,创建第二个slab缓存的时候g_cpucache_up == NONE成立。第三个创建的是用来分配三链表数组分配空间的slab缓存,这个时候用来分配对象缓存的slab已经创建好了,但这个时候要考虑一直情况,就是如果分配三链表数组和对象缓存数组的是在同一个slab缓存,这时候三链表也可以直接从slab中分配了,如果分配三链表数组和对象缓存数组的不是在同一个slab缓存中,则第三个创建的slab缓存的三链表是以SIZE_L3为索引使用initkmem_list3的空间。
kmem_cache_init函数的代码分成5段来读
第一段:1499-1577。1514行可以看出对第零个创建的缓存的三链表初始化是以CACHE_CACHE为下标占用initkmem_list3的一段,1550行表明缓存堆栈数组是指向全局变量initarray_cache,其他对全局量cache_cache的初始化和kmem_cache_create函数大同小异。
第二段:1579-1604。第二段的任务是创建为缓存堆栈分配空间的缓存和为三链表分配内存的缓存,1594行的判断条件就是分配缓存堆栈空间和分配三链表空间的缓存是同一个的情况。
第三段:1605-1631。这一段是创建通用长度缓存。
第四段:1633-1661。这一段的工作从slab系统中分配内存替换掉刚才临时使用的全局对象缓存数组。
第五段:1663-1677。这一段的工作从slab系统中分配内存替换掉刚才临时使用的全局三链表数组。

setup_cpu_cache函数

setup_cpu_cache函数是在mm/slab.c中实现代码如下:
2195 static int __init_refoksetup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp)
2196 {
2197        if (g_cpucache_up == FULL)
2198                 return enable_cpucache(cachep,gfp);
2199
2200        if (g_cpucache_up == NONE) {
2201                 /*
2202                  * Note: the first kmem_cache_createmust create the cache
2203                  * that's used by kmalloc(24),otherwise the creation of
2204                  * further caches will BUG().
2205                  */
2206                cachep->array[smp_processor_id()] = &initarray_generic.cache;
2207
2208                 /*
2209                  * If the cache that's used bykmalloc(sizeof(kmem_list3)) is
2210                  * the first cache, then weneed to set up all its list3s,
2211                  * otherwise the creation offurther caches will BUG().
2212                  */
2213                 set_up_list3s(cachep,SIZE_AC);
2214                 if (INDEX_AC == INDEX_L3)
2215                         g_cpucache_up =PARTIAL_L3;
2216                 else
2217                         g_cpucache_up = PARTIAL_AC;
2218        } else {
2219                cachep->array[smp_processor_id()] =
2220                         kmalloc(sizeof(structarraycache_init), gfp);
2221
2222                 if (g_cpucache_up ==PARTIAL_AC) {
2223                         set_up_list3s(cachep,SIZE_L3);
2224                         g_cpucache_up =PARTIAL_L3;
2225                 } else {
2226                         int node;
2227                        for_each_online_node(node) {
2228                                cachep->nodelists[node]=
2229                                    kmalloc_node(sizeof(struct kmem_list3),
2230                                                gfp, node);
2231                                BUG_ON(!cachep->nodelists[node]);
2232                                kmem_list3_init(cachep->nodelists[node]);
2233                         }
2234                 }
2235        }
2236        cachep->nodelists[numa_mem_id()]->next_reap =
2237                         jiffies +REAPTIMEOUT_LIST3 +
2238                         ((unsignedlong)cachep) % REAPTIMEOUT_LIST3;
2239
2240        cpu_cache_get(cachep)->avail = 0;
2241        cpu_cache_get(cachep)->limit = BOOT_CPUCACHE_ENTRIES;
2242        cpu_cache_get(cachep)->batchcount = 1;
2243        cpu_cache_get(cachep)->touched = 0;
2244        cachep->batchcount = 1;
2245        cachep->limit = BOOT_CPUCACHE_ENTRIES;
2246        return 0;
2247 }

setup_cpu_cache函数是在kmem_cache_create函数中调用的,函数的功能是设置缓存的缓存堆栈和三链表结构。setup_cpu_cache函数根据变量g_cpucache_up不同的值有不同的设置方法。g_cpucache_up变量的取值是g_cpucache_up变量的值之一,在创建kmem_cache缓存的时候g_cpucache_up的值为NONE,在创建堆栈缓存的时候g_cpucache_up的值为SIZE_AC,在创建三链表缓存的时候g_cpucache_up的值为PARTIAL_L3。
2200-2218行是创建堆栈缓存时的处理代码,从这段代码可以看出,堆栈缓存的对象缓存初始化时使用的是initarray_generic的空间,三链表是以SIZE_AC为下标initkmem_list3中的空间,这从2213行代码可以看出来。2214-2217行知道如果堆栈缓存和三链表缓存是同一个缓存,这时候直接把g_cpucache_up置为PARTIAL_L3,因为这时候三链表也可以从slab中分配了。
2218-2235行,这是创建三链表堆栈的处理代码。2219-2220行,因为堆栈缓存以及创建好,这时候缓存堆栈的空间可以从slab中分配了。三链表的分配要看三链表和堆栈缓存是不是同一个,不是还有借用静态变量initkmem_list3的空间,是则可以直接在slab中分配空间了。
2236-2246是对一些变量的初始化。

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 ancjf@163.com

文章标题:linux 源代码分析11:Slab初始化

本文作者:ancjf

发布时间:2013-06-12, 19:43:06

最后更新:2020-04-18, 15:58:04

原始链接:http://ancjf.com/2013/06/12/linux-3-4-10-%E5%86%85%E6%A0%B8%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E6%BA%90%E4%BB%A3%E7%A0%81%E5%88%86%E6%9E%9011-Slab%E5%88%9D%E5%A7%8B%E5%8C%96/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏