kernel hacker修炼之道之内存管理-SLAB(SLAB的基本数据结构)

本文深入探讨SLAB内存管理的实现原理,包括基本数据结构、高速缓存创建与销毁、块分配与回收等关键步骤,揭示了SLAB如何解决内碎片问题,实现对象的高效复用。

kernel hacker修炼之道之内存管理-SLAB

作者:李万鹏 于北京 borqs

2011.9.13 21:15

本SLAB系列文章从几个方面讨论:

1. SLAB的基本数据结构

2. 创建SLAB高速缓存kmem_cache_create()

3. 销毁SLAB高速缓存kmem_cache_destroy()

4. 创建SLAB块cache_grow()

5. 撤销SLAB块cache_destroy()

6. 分配SLAB对象kmem_cache_alloc()

7. 释放SLAB对象kmem_cache_free()

8. SLAB系统的初始化代码

slab分配器基本原理:


slab最初是在Solaris 2.4中引入linux操作系统的,用于解决内碎片问题。程序经常需要创建一些数据结构,比如进程描述符task_struct,内存描述符mm_struct等。slab分配器把这些需要分配的小块内存区作为对象,类似面向对象的思想。每一类对象分配一个cache,cache有一个或多个slab组成,slab由一个或多个物理页面组成。需要分配对象的时候从slab中空闲的对象取,用完了再放回slab中,而不是释放给物理页分配器,这样下次用的时候就不用重新初始化了,实现了对象的复用。

