slab机制
1.内部碎片和外部碎片
外部碎片
什么是外部碎片呢?我们通过一个图来解释:

假设这是一段连续的页框,阴影部分表示已经被使用的页框,现在需要申请一个连续的5个页框。这个时候,在这段内存上不能找到连续的5个空闲的页框,就会去另一段内存上去寻找5个连续的页框,这样子,久而久之就形成了页框的浪费。称为外部碎片。
内核中使用伙伴算法的迁移机制很好的解决了这种外部碎片。
内部碎片
当我们申请几十个字节的时候,内核也是给我们分配一个页,这样在每个页中就形成了很大的浪费。称之为内部碎片。
内核中引入了slab机制去尽力的减少这种内部碎片。
2.slab分配机制
slab分配器是基于对象进行管理的,所谓的对象就是内核中的数据结构(例如:task_struct,file_struct 等)。相同类型的对象归为一类,每当要申请这样一个对象时,slab分配器就从一个slab列表中分配一个这样大小的单元出去,而当要释放时,将其重新保存在该列表中,而不是直接返回给伙伴系统,从而避免内部碎片。slab分配器并不丢弃已经分配的对象,而是释放并把它们保存在内存中。slab分配对象时,会使用最近释放的对象的内存块,因此其驻留在cpu高速缓存中的概率会大大提高。
3.内核中slab的主要数据结构

简要分析下这个图:kmem_cache是一个cache_chain的链表,描述了一个高速缓存,每个高速缓存包含了一个slabs的列表,这通常是一段连续的内存块。存在3种slab:slabs_full(完全分配的slab),slabs_partial(部分分配的slab),slabs_empty(空slab,或者没有对象被分配)。slab是slab分配器的最小单位,在实现上一个slab有一个货多个连续的物理页组成(通常只有一页)。单个slab可以在slab链表之间移动,例如如果一个半满slab被分配了对象后变满了,就要从slabs_partial中被删除,同时插入到slabs_full中去。
举例说明:如果有一个名叫inode_cachep的struct kmem_cache节点,它存放了一些inode对象。当内核请求分配一个新的inode对象时,slab分配器就开始工作了:
- 首先要查看inode_cachep的slabs_partial链表,如果slabs_partial非空,就从中选中一个slab,返回一个指向已分配但未使用的inode结构的指针。完事之后,如果这个slab满了,就把它从slabs_partial中删除,插入到slabs_full中去,结束;
- 如果slabs_partial为空,也就是没有半满的slab,就会到slabs_empty中寻找。如果slabs_empty非空,就选中一个slab,返回一个指向已分配但未使用的inode结构的指针,然后将这个slab从slabs_empty中删除,插入到slabs_partial(或者slab_full)中去,结束;
- 如果slabs_empty也为空,那么没办法,cache内存已经不足,只能新创建一个slab了。
接下来我们来分析下slab在内核中数据结构的组织,首先要从kmem_cache这个结构体说起了
struct kmem_cache {
struct array_cache *array[NR_CPUS];//per_cpu数据,记录了本地高速缓存的信息,也是用于跟踪最近释放的对象,每次分配和释放都要直接访问它。
unsigned int batchcount;//本地高速缓存转入和转出的大批数据数量
unsigned int limit;//本地高速缓存中空闲对象的最大数目
unsigned int shared;
unsigned int buffer_size;/*buffer的大小,就是对象的大小*/
u32 reciprocal_buffer_size;
unsigned int flags; /* constant flags */
unsigned int num; /* # of objs per slab *//*slab中有多少个对象*/
/* order of pgs per slab (2^n) */
unsigned int gfporder;/*每个slab中有多少个页*/
gfp_t gfpflags; /*与伙伴系统交互时所提供的分配标识*/
size_t colour; /* cache colouring range *//*slab中的着色*/
unsigned int colour_off; /* colour offset */着色的偏移量
struct kmem_cache *slabp_cache;
unsigned int slab_size; //slab管理区的大小
unsigned int dflags; /* dynamic flags */
/* constructor func */
void (*ctor)(void *obj); /*构造函数*/
/* 5) cache creation/removal */
const char *name;/*slab上的名字*/
struct list_head next; //用于将高速缓存连入cache chain
/* 6) statistics */ //一些用于调试用的变量
#ifdef CONFIG_DEBUG_SLAB
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;
unsigned long node_frees;
unsigned long node_overflow;
atomic_t allochit;
atomic_t allocmiss;
atomic_t freehit;
atomic_t freemiss;
int obj_offset;
int obj_size;
#endif /* CONFIG_DEBUG_SLAB */
//用于组织该高速缓存中的slab
struct kmem_list3 *nodelists[MAX_NUMNODES];/*最大的内存节点*/
};
/* Size description struct for general caches. */
struct cache_sizes {
size_t cs_size;
struct kmem_cache *cs_cachep;
#ifdef CONFIG_ZONE_DMA
struct kmem_cache *cs_dmacachep;
#endif
};
由上面的总图可知,一个核心的数据结构就是kmem_list3,它描述了slab描述符的状态。
struct kmem_list3 {
/*三个链表中存的是一个高速缓存slab*/
/*在这三个链表中存放的是cache*/
struct list_head slabs_partial; //包含空闲对象和已经分配对象的slab描述符
struct list_head slabs_full;//只包含非空闲的slab描述符
struct list_head slabs_free;//只包含空闲的slab描述符
unsigned long free_objects; /*高速缓存中空闲对象的个数*/
unsigned int free_limit; //空闲对象的上限
unsigned int colour_next; /* Per-node cache coloring *//*即将要着色的下一个*/
spinlock_t list_lock;
struct array_cache *shared; /* shared per node */
struct array_cache **alien; /* on other nodes */
unsigned long next_reap; /* updated without locking *//**/
int free_touched; /* updated without locking */
};
接下来介绍描述单个slab的结构struct slab
struct slab {
struct list_head list; //用于将slab连入keme_list3的链表
unsigned long colouroff; //该slab的着色偏移
void *s_mem; /* 指向slab中的第一个对象*/
unsigned int inuse; /* num of objs active in slab */已经分配出去的对象
kmem_bufctl_t free; //下一个空闲对象的下标
unsigned short nodeid; //节点标识符
};
在kmem_cache中还有一个重要的数据结构struct array_cache.这是一个指针数组,数组的元素是系统的cpu的个数。该结构用来描述每个cpu的高速缓存,它的主要作用是减少smp系统中对于自旋锁的竞争。
- 实际上,每次分配内存都是直接与本地cpu高速缓存进行交互,只有当其空闲内存不足时,才会从keme_list中的slab中引入一部分对象到本地高速缓存中,而keme_list中的空闲对象也不足时,那么就要从伙伴系统中引入新的页来建立新的slab了。
struct array_cache {
unsigned int avail;/*当前cpu上有多少个可用的对象*/
unsigned int limit;/*per_cpu里面最大的对象的个数,当超过这个值时,将对象返回给伙伴系统*/
unsigned int batchcount;/*一次转入和转出的对象数量*/
unsigned int touched;/*标示本地cpu最近是否被使用*/
spinlock_t lock;/*自旋锁*/
void *entry[]; /*
* Must have this definition in here for the proper
* alignment of array_cache. Also simplifies accessing
* the entries.
*/
};
对上面提到的各个数据结构做一个总结,用下图来描述:

