Linux 内存管理之 SLUB分配器(2) :kmalloc_cache 结构
本部分主要整理:SLUB 数据结构
首先一张图show出本小节主要内容
图示即SLUB分配器中的主要数据结构:
- kmalloc_cache数组,按照size大小构建此数组,每个元素指向kmem_cache;
- kmem_cache结构体,用于分配的核心结构体;
- kmem_cache_cpu结构体,关联到每个CPU上,不需要考虑竞争问题,partial中使用时需要加锁;
- kmem_cache_node结构体,对应的kmem_cache的缓存池子,访问需要考虑竞争;
1 slab_cache的双向链表
与SLOB一样,对于SLUB中的核心结构采用双向链表的形式链接:
- 头为slab_caches
- 节点为kmem_cache 中 list
- 新元素插入到slab_caches和其下一个节点之间
2. kmalloc_cache数组结构
在初始化时,创建kmalloc_caches[12] 数组,其中每个元素均为指向kmem_cache的指针
如下为其核心构造逻辑:
setup_kmalloc_cache_index_table();//填充size_index create_kmalloc_caches(0); new_kmalloc_cache(i, flags)//添加数组中3~12元素 kmalloc_dma_caches[i] = create_kmalloc_cache(kmalloc_info[idx].name, kmalloc_info[idx].size, flags) kmem_cache *s = kmem_cache_zalloc(kmem_cache, GFP_NOWAIT)//申请slub object用于kmem_cache使用 create_boot_cache(s, name, size, flags)//填充kmem_cache结构数据 list_add(&s->list, &slab_caches)//往slab_caches这个链表上加kmem_caches kmalloc_dma_caches[i] = create_kmalloc_cache(n, size, flga|SLAB_CACHE_DMA)//对应添加dma_caches kmem_cache *s = kmem_cache_zalloc(kmem_cache, GFP_NOWAIT) create_boot_cache(s, name, size, flags) list_add(&s->list, &slab_caches)
3. kmem_cache_cpu 结构
kmem_cache_cpu 结构体关键部分如下:
-
freelist 指向 page 中空闲的 object,访问非常快,不需要竞争所以没有加锁
page 中的 freelist 在分配给到 CPU 后要设置为NULL,保证只有关联到的 CPU 可以从此 page 上分配 object,其他 CPU 只能释放
-
page 即当前正在使用的页
-
partial 即page的next结构,其中均为部分分配的page
struct kmem_cache {
struct kmem_cache_cpu __percpu *cpu_slab;//每CPU,即每个CPU对应一个此结构体
unsigned long flags;//填充过
unsigned long min_partial;
int size; //一个obj的完整大小,通过calculate_size计算,根据debug情况而添加layout内容
int object_size; //纯object大小,8~8k Bytes
int offset; //找到下个object的偏移,即object + offset得到下个object地址
int cpu_partial;
struct kmem_cache_order_objects oo;//高16bit存储order,低16bit存储count
struct kmem_cache_order_objects max;
struct kmem_cache_order_objects min;
gfp_t allocflags; /* gfp flags to use on each alloc */
int refcount; /* Refcount for slab cache destroy */
void (*ctor)(void *);
int inuse; //注意这里的inuse与page->inuse不同,这里是指object 之后的内容在debug情况下是fp之前的大小
int align; //对齐操作
int reserved; //这个是为了计算page中包含多少个object,可能存在reserved大小;
const char *name; /* Name (only for display!) */
struct list_head list; //kmem_cache链表
int red_left_pad; //左侧的red区域大小,用于判断左侧是否越界的;
struct kobject kobj; /* For sysfs */
struct memcg_cache_params memcg_params;
int max_attr_size; /* for propagation, maximum size of a stored attr */
struct kset *memcg_kset;
struct kmem_cache_node *node[MAX_NUMNODES];
};
另外,第一小节已经介绍过object layout的内容,这里每个object均包含layout那么大的size,在page内部计算偏移即可
4. page结构
由于SLUB中没有结构体用于管理其机构,实际为复用的page中的几个元素:
struct page {
/* First double word block */
unsigned long flags;//标记为page被什么使用
...
void *freelist; //page指向第一个空闲的object,如果关联到cpu上则此处指向NULL
unsigned counters;//与如下三个共用空间,用于数据获取
struct { /* SLUB */
unsigned inuse:16;//当前page上已经使用的object个数
unsigned objects:15;//当前page上可以分配的object总数
unsigned frozen:1;//此page是否被cpu关联上,即挂在了kmem_cache_cpu上
};
struct list_head lru;//这个地方用于管理node中的partial和full链表
struct page *next; //kmem_cache_cpu的partial链表用next连接
struct kmem_cache *slab_cache; /* SL[AU]B: Pointer to slab */
5. kmem_cache_node 结构
其中关键结构即partial和full两个链表,分别存储部分分配和全部分配的page;
struct kmem_cache_node {
spinlock_t list_lock;
#ifdef CONFIG_SLUB
unsigned long nr_partial;//node上的partial数量
struct list_head partial;//node上的partial链表
#ifdef CONFIG_SLUB_DEBUG
atomic_long_t nr_slabs;//slab数量
atomic_long_t total_objects;//总共所有的object数量
struct list_head full;//node上的full链表
#endif
#endif
};