struct kmem_cache_s { /* 1) per-cpu data, touched during every alloc/free */ struct array_cache *array[NR_CPUS]; unsigned int batchcount; unsigned int limit; /* 2) touched by every alloc & free from the backend */ struct kmem_list3 lists; /* NUMA: kmem_3list_t *nodelists[MAX_NUMNODES] */ unsigned int objsize; unsigned int flags; /* constant flags */ unsigned int num; /* # of objs per slab */ unsigned int free_limit; /* upper limit of objects in the lists */ spinlock_t spinlock; /* 3) cache_grow/shrink */ /* order of pgs per slab (2^n) */ unsigned int gfporder; /* force GFP flags, e.g. GFP_DMA */ unsigned int gfpflags; size_t colour; /* cache colouring range */ unsigned int colour_off; /* colour offset */ unsigned int colour_next; /* cache colouring */ kmem_cache_t *slabp_cache; unsigned int slab_size; unsigned int dflags; /* dynamic flags */ /* constructor func */ void (*ctor)(void *, kmem_cache_t *, unsigned long); /* de-constructor func */ void (*dtor)(void *, kmem_cache_t *, unsigned long); /* 4) cache creation/removal */ const char *name; struct list_head next; /* 5) statistics */ #if STATS unsigned long num_active; unsigned long num_allocations; unsigned long high_mark; unsigned long grown; unsigned long reaped; unsigned long errors; unsigned long max_freeable; unsigned long node_allocs; atomic_t allochit; atomic_t allocmiss; atomic_t freehit; atomic_t freemiss; #endif #if DEBUG int dbghead; int reallen; #endif };

如图,每一个cache用高速缓存描述符struct kmem_cache_t来描述,所有的cache链接在一个以cache_chain为链表头的链表上。其中ctor,dtor 分别是构造和析构函数,在调用cache_grow()分配slab块之后,会调用ctor为每个对象初始化,有点面向对象的思想。colour是颜色的粒度的最大值,colour_off是颜色粒度的单位,colour_next下一个被分配的slab使用的颜色。

struct kmem_list3 { struct list_head slabs_partial; /* partial list first, better asm code */ struct list_head slabs_full; struct list_head slabs_free; unsigned long free_objects; int free_touched; unsigned long next_reap; struct array_cache *shared; };struct kmem_cache_t有一个叫做struct kmem_list3的字段,里边保存了一些链表头,有list3,也就是slabs_full(没有空闲对象),slabs_partial(有部分空闲对象),slabs_free(只包含空闲对象)。还有一个struct array_cache *share字段,指向一个共享本地高速缓存。
struct array_cache { unsigned int avail; unsigned int limit; unsigned int batchcount; unsigned int touched; };struct kmem_cache_t的struct array_cache *array[NR_CPUS];字段指向了CPU的本地高速缓存。avail当前空闲对象的位置,limit是空闲对象的最大值。batchcount一次要填充给数组的对象数,或一次要释放的对象数。至于touched,如果本地高速缓存最近被使用过则touched置1。

struct cache_sizes { size_t cs_size; kmem_cache_t *cs_cachep; kmem_cache_t *cs_dmacachep; };这个是malloc_sizes表的类型,即普通高速缓存,cs_size指出了cache每次分配的内存区的大小,为32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072。这里一共13种几何分布的内存区,每种有两个高速缓存,一个用于ISA DMA分配,另一个适用于常规分配。

struct slab { struct list_head list; unsigned long colouroff; void *s_mem; /* including colour offset */ unsigned int inuse; /* num of objs active in slab */ kmem_bufctl_t free; };

这个就是SLAB描述符了,所有的slab描述符连在头节点为slabs_full,slabs_partial,slabs_free的双向链表上,colouroff为第一个对象的偏移。s_mem是slab中第一个对象的地址。这里的kmem_bufctl_t是对象描述符,在i386体系中是unsigned short类型,占2个字节。free保存下一个空闲对象的下标。

普通和专用高速缓存:

static kmem_cache_t cache_cache = { .lists = LIST3_INIT(cache_cache.lists), .batchcount = 1, .limit = BOOT_CPUCACHE_ENTRIES, .objsize = sizeof(kmem_cache_t), .flags = SLAB_NO_REAP, .spinlock = SPIN_LOCK_UNLOCKED, .name = "kmem_cache", #if DEBUG .reallen = sizeof(kmem_cache_t), #endif };第一个高速缓存叫kmem_cache,存放在cache_cache变量中,它包含由内核其他部分使用的高速缓存的高速缓存描述符。上边说的那个叫malloc_sizes的表保存了指向这26个高速缓存的描述符。专用高速缓存使用kmem_cache_create()创建的,下一篇文档会专门介绍。

slab着色基本原理:

CPU访问内存时,会把一些经常访问的缓存到cache中。缓存到哪个cache line是靠低地址,所以即使距离很远的两块内存,只要低地址相同还是会被缓存到相同的cache line中,如下图。这样会经常造成cache miss,cache line被不停的换入,换出,导致cache颠簸。


如果在同一个cache的不同slab前加上不同的偏移,这样他们的低地址就不同了,可以被缓存在不同的cache line。

但是这样也存在问题,比如colour是3,那么第一个对象的偏移是0*32,第二个是1*32,第三个是2*32,可是第四个的时候又循环回来0*32,所以着色是有局限性的,当slab比较少的时候可以,比较多了着色就不起什么作用了,这是slab的缺陷之一。




Linux常见驱动源码分析(kernel hacker修炼)--李万鹏 李万鹏 IBM Linux Technology Center kernel team 驱动资料清单内容如下: Linux设备模型(中)之上层容器.pdf Linux设备模型(上)之底层模型.pdf Linux驱动修炼-驱动中一些常见的宏.pdf Linux驱动修炼-内存映射.pdf Linux驱动修炼-看门狗框架源码分析.pdf Linux驱动修炼-触摸屏驱动之s3c2410_ts源码分析.pdf Linux驱动修炼-SPI驱动框架源码分析().pdf Linux驱动修炼-SPI驱动框架源码分析().pdf Linux驱动修炼-SPI驱动框架源码分析().pdf Linux驱动修炼-RTC子系统框架源码分析.pdf Linux驱动修炼-platform.pdf Linux驱动修炼-LCD背光gpio控制.pdf Linux驱动修炼-INPUT子系统().pdf Linux驱动修炼-INPUT子系统().pdf Linux驱动修炼-framebuffer(中).pdf Linux驱动修炼-framebuffer(下).pdf Linux驱动修炼-framebuffer(上).pdf Linux驱动修炼-DMA框架源码分析().pdf Linux驱动修炼-DMA框架源码分析().pdf Linux驱动修炼-DM9000A网卡驱动框架源码分析().pdf Linux驱动修炼-DM9000A网卡驱动框架源码分析().pdf Linux驱动修炼-DM9000A网卡驱动框架源码分析().pdf Linux驱动修炼-clock框架.pdf Linux驱动修炼-ADC驱动.pdf Linux内核访问外设I O资源的方式.pdf LINUX内核USB子系统学习笔记之初识USB.pdf kernel hacker修炼之驱动-流水灯.pdf kernel hacker修炼之驱动-混杂设备.pdf kernel hacker修炼之驱动-按键.pdf kernel hacker修炼之PCI subsystem().pdf kernel hacker修炼之PCI subsystem().pdf kernel hacker修炼之PCI subsystem().pdf kernel hacker修炼之PCI subsystem().pdf kernel hacker修炼之PCI subsystem().pdf
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值