4.关于slab分配器的API
下面看一下slab分配器的接口——看看slab缓存是如何创建、撤销以及如何从缓存中分配一个对象的。一个新的kmem_cache通过kmem_cache_create()函数来创建:
struct kmem_cache *
kmem_cache_create( const char *name, size_t size, size_t align,
unsigned long flags, void (*ctor)(void*));
*name是一个字符串,存放kmem_cache缓存的名字;size是缓存所存放的对象的大小;align是slab内第一个对象的偏移;flag是可选的配置项,用来控制缓存的行为。最后一个参数ctor是对象的构造函数,一般是不需要的,以NULL来代替。kmem_cache_create()成功执行之后会返回一个指向所创建的缓存的指针,否则返回NULL。kmem_cache_create()可能会引起阻塞(睡眠),因此不能在中断上下文中使用。
撤销一个kmem_cache则是通过kmem_cache_destroy()函数:
int kmem_cache_destroy( struct kmem_cache *cachep);
该函数成功则返回0,失败返回非零值。调用kmem_cache_destroy()之前应该满足下面几个条件:首先,cachep所指向的缓存中所有slab都为空闲,否则的话是不可以撤销的;其次在调用kmem_cache_destroy()过程中以及调用之后,调用者需要确保不会再访问这个缓存;最后,该函数也可能会引起阻塞,因此不能在中断上下文中使用。
可以通过下面函数来从kmem_cache中分配一个对象:
void* kmem_cache_alloc(struct kmem_cache* cachep, gfp_t flags);
这个函数从cachep指定的缓存中返回一个指向对象的指针。如果缓存中所有slab都是满的,那么slab分配器会通过调用kmem_getpages()创建一个新的slab。
释放一个对象的函数如下:
void kmem_cache_free(struct kmem_cache* cachep, void* objp);
这个函数是将被释放的对象返还给先前的slab,其实就是将cachep中的对象objp标记为空闲而已
5.使用以上的API写内核模块,生成自己的slab高速缓存。
其实到了这里,应该去分析以上函数的源码,但是几次奋起分析,都被打趴在地。所以就写个内核模块,鼓励下自己吧。
#include <linux/autoconf.h>
#include <linux/module.h>
#include <linux/slab.h>
MODULE_AUTHOR("wangzhangjun");
MODULE_DESCRIPTION("slab test module");
static struct kmem_cache *test_cachep = NULL;
struct slab_test
{
int val;
};
void fun_ctor(struct slab_test *object , struct kmem_cache *cachep , unsigned long flags )
{
printk(KERN_INFO "ctor fuction ...\n");
object->val = 1;
}
static int __init slab_init(void)
{
struct slab_test *object = NULL;//slab的一个对象
printk(KERN_INFO "slab_init\n");
test_cachep = kmem_cache_create("test_cachep",sizeof(struct slab_test)*3,0,SLAB_HWCACHE_ALIGN,fun_ctor);
if(NULL == test_cachep)
return -ENOMEM ;
printk(KERN_INFO "Cache name is %s\n",kmem_cache_name(test_cachep));//获取高速缓存的名称
printk(KERN_INFO "Cache object size is %d\n",kmem_cache_size(test_cachep));//获取高速缓存的大小
object = kmem_cache_alloc(test_cachep,GFP_KERNEL);//从高速缓存中分配一个对象
if(object)
{
printk(KERN_INFO "alloc one val = %d\n",object->val);
kmem_cache_free( test_cachep, object );//归还对象到高速缓存
//这句话的意思是虽然对象归还到了高速缓存中,但是高速缓存中的值没有做修改
//只是修改了一些它的状态。
printk(KERN_INFO "alloc three val = %d\n",object->val);
object = NULL;
}else
return -ENOMEM;
return 0;
}
static void __exit slab_clean(void)
{
printk(KERN_INFO "slab_clean\n");
if(test_cachep)
kmem_cache_destroy(test_cachep);//调用这个函数时test_cachep所指向的缓存中所有的slab都要为空
}
module_init(slab_init);
module_exit(slab_clean);
MODULE_LICENSE("GPL");
我们结合结果来分析下这个内核模块:

