slab allocator (3)—— array_cache
array_cache也就是Gorman书中的per-CPU cache。书中提到,为什么要有这个数据结构,有两点原因:
1)This way the hardware cache will be used for as long as possible on the same CPU.
2)The second major benefit of this method is that spinlocks do not have to be held
先看下kmem_cache数据结构:
struct kmem_cache {
/* 1) per-cpu data, touched during every alloc/free */
struct array_cache *array[NR_CPUS];
/* 2) Cache tunables. Protected by cache_chain_mutex */
unsigned int batchcount;
unsigned int limit;
unsigned int shared;
unsigned int buffer_size;
u32 reciprocal_buffer_size;
/* 1) per-cpu data, touched during every alloc/free */
struct array_cache *array[NR_CPUS];
/* 2) Cache tunables. Protected by cache_chain_mutex */
unsigned int batchcount;
unsigned int limit;
unsigned int shared;
unsigned int buffer_size;
u32 reciprocal_buffer_size;
……
}
array_cache是kmem_cache的第一个成员,而且是针对每个CPU都有一个这样的结构与之对应,这也印证了上面Gorman提到的两点原因。在每个kmem_cache被创建的时候,都需要去初始化这个结构,具体来说就是:
setup_cpu_cache -> enable_cpucache -> do_tune_cpucache
具体流程如下:
可见do_tune_cpucache的作用就是分配这个array_cache,并付给对应的CPU的变量。这里不清楚为什么要所有的online CPU都要分配一遍,然后只取用一个当前CPU的值。那么怎么使用这个array_cache,会在讲分配的笔记中再做详细介绍。不过这里有个free_block函数,阅读这个函数,可以对array_cache的工作原理也有一个大体的了解。
先看整个调用流程:
array_cache数据结构正如上图中的代码所显示,除了touched不知道有什么用之外,其他几个变量的作用还是很清晰的,从名字也可以看得出来:
- avail,当前还有几个空闲的位子可以分配
- limit,自然就是分配的上限,之前也说过,在enable_cpucache中也有定义
- batchcount,linux中一般,一次操作的数量都是用batch来表示,所以这里就是array_cache在增长或释放时,一次操作的object数
- entry,就是array_cache的核心成员了,也就是我们的object数组
经过阅读分配内存的代码,可以回答这个问题了,首先分配内存,都是从array_cache->entry数组中取object,返回给调用者,此时avail的object会被分配出去,avail变少。当avail为0时,系统调用某个函数,从kmem_cache中去取object,赋给array_cache,一次取batchcount个object,这时avail就会增加,而kmem_list3->free_objects就会减少相应的数目。而kmem_list3->free_objects会在cache_grow时递增cachep->num。