linux 内存管理分析之-----SLAB层
SLAB层: 想必大多数人一提起linux内存管理,第一反应是"啊,特么的怎么这么复杂",哈哈,其实就是看书时蒙蔽了,别急,兄弟我陪你捋捋.正经的来吧: (1) 先说说平时编程吧,实现个链表,给每个元素申请空间,用的时候添加链表,不用是删除,释放空间,俗话说的增删改查嘛,可是这个效率方面到底高不高呢?答案是:我特么的怎么知道,又不是搞导
·
SLAB层:
想必大多数人一提起linux内存管理,第一反应是"啊,特么的怎么这么复杂",哈哈,其实就是看书时蒙蔽了,别急,兄弟我陪你捋捋.
正经的来吧:
(1) 先说说平时编程吧,实现个链表,给每个元素申请空间,用的时候添加链表,不用是删除,释放空间,俗话说的增删改查嘛,可是这个效率方面到底高不高呢?答案是:我特么的怎么知道,又不是搞导弹路线计算,需要那么搞吗,这个要看项目的具体需要了,但是需不需要都要先搞明白嘛,开源能留下来的东西,你我就别争论了.
(2) 言归正传吧.............. 分配和释放数据结构是linux内核最普遍的操作之一.为了便于数据的频繁分配和释放,编程人员都会实现空链表,哈哈,这会有人郁闷了,这和空链表有什么关系,别急,往下看,这里所说的空链表就是先申请好许多对象在内存,这样在用的时候直接从链表里面取出来就可以用了,不用的时候,在给它放入链表中就可以了,这就是所谓的特么的空链表. 原谅我的语言怎么这么脏,兄弟就是最近心情不好,就是特么的那种特别的不好,搞技术的我就只能在文章里表现了哈哈!!!!
(3) 综上所述,空链表就相当于计算机中的高速缓存了,但是这样还是远远不够,否则的话,linux的命运岂不是掌握在了驱动开发工程师的手里面了,这样的话内核面临的问题是不能全局的控制,当可用的内存变的紧张的时候,内核无法通知每个空闲链表区收缩一下自己的缓存,特么的能通知的到才见鬼了,切切切,可是这样内核不爽了,让你收缩一下自己的缓存你还听不见,反了你了,基于这种情况,linux决定把空链表的特权回收,就应该这样治治你,不然特么的翻天呀你还,于是linux决定提出slab层(也就是所谓的slab分配器). slab分配器扮演着通用数据结构缓存层的角色.丫的,让你牛逼!!
(4) slab分配器的概念在linux操作系统中得以实现,linux数据结构缓冲层具有同样的名字和基本设计思想:
1,频繁使用的数据结构也会频繁的分配和释放,所以应该特么的缓存它们.
2, 频繁分配和释放势必会导致内存碎片,(难以找到大块的内存).为了避免,空闲链表的缓存会连续的存放.因为以释放的数据结构又会放回空闲链表,因此不会导致碎片,
3,回收的对象可以立即投入到下一次的分配中,因此频繁分配和释放的数据结构使用空链表可以提高其性能.
4,如果slab分配器知道对象数据结构,页大小还有缓冲区的大小的话,会做出更加明智的决策.
5,如果让部分缓存专属于特定的CPU处理器,那么就特么的不用考虑SMP,更不用考虑加锁了.
6,对存放的对象进行着色,以防止多个对象映射到相同的高速缓存行
linux slab分配器在设计和实现上也确实考虑到了以上原则.
(5) slab层把不同的对象划分为不同的所谓的高速缓存组,其中每个高速缓存组都存放不同类型的对象,真特么的废话,不然怎么给你讲呢哈哈!!!
例如:一个高速缓存组里面存放( task_struct )进程描述符,而另一个高速缓存组存放(inode)描述符,更特么搞笑的是居然我们平时驱动中使用的kmalloc()也是在slab层之上使用了一组高速缓存,真特么的逆天吧,对于此种原因我就不解释了吧,记住,这就是特么的实现,也是现实.然后这些高速缓存又被划分为slab.明白了吗?,slab由一个或者多个物理上连续的页组成,一般条件下,slab也就只有一个页面的大小,每个高速缓存可以由多个slab组成,
(6) 每个slab都包含一些成员对象,也就是被缓存的数据结构,每个slab处于三种状态之一:空,部分满,满. 当内核需要一个新的对象时,优先从部分满的slab中分配,其次是从空的slab中分配,再特么的不行就创建一个slab,这种策略可以减少碎片.
高速缓存 ------>slab -----> [对象] [对象] [对象]
|
|------->slab -----> [对象]
(7) 每个高速缓存都使用struct kmem_cache 结构表示.该结构包含三个链表:slab_full,slabs_partial, slabs_empty,均存放在kmem_list3结构内,该结构在<mm/slab.c>中定义.这些链表包含高速缓存中的所有slab,我特么的没看错吧,是的你没有看错,是所有的slab, slab描述符struct slab 用来描述每个slab
struct slab{
struct list_head list;
unsigned long colourful //对象的着色偏移
void *s_mem //slab中的第一个对象
unsigned int inuse //slab已分配的对象数
kmem_bufctl_t free //第一个空闲对象,如果有的话
}
(8) slab分配器可以创建新的slab,这是通过_get_free_pages()低级内核页分配器分配的,举个简单的例子吧.(包你看的明白)
static inline void* kmem_getpages(struct kmem_cache *cachp, gfp_t flags)
{
void* addr;
flags |= cachp->gfpflags;
addr = (void*) _get_free_pages(flags, cachp->gfporder);
return addr;
}
接着调用kmem_freepages()释放内存,最终调用的是free_pages().
当然slab层的关键是避免频繁分配和释放页,由此可知,slab层只有当给定的高速缓存部分既没有满也没有空的slab时才会调用页分配函数,而只有在下列情况下才会真正的调用释放函数:(1)当可用的内存变得紧缺时,系统会释放出更多的内存以供使用(2)或当高速缓存被显示的撤销时才会调用函数释放.
slab层的管理是在每个高速缓冲区的基础上,并提供给内核一个简单的接口来完成的.通过接口可以创建和撤销新的高速缓存,并在高速缓存上分配和释放对象,高速缓存及其内slab的复杂管理完全通过slab层的内部机制来处理,
(9) slab分配器的接口:
1, 一个新的高速缓存通过以下函数创建:
struct kmem_cache * kmem_cache_create( const char* name, size_t size, size_t align, unsigned long flags, void (*ctor)(void*));
第一个参数是高速缓存区的名字;
第二个参数是高速缓存区的每个元素的大小;
第三个参数是slab内第一个对象的偏移,一般情况下为0;
第四个参数是flags是可选项
第五个参数为NULL;
2,撤销一个高速缓存则调用
int kmem_cache_destory(struct mem_cache *cachep); return 成功返回0,失败返回非0值;
调用该函数时必须满足两个条件:(1) 高速缓存区中的所有slab都必须为空,哪怕只有一个对象被分配出去并正在使用的话,就不能去撤销这个高速缓存区.
(2) 调用此函数的过程中不能再访问这个高速缓存区了.
(10) 从缓冲区中分配对象
创建高速缓冲区后可以从中获取对象
void* kmem_cache_calloc(struct mem_cache *cachep, gfp_t flags); //flags 为GFP_KERNEL或者GFP_ATOMIC
(11) 释放对象到缓冲区中
void kmem_cache_free(struct mem_cache *cachep,void * objp);
这样就能把它返回到原先的slab中,意思就是把cachep中的对象objp标记为空闲
exmple:
struct kmem_cache *task_struct_cachep;
task_struct_cachep = kmem_cache_create("task_struct", sizeof(struct task_struct), 0, SLAB_PANIC| SLAB_NOTRACK, NULL);
struct task_struct *tsk;
tsk = kmem_cache_calloc(cachep, GFP_KERNEL);
if(!tsk)
return NULL;
int err;
err = kmem_cache_destory(task_struct_cachep);
if(err)
/*出错,撤销高速缓存区*/
哈哈,差不多了,slab就介绍到这吧,简单明了吧,不用自己傻呵呵的去实现空链表了....................................
想必大多数人一提起linux内存管理,第一反应是"啊,特么的怎么这么复杂",哈哈,其实就是看书时蒙蔽了,别急,兄弟我陪你捋捋.
正经的来吧:
(1) 先说说平时编程吧,实现个链表,给每个元素申请空间,用的时候添加链表,不用是删除,释放空间,俗话说的增删改查嘛,可是这个效率方面到底高不高呢?答案是:我特么的怎么知道,又不是搞导弹路线计算,需要那么搞吗,这个要看项目的具体需要了,但是需不需要都要先搞明白嘛,开源能留下来的东西,你我就别争论了.
(2) 言归正传吧.............. 分配和释放数据结构是linux内核最普遍的操作之一.为了便于数据的频繁分配和释放,编程人员都会实现空链表,哈哈,这会有人郁闷了,这和空链表有什么关系,别急,往下看,这里所说的空链表就是先申请好许多对象在内存,这样在用的时候直接从链表里面取出来就可以用了,不用的时候,在给它放入链表中就可以了,这就是所谓的特么的空链表. 原谅我的语言怎么这么脏,兄弟就是最近心情不好,就是特么的那种特别的不好,搞技术的我就只能在文章里表现了哈哈!!!!
(3) 综上所述,空链表就相当于计算机中的高速缓存了,但是这样还是远远不够,否则的话,linux的命运岂不是掌握在了驱动开发工程师的手里面了,这样的话内核面临的问题是不能全局的控制,当可用的内存变的紧张的时候,内核无法通知每个空闲链表区收缩一下自己的缓存,特么的能通知的到才见鬼了,切切切,可是这样内核不爽了,让你收缩一下自己的缓存你还听不见,反了你了,基于这种情况,linux决定把空链表的特权回收,就应该这样治治你,不然特么的翻天呀你还,于是linux决定提出slab层(也就是所谓的slab分配器). slab分配器扮演着通用数据结构缓存层的角色.丫的,让你牛逼!!
(4) slab分配器的概念在linux操作系统中得以实现,linux数据结构缓冲层具有同样的名字和基本设计思想:
1,频繁使用的数据结构也会频繁的分配和释放,所以应该特么的缓存它们.
2, 频繁分配和释放势必会导致内存碎片,(难以找到大块的内存).为了避免,空闲链表的缓存会连续的存放.因为以释放的数据结构又会放回空闲链表,因此不会导致碎片,
3,回收的对象可以立即投入到下一次的分配中,因此频繁分配和释放的数据结构使用空链表可以提高其性能.
4,如果slab分配器知道对象数据结构,页大小还有缓冲区的大小的话,会做出更加明智的决策.
5,如果让部分缓存专属于特定的CPU处理器,那么就特么的不用考虑SMP,更不用考虑加锁了.
6,对存放的对象进行着色,以防止多个对象映射到相同的高速缓存行
linux slab分配器在设计和实现上也确实考虑到了以上原则.
(5) slab层把不同的对象划分为不同的所谓的高速缓存组,其中每个高速缓存组都存放不同类型的对象,真特么的废话,不然怎么给你讲呢哈哈!!!
例如:一个高速缓存组里面存放( task_struct )进程描述符,而另一个高速缓存组存放(inode)描述符,更特么搞笑的是居然我们平时驱动中使用的kmalloc()也是在slab层之上使用了一组高速缓存,真特么的逆天吧,对于此种原因我就不解释了吧,记住,这就是特么的实现,也是现实.然后这些高速缓存又被划分为slab.明白了吗?,slab由一个或者多个物理上连续的页组成,一般条件下,slab也就只有一个页面的大小,每个高速缓存可以由多个slab组成,
(6) 每个slab都包含一些成员对象,也就是被缓存的数据结构,每个slab处于三种状态之一:空,部分满,满. 当内核需要一个新的对象时,优先从部分满的slab中分配,其次是从空的slab中分配,再特么的不行就创建一个slab,这种策略可以减少碎片.
高速缓存 ------>slab -----> [对象] [对象] [对象]
|
|------->slab -----> [对象]
(7) 每个高速缓存都使用struct kmem_cache 结构表示.该结构包含三个链表:slab_full,slabs_partial, slabs_empty,均存放在kmem_list3结构内,该结构在<mm/slab.c>中定义.这些链表包含高速缓存中的所有slab,我特么的没看错吧,是的你没有看错,是所有的slab, slab描述符struct slab 用来描述每个slab
struct slab{
struct list_head list;
unsigned long colourful //对象的着色偏移
void *s_mem //slab中的第一个对象
unsigned int inuse //slab已分配的对象数
kmem_bufctl_t free //第一个空闲对象,如果有的话
}
(8) slab分配器可以创建新的slab,这是通过_get_free_pages()低级内核页分配器分配的,举个简单的例子吧.(包你看的明白)
static inline void* kmem_getpages(struct kmem_cache *cachp, gfp_t flags)
{
void* addr;
flags |= cachp->gfpflags;
addr = (void*) _get_free_pages(flags, cachp->gfporder);
return addr;
}
接着调用kmem_freepages()释放内存,最终调用的是free_pages().
当然slab层的关键是避免频繁分配和释放页,由此可知,slab层只有当给定的高速缓存部分既没有满也没有空的slab时才会调用页分配函数,而只有在下列情况下才会真正的调用释放函数:(1)当可用的内存变得紧缺时,系统会释放出更多的内存以供使用(2)或当高速缓存被显示的撤销时才会调用函数释放.
slab层的管理是在每个高速缓冲区的基础上,并提供给内核一个简单的接口来完成的.通过接口可以创建和撤销新的高速缓存,并在高速缓存上分配和释放对象,高速缓存及其内slab的复杂管理完全通过slab层的内部机制来处理,
(9) slab分配器的接口:
1, 一个新的高速缓存通过以下函数创建:
struct kmem_cache * kmem_cache_create( const char* name, size_t size, size_t align, unsigned long flags, void (*ctor)(void*));
第一个参数是高速缓存区的名字;
第二个参数是高速缓存区的每个元素的大小;
第三个参数是slab内第一个对象的偏移,一般情况下为0;
第四个参数是flags是可选项
第五个参数为NULL;
2,撤销一个高速缓存则调用
int kmem_cache_destory(struct mem_cache *cachep); return 成功返回0,失败返回非0值;
调用该函数时必须满足两个条件:(1) 高速缓存区中的所有slab都必须为空,哪怕只有一个对象被分配出去并正在使用的话,就不能去撤销这个高速缓存区.
(2) 调用此函数的过程中不能再访问这个高速缓存区了.
(10) 从缓冲区中分配对象
创建高速缓冲区后可以从中获取对象
void* kmem_cache_calloc(struct mem_cache *cachep, gfp_t flags); //flags 为GFP_KERNEL或者GFP_ATOMIC
(11) 释放对象到缓冲区中
void kmem_cache_free(struct mem_cache *cachep,void * objp);
这样就能把它返回到原先的slab中,意思就是把cachep中的对象objp标记为空闲
exmple:
struct kmem_cache *task_struct_cachep;
task_struct_cachep = kmem_cache_create("task_struct", sizeof(struct task_struct), 0, SLAB_PANIC| SLAB_NOTRACK, NULL);
struct task_struct *tsk;
tsk = kmem_cache_calloc(cachep, GFP_KERNEL);
if(!tsk)
return NULL;
int err;
err = kmem_cache_destory(task_struct_cachep);
if(err)
/*出错,撤销高速缓存区*/
哈哈,差不多了,slab就介绍到这吧,简单明了吧,不用自己傻呵呵的去实现空链表了....................................
更多推荐
已为社区贡献2条内容
所有评论(0)