这是dmesg的结果,可以发现我们自己创建的高速缓存的名字test_cachep,还有每个对象的大小。

还有构造函数修改了对象里面的值,至于为什么构造函数会出现这么多次,可能是因为,这个函数被注册了之后,系统的其他地方也会调用这个函数。在这里可以分析源码,当调用keme_cache_create()的时候是没有调用对象的构造函数的,调用kmem_cache_create()并没有分配slab,而是在创建对象的时候发现没有空闲对象,在分配对象的时候,会调用构造函数初始化对象。
另外结合上面的代码可以发现,alloc three val是在kmem_cache_free之后打印的,但是它的值依然可以被打印出来,这充分说明了,slab这种机制是在将某个对象使用完之后,就其缓存起来,它还是切切实实的存在于内存中。
再结合/proc/slabinfo的信息看我们自己创建的slab高速缓存

可以发现名字为test_cachep的高速缓存,每个对象的大小(objsize)是16,和上面dmesg看到的值相同,objperslab(每个slab中的对象时202),pagesperslab(每个slab中包含的页数),可以知道objsize * objperslab < pagesperslab。
6.总结
目前只是对slab机制的原理有了一个感性的认识,对于这部分相关的源码涉及到着色以及内存对齐等细节。看的不是很清楚,后面还需要仔细研究。
slab机制的更多相关文章
- 详解slab机制
转 详解slab机制 2015年01月15日 16:34:47 cosmoslhf 阅读数:12657 http://blog.csdn.net/u010246947/article/detail ...
- slab机制总结篇
一: slab是为了解决内部碎片提出的,还是外部碎片? 为了解决内部碎片. 内部碎片的产生:因为所有的内存分配必须起始于可被 4.8 或 16 整除(视处理器体系结构而定)的地址或者因为MMU的分页机 ...
- Nginx之共享内存与slab机制
1. 共享内存 在 Nginx 里,一块完整的共享内存以结构体 ngx_shm_zone_t 来封装,如下: typedef struct ngx_shm_zone_s ngx_shm_zone_t; ...
- [置顶] NGINX原理分析之SLAB分配机制
一.基础概述 如果使用伙伴系统分配和释放算法,不仅会造成大量的内存碎片,同时处理效率也比较低.SLAB是一种内存管理机制,其核心思想是预分配.SLAB是将空间按照SIZE对内存进行分类管理的,当申请一 ...
- Linux内存分配机制之伙伴系统和SLAB
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6539590.html 内核内存管理的一项重要工作就是如何在频繁申请释放内存的情况下,避免碎片的产生.这就要求 ...
- NGINX原理 之 SLAB分配机制(转)
1 引言 众所周知,操作系统使用伙伴系统管理内存,不仅会造成大量的内存碎片,同时处理效率也较低下.SLAB是一种内存管理机制,其拥有较高的处理效率,同时也有效的避免内存碎片的产生,其核心思想是预分配. ...
- [转载]NGINX原理分析 之 SLAB分配机制
作者:邹祁峰 邮箱:Qifeng.zou.job@hotmail.com 博客:http://blog.csdn.net/qifengzou 日期:2013.09.15 23:19 转载请注明来自&q ...
- 分布式缓存技术memcached学习(三)——memcached内存管理机制
几个重要概念 Slab memcached通过slab机制进行内存的分配和回收,slab是一个内存块,它是memcached一次申请内存的最小单位,.在启动memcached的时候一般会使用参数-m指 ...
- Linux内存管理 (5)slab分配器
专题:Linux内存管理专题 关键词:slab/slub/slob.slab描述符.kmalloc.本地/共享对象缓冲池.slabs_partial/slabs_full/slabs_free.ava ...
随机推荐
- Java中的Integer
包装类---Integer Integer 类在对象中包装了一个基本类型int的值.Integer类型的对象包含一个 int 类型的字段.此外,该类提供了多个方法,能在 int 类型和 String ...
- Codeforces 193 D. Two Segments
http://codeforces.com/contest/193/problem/D 题意: 给一个1~n的排列,在这个排列中选出两段区间,求使选出的元素排序后构成公差为1的等差数列的方案数. 换个 ...
- DELL EqualLogic PS存储硬盘故障数据恢复成功案例分享
DELL EqualLogic PS4000采用虚拟ISCSI SAN阵列,为远程或分支办公室.部门和中小企业存储部署带来企业级功能.智能化.自动化和可靠性.以简化的管理.快速的部署及合理的价格满足了 ...
- 链家2018春招Java工程师编程题题解
Light 题目描述 在小红家里面,有n组开关,触摸每个开关,可以使得一组灯泡点亮.现在问你,使用这n组开关,最多能够使得多少个灯泡点亮呢? 输入 第一行一个n,表示有n组开关.接下来n行,每行第一个 ...
- Mego开发文档 - 事务
事务 事务允许以原子方式处理多个数据库操作.如果事务已提交,则所有操作都已成功应用于数据库.如果事务回滚,则没有任何操作应用于数据库. 默认行为 默认情况下,如果数据库提供程序支持事务,则单次的提交操 ...
- 1.phpStrom连接远程代码
1.选择一个新的文件 2.选择自己需要的传输方式 3.添加项目名+路径 4.填写连接基本信息 5.配置成功,下载完毕后,设计本地与远程代码同步修改 自此本地修改代码,同时修改远程服务器代码就设置完毕~ ...
- kubernetes入门(09)kubernetes的命令
Help执行<kubectl>或<kubectl help> | <kubectl --help>获得命令的帮助信息.kubectl的帮助信息.示例相当详细,而且简 ...
- api-gateway实践(13)新服务网关 - 断路保护/熔断机制
参考链接:SpringCloud的Hystrix(五) Hystrix机制 新需求列表 1.在线测试 根据定义,生成输入界面, 点击测试, 验证参数,发起调用,返回执行结果 2.熔断保护 两个实现类: ...
- 配置ssh无密钥登陆
ssh 无密码登录要使用公钥与私钥. linux下可以用用ssh-keygen生成公钥/私钥对,下面以CentOS为例. 有机器LxfN1(192.168.136.128),LxfN2(192.168 ...
- C#实现导出Excel
这段时间用到了导出Excel的功能,这个功能还是比较常用的,我常用的有两个方法,现在整理一下,方便以后查看. 一.实现DataTable数据导出到本地,需要自己传进去导出的路径. /// <su ...