内存管理 初始化(七)kmem_cache_init_late 初始化slab分配器(下)

我们知道kmem_cache中对于每CPU都有一个array_cache,已作为每CPU申请内存的缓存.  此函数的目的在于:每个kmem_cache都有一个kmem_list3实例,该实例的shared作为一个kmem_cache上所有CPU的内存申请缓存.  但是在此之前,seup_cpu_cache中对于kmem_cache中array_cache的值初始化体现不出缓存思想,而且对于kmem_cache中的kmem_list3.shared也没有利用.

kmem_cache_init_late的目的就在于完善slab分配器的缓存机制.                                                                              
 

start_kernel()
  |-->page_address_init()
  |
  |-->setup_arch(&command_line);
  |
  |-->setup_per_cpu_areas();
  |
  |-->build_all_zonelist()
  |
  |-->page_alloc_init()
  |
  |-->pidhash_init()
  |
  |-->vfs_caches_init_early()
  |
  |-->mm_init()
  |
  |-->.......
  |
  |-->gfp_allowed_mask = __GFP_BITS_MASK;
  |   在此之前,gfp_allowed_mask = GFP_BOOT_MASK;
  |
  |-->kmem_cache_init_late();
  |

void __init kmem_cache_init_late(void)
  |-->struct kmem_cache *cachep;
  |
  |-->list_for_each_entry(cachep, &cache_chain, next)
  |-->if (enable_cpucache(cachep, GFP_NOWAIT))  BUG();
  |
  |--g_cpucache_up = FULL;
  |
  |-->init_lock_keys();
  |
  |-->register_cpu_notifiler(&cpu_notifier);

int enabel_cpucache(struct kmem_cache *cachep, gfp_t gfp)
  |-->int limit;
  |
  |-->if (cachep->buffer_size > 131072) limit = 1;
  |   else if (cachep->buffer_size > PAGE_SIZE) limit = 8;
  |   else if (cachep->buffer_size > 1024) limit = 24;
  |   else if (cachep->buffer_size > 256) limit = 54;
  |   else limit = 120;
  |   为什么选择这些数值啊,不明白???
  |
  |-->int shared = 0;
  |   if(cachep->buffer_size <= PAGE_SIZE && num_possible_cpus() > 1)
  |      shared = 8;
  |
  |--int err = 0;
  |  err = do_tune_cpucache(cachep, limit, (limit + 1) / 2, shared, gfp);
  |
  |--return err;

int do_tune_cpucache(struct kmem_cache *cachep, int limit,
        int batchcount, int shared, gfp_t gfp)
  |-->struct ccupdate_struct *new = NULL;
  |   new = kazlloc(sizeof(*new), gfp);
  |
  |--int i;
  |--for_each_online_cpu(i)
  |--{
  |    new->new[i] = alloc_arraycache(cpu_to_node(i), limit,
  |                                    batchcount, gfp);
  |    根据limit, batchcount数值,构建新的array_cache实例.
  |
  |    因为kmem_cache中的array_cache是每个CPU的,所以此处是循环,为每个CPU都
  |    都构建一个array_cache实例.
  |--}
  |
  |-->new->cachep = cachep;
  |
  |-->on_each_cpu(do_ccupdate_local, (void*)new, 1);
  |   将kmem_cache下的每个CPU的array_cache[i]更换成new->new[i];
  |
  |-->cachep->batchcount = batchcount;
  |   cachep->limit = limit;
  |   cachep->shared = shared;
  |
  |
  |  上面以替换了kmem_cache下的每个CPU的array_cache[i],
  |  因此需要把原来的array_cache释放掉.
  |--for_each_online_cpu(i)
  |--{
  |    struct array_cache *ccolde = new->new[i];
  |    if(!ccold) continue;
  |
  |    free_block(cachep, ccold->entry, ccold->avail, cpu_to_node(i));
  |    我们知道在此之前,ccold->avail一直为0,所以该函数暂时可以不看.
  |    此函数,就是把ccold->avail个ccole->entry中的数组元素指向的内存空间
  |    释放给slab管理器.
  |
  |
  |    kfree(ccold);
  |    基本同于free_block,我们知道slab所管理的内存都是位于低端内存,低端内存的物
  |    理地址及其对应的虚拟地址存在固定偏移,因此根据该部分的虚拟地址可以很容易的找到
  |    struct page实例,而struct page中的lru链表,在slab中被复用了,根据链表
  |    指针可以找到kmem_cache实例,所以kfree基本等同于free_block;
  |    但是kfree与free_block的重要的不同点在于,free_block直接将内存释放给了
  |    slab管理器,而kfree首选将内存释放给每CPU的array_cache数组.
  |
  |--}
  |-->kfree(new);
  |
  |--return alloc_kmemlist(cachep, gfp);
  |  每个kmem_cache中的kmem_list3.shared上array_cache可以被所有CPU共享.

