引言
slab是Linux在伙伴系统之上的一种内存管理机制。伙伴系统最小的内存处理大小为4k(即一页的内存大小),然而,实际使用内存过程中,很多都是小内存,为了提高内存申请释放效率、防止内存碎片的产生、防止内存连续分配导致CPU缓存命中率低,linux在伙伴系统之上设计出slab机制来解决这些问题。slab可以理解为它是linux内核的对象池。
1 概述
整个slab机制的数据结构如下图。
注1 :page描述符中很多字段是以union形式存在,所以最好结合源码查看。
1.1 kmem_cache
kmem_cache缓存对象,每个kmem_cache对应一种object类型。各个字段意义如下:
- name: 缓存名称;
- list: 缓存链表,所有的kmem_cache都会连接成一个全局的链表;
- refcount: 应用计数;
- object_size:对象大小;
- align:对象以align个字节对齐;
- ctor:对象初始化函数指针,可以理解为c++的构造函数;
- node: 保存kmem_cache_node数组,在NUMA架构中,每个内存节点对应一个缓存节点来分开管理;
- cpu_cache:为每个cpu变量的对象缓存池,用于加速对象的分配。
1.2 kmem_cache_node
kmem_cache_node是对于不同内存结点的缓存结构体。主要由三个list_head,即三个存储slab描述符的链表。slab是一组连续的页框,这些页框用来分配object所需的内存。
slab描述符结合在page描述符中,也就是page描述符描述slab的时候,就是slab描述符。至少在4.0的源码中看到的是这样。书本所说,slab描述符存储在两个地方:
- 外部slab描述符 :存放在slab外部,位于cache_sizes指向的一个不适合ISA DMA的普通高速缓存中。
- 内部slab描述符:存放在slab内部,位于分配给slab的第一个页框的起始位置。
当对象小于512MB时,或者当内部碎片为slab描述符和对象描述符在slab中留下足够的空间时,slab分配器选择第二种方案。如果slab描述符存放在slab外部,那么高速缓存描述符的flags字段中的CFLGS_OFF_SLAB标志置为1;否则它被置为0。
三个存储slab描述符的链表字段意义如下:
- slabs_free: 空闲slab,即该slab还没有分配一个对象;
- slabs_partial: 部分slab, 即slab已经分配了对象,但是还有部分可以继续分配;
- slabs_full: 满slab,即所有可分配的对象都已分配完,无法再继续分配。
三个链表会随着状态迁移。
1.3 page
page为页描述符字段,同时,当嵌套的slab描述字段中的flags为PG_slab时,表明是一个slab描述符。在page描述符结构体中有两个关键字段s_mem和freelist。
- s_mem: 指向这段连续页框中第一个对象;
- freelist: 指向空闲对象链表。
整个page所指向的内存结构如下图:
整个内存前面为freelist指向;后面为object数组,s_mem指向object 1的起始地址;中间为对象描述符数组。
对象申请释放过程如下:
- 初始状态
假设有五个对象的大小可以分配,才开始都空闲,freelist指向0,page的activie为0。 - 连续分配三个对象
连续分配三个对象后,freelist指向为3,page的activie为3。 - 释放对象1
释放对象1的时候,首先将page的activie减1变为2,然后将1和active所在的2交换位置。然后,后面申请对象的时候就直接申请对象1了。
注:其实关于slab的对象申请和释放远远比这个复杂,这种方式会造成CPU缓存行命中率降低。slab采用着色机制来打乱对象的分配顺序。