本文将介绍Linux的slab层,首先我们要解决一个问题就是什么是slab,slab是做什么用的。
在linux内核中会有许多小对象,这些对象构造销毁十分频繁,比如i-node,dentry。这么这些对象如果每次构建的时候就向内存要一个页,而其实际大小可能只有几个字节,这样就非常浪费,为了解决这个问题就引入了一种新的机制来处理在同一页框中如何分配小存储器区。这就是我们要讨论的slab层。在讲述slab前,我想先铺垫一下有关内存页的概念,我们都知道在linux中内存都是以页为单位来进行管理的(通常为4KB),当内核需要内存就调用如:kmem_getpages这样的接口(底层调用__alloc_pages())。那么内核是如何管理页的分配的,这里linux使用了伙伴算法。slab也是向内核申请一个个页框,然后再对这些页框做管理来达到分配小存储区的目的的。
slab层主要起到了两个方面的作用:
1. slab可以对小对象进行分配,这样就不用为每个小对象分配一个页框,节省了空间。
2. 内核中的一些小对象创建析构很频繁,slab对这些小对象做了缓存,可以重复利用一些相同的对象,减少内存分配次数。
高速缓存
linux中是通过高速缓存数据结构来管理相同大小或者说是相同对象的。比如所有dentry的对象都是在dentry_cache中管理。
高速缓存的数据结构是struct kmem_cache_s。它由以下几部分组成:
1. 三个不同类型的slab组成的链表
分别代表没有空闲,部分空闲和全部空闲的slab。当请求一个对象时,会先从slabs_partial中的slab中分配对象,如果slabs_partial为空,就去slabs_free中去找(然后将这个slab放到slabs_partial中)。
2. 对象的大小和对象的个数
slab描述符
我们先介绍下什么是slab描述符,它代表了什么:
slab在这里就是对一组相同大小的对象进行管理的容器,它的struct类型是slab_t
既然是对对象进行管理,那么就有一个内存的指针指向它所管理的对象:void* s_mem。并且它还需要记录哪些对象被使用了,哪些是空闲的。这时就需要有一个数组记录这个东西,这就是free变量。你可能会奇怪它的类型是一个unsigned int:
它是如何记录这个数组的呢?事实上是记录在slab描述符后面的数组中,这个数组中的每一个元素记录着下一个空闲的内存对象的位置。而free记录的就是第一个空闲的对象。这样就可以从free开始遍历所有空闲的对象。
下图显示了slab对象的内存分布,这里需要解释一下的是slab descriptor会根据情况被存放在当前slab的内存中或者存放在通用的高速缓存中。
高速缓存
slab分配器把对象分组放进高速缓存中,每个高速缓存都是同种类型对象的一种“储备”,例如,当一个文件被打开时,存放相应“打开文件”对象所需的存储器区就是从一个叫做filp的高速缓存中得到的。
那我们先来说下高速缓存描述符: struct kmem_cache_s,这是用来管理一组相同大小的对象的。它有三个由slab组成的链表:
struct list_head slabs_full; struct list_head slabs_partial; struct list_head slabs_free;
分别代表没有空闲,部分空闲和全部空闲的slab,比如
slab着色
多处理器的优化:本地对象数组
cache_cache高速缓存的高速缓存
cache_sizes普通高速缓存
API
这里有一篇文章重点介绍slab的用法: https://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/
这里简单介绍下linux内核中的slab的接口:
1. 创建slab缓存结构
struct kemem_cache* my_cachep;
kmem_cache_create/kmem_cache_destroy
struct kmem_cache * kmem_cache_create( const char *name, size_t size, size_t align, unsigned long flags; void (*ctor)(void*, struct kmem_cache *, unsigned long), void (*dtor)(void*, struct kmem_cache *, unsigned long));