linux 内存管理分析之-----SLAB层

时间:2022-08-24 23:39:56
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就介绍到这吧,简单明了吧,不用自己傻呵呵的去实现空链表了....................................