内核设置全局性的缓冲池为进程分配task_struct结构,这些小块存储不局限于某个子程序,并且动态变化。Linux采用slab的缓冲区分配和管理方法。
在slab方法中,每种数据结构都有自已专用的缓冲队列,构造函数(创建并初始化),析构函数(撤销并释放);slab管理队列分为2级管理;第一级为cache_cache结构其中保存的都是kmem_cache_t结构(都是其他对象slab的队列头),第二级kmem_cache_t后跟的都是slab_t的slab队列(空闲对象队列)。
struct kmem_cache_s {
//每CPU指针数组,指向包含空闲对象的本地高速缓存。
struct array_cache *array[NR_CPUS];
//要转移进本地高速缓存或从本地高速缓存中转移出的大批对象的数量。
unsigned int batchcount;
//本地高速缓存中空闲对象的最大数目。这个参数可调。
unsigned int limit;
//包含三个链表,为什么要单独放到一个描述符中呢?
struct kmem_list3 lists;
//高速缓存中包含的对象的大小。
unsigned int objsize;
//描述高速缓存永久属性的一组标志。
unsigned int flags; /* constant flags */
//在一个单独slab中的对象的个数。高速缓存中的所有slab具有相同的大小。
unsigned int num; /* # of objs per slab */
//整个slab高速缓存中空闲对象的上限。
unsigned int free_limit; /* upper limit of objects in the lists */
//高速缓存自旋锁。
spinlock_t spinlock;
//一个单独slab中包含的连续页框数目的对数(2^n)。
unsigned int gfporder;
//分配页框时传递给伙伴系统函数的一组标志。
unsigned int gfpflags;
//slab使用的颜色个数。用于slab着色。
size_t colour; /* cache colouring range */
//slab中的基本对齐偏移。
unsigned int colour_off; /* colour offset */
//下一个被分配的slab使用的颜色。就是对齐因子。
unsigned int colour_next; /* cache colouring */
//指向包含slab描述符的普通slab高速缓存。如果使用了内部slab描述符,则这个字段为NULL。
kmem_cache_t *slabp_cache;
//单个slab的大小。
unsigned int slab_size;
//高速缓存动态属性标志。
unsigned int dflags; /* dynamic flags */
//高速缓存相关的构造方法的指针。
void (*ctor)(void *, kmem_cache_t *, unsigned long);
//高速缓存相关的析构方法的指针。
void (*dtor)(void *, kmem_cache_t *, unsigned long);
//高速缓存名称。
const char *name;
//高速缓存链表。
struct list_head next;
}
struct slab {
//slab高速缓存描述符的三个双向循环链表中的一个。
struct list_head list;
//slab中第一个对象的偏移。
//同一个高速缓存的不同slab有不同的coloroff值。这样可以避免硬件缓存行的不利影响。
unsigned long colouroff;
//slab中第一个对象的地址。
void *s_mem; /* including colour offset */
//当前正在使用的slab中的对象个数。
unsigned int inuse; /* num of objs active in slab */
//slab中下一个空闲对象的下标。如果没有剩下空闲对象则为BUFCT_END
kmem_bufctl_t free;
};
slab的大小可能2^n(n=0...5)个连续的物理页面构成。具体大小因对象而异,初始化时会计算出大小。slab的双向链队列从逻辑上分为3截(1各个slab上的对象都已分配完,2各个slab上的对象已经部分的分配使用,3各个slab上的对象都处于空闲状态)
并非内核中所有使用的数据结构都有必要拥有专用的缓冲区队列,一些不常用、初始化开销也不大的数据结构可以合用一个通用的缓冲区分配机制--slab_cache采用结构数组,从32byte到128k大小;通过kmalloc()分配,kfree()释放;
struct array_cache {
//指向本地高速缓存中可使用对象的指针的个数。
//它同时也作为高速缓存中第一个空槽的下标。
unsigned int avail;
//本地高速缓存的大小,也就是本地高速缓存中指针的最大个数
unsigned int limit;
//本地高速缓存重新填充或者腾空时使用的块大小
unsigned int batchcount;
//如果最近被使用过,则置为1
unsigned int touched;
};
缓冲区的分配和释放
skb_init()建立了一种缓冲区的专用队列以后,就可以通过kmem_cache_alloc( )来分配缓冲区了。
要分配sk_buff(kmem_cache_s结构),先通过skb-head_from_pool()试试缓冲池,如果缓冲池中得不到,那就进一步通过kmem_cache_alloc()分配。如果slab队列已经没有空闲的slab块了,通过alloc_new_slab()进一步扩充该slab队列。
slab队列的释放由kmem_cache_reap( )进行,依次检查若干专用缓冲区slab队列,看看是否有完全空闲的slab存在。有的话就将这些slab占用的内存页面释放。每次扫描都只扫描一部分slab队列,有全局变量clock_searchp记录下次扫描的起点。并且不会全部回收只回收80%。