我们知道kmem_cache中对于每CPU都有一个array_cache,已作为每CPU申请内存的缓存. 
此函数的目的在于:每个kmem_cache都有一个kmem_list3实例,该实例的shared作为
一个kmem_cache上所有CPU的内存申请缓存(对于UMA,kmem_cache.alien没有用处).
此时,我们不妨猜测,当一个CPU通过kmalloc申请内内存时,将从kmem_cache实例上
自己的array_cache进行申请,如果没有则从kmem_list3->shared上补充到array_cache上,
如果kmem_list3上也每有,将从slab管理器上获取,充分体现了缓存的利用.
int alloc_kmemlist(struct kmem_cache *cachep, gfp_t gfp)
  |-->int node = 0;
  |   struct kmem_list3 *l3 = NULL;
  |   struct array_cache *new_shared = NULL;
  |   struct array_cache **new_alien = NULL;
  |
  |-->for_each_online_node(node)
  |--{
  |    new_shared = NULL;
  |    if(cachep->shared)
  |        new_shared = alloc_arraycache(node, 
  |                     cachep->shared * cachep->batchcount,
  |                     0xbaadf00d, gfp);
  |
  |
  |    l3 = cachep->nodelists[node];
  |    if(l3)
  |    |-{
  |    |    struct array_cache *shared = l3->shared;
  |    |    if(shared) 
  |    |         free_block(cachep, shared->entry, shared->avail, node);
  |    |    l3->shared = new_shared;
  |    |    if(!l3->alien) l3->alien = new_alien, new_alien = NULL;
  |    |    l3->free_limit = (1 + NR_CPUS) * cachep->batchcount
  |    |                     + cachep->num;|    |    kfree(shared);
  |    |    free_alien_cache(new_alien);
  |    |    continue; //对于单节点,再次continue时,将退出循环
  |    |-}
  |    |
  |    |
  |    ...... 对于UMA体系 nothing
  |    |
  |--}
  |
  |--return 0;

void do_ccupdate_local(void *info)
  |-->struct ccupdate_struct *new = info;
  |   struct array_cache *old = cpu_cache_get(new->cachep);
  |
  |-->new->cachep->array[smp_processor_id()] = 
  |                new->new[smp_processor_id];
  |   new->new[smp_processor_id()] = old;

struct array_cache *alloc_arraycache(int node, int entries,
        int batchcount, gfp_t gfp)
  |-->int memsize = sizeof(void *) * entries
  |                 + sizeof(struct array_cache);
  |   根据entries的数值,计算该分配的array_cache空间大小.
  |
  |-->struct array_cache *nc = NULL;
  |   nc = kmalloc_node(memsize, gfp, node);
  |   nc->avail = 0;
  |   nc->limit = entries;
  |   nc->batchcount = batchcount;
  |   nc->touched = 0;
  |   spin_lock_init(&nc->lock);
  |
  |-->return nc;

你可能感兴趣的:(Linux内核-内存管理,linux内核,内存)