Slab
understand
是基于2.6的这本书比较有年代了,学习基本思想后看最新代码,应该会好点,记录下备用。
在本章中,将描述通用分配器。它是一个slab分配器,在许多方面与Solaris [MM01]中使用的通用内核分配器非常相似。 Linux的实现很大程度上基于Bonwick [Bon94]的第一份slab分配器文件,其许多改进与他在其后一篇论文[BA01]中所述的非常相似。我们将首先快速了解分配器,然后在深入介绍分配器负责的每个任务之前,先描述所使用的不同结构。
slab分配器的基本思想是使常用对象的缓存保持在初始化状态,以供内核使用。如果没有基于对象的分配器,内核将花费大量时间分配,初始化和释放同一对象。slab分配器旨在缓存释放的对象,以便在使用之间保留基本结构[Bon94]。
slab分配器由可变数量的高速缓存组成,这些高速缓存在称为高速缓存链的双向链接循环列表上链接在一起。在slab分配器的上下文中,高速缓存是特定类型的许多对象的管理器,例如mm_struct
或fs_cache
高速缓存,并由稍后将详细讨论的struct kmem_cache_s
进行管理。缓存通过缓存结构中的下next
字段链接。
每个高速缓存在内存中维护称为slab的连续页面块,这些页面被切成小块,用于高速缓存管理的数据结构和对象。这些不同结构之间的关系如图8.1所示。
slab分配器具有三个主要目标:
- 分配小内存块,以帮助消除内部的碎片,否则碎片会由伙伴系统引起;
- 缓存常用对象,以便系统不会浪费时间分配,初始化和销毁对象。 Solaris上的基准测试表明,使用中的slab分配器的分配速度显着提高[Bon94];
- 通过将对象对准L1或L2缓存来更好地利用硬件缓存。
为了帮助消除通常由二进制伙伴分配器引起的内部碎片,维护了两组小型存储器缓冲区的高速缓存,范围从2^5 (32)
字节到2^17 (131072)
字节。一种高速缓存集适用于DMA
设备。这些高速缓存称为size-N
和size-N(DMA)
,其中N
是分配的大小,并提供了kmalloc()
函数(请参见第8.4.1节)来分配它们。这样,解决了低级页面分配器的最大问题。
slab分配器的第二个任务是维护常用对象的缓存。对于内核中使用的许多结构,初始化对象所需的时间与为对象分配空间的成本相当或超过。当创建一个新的slab时,许多对象被打包到其中并使用构造函数(如果可用)进行初始化。释放对象后,它会保持其初始化状态,这样可以快速分配对象。
slab分配器的最终任务是硬件高速缓存利用率。如果将对象包装到slab中之后仍有剩余空间,则将剩余空间用于为slab着色。slab着色是一种尝试使不同slab中的对象使用高速缓存中不同行的方案。通过将对象放置在slab中的不同起始偏移处,对象可能会在CPU高速缓存中使用不同的行,从而有助于确保来自同一slab高速缓存的对象不太可能相互刷新。通过这种方案,原本会被浪费的空间将实现一项新功能。下图显示了从伙伴分配器分配的页面如何用于存储对象,这些对象使用着色将对象与L1 CPU缓存对齐。
图8.2:包含与L1 CPU缓存对齐的对象的Slab页面
Linux不会尝试根据其物理地址(Kes91)或放置对象的顺序(如针对数据[GAV95]或代码段描述的对象[HK97])来为页面分配着色,但是使用的方案确实有助于提高缓存行的使用率。缓存着色将在8.1.5节添加链接描述中进一步讨论。在SMP系统上,采取了进一步的步骤来帮助提高缓存利用率,其中每个缓存都有为每个CPU保留的一小部分对象。这将在8.5节中进一步讨论。
如果在编译时使用CONFIG_SLAB_DEBUG
设置了选项,则slab分配器将提供slab调试的附加选项。提供了两个调试功能,称为红色分区和对象中毒。使用红色分区时,将标记放置在对象的任一端。如果此标记受到干扰,分配器将知道发生缓冲区溢出的对象并报告该对象。中毒时,将在创建slab时和释放之后用预定义的位模式(在mm / slab.c
中定义为0x5A
)填充对象。在分配时,将检查此模式,如果更改了该模式,分配器将在分配对象之前知道该对象已被使用并对其进行标记。
列出分配器导出的小型但功能强大的API
。
kmem_cache_t * kmem_cache_create(const char *name, size_t size, size_t offset, unsigned long flags,
void (*ctor)(void*, kmem_cache_t *, unsigned long),
void (*dtor)(void*, kmem_cache_t *, unsigned long))
创建一个新的缓存并将其添加到缓存链
int kmem_cache_reap(int gfp_mask)
扫描REAP_SCANLEN
高速缓存,并选择其中一个用于从中获取所有每个cpu对象和空闲slab。内存不足时调用
int kmem_cache_shrink(kmem_cache_t *cachep)
此函数将删除与缓存关联的所有按CPU对象,并删除slabs_free
列表中的所有slab。它返回已释放的页面数。
void * kmem_cache_alloc(kmem_cache_t *cachep, int flags)
从缓存中分配单个对象,并将其返回给调用方
void kmem_cache_free(kmem_cache_t *cachep, void *objp)
释放对象并将其返回到缓存
void * kmalloc(size_t size, int flags)
从大小缓存之一分配一块内存
void kfree(const void *objp)
释放由kmalloc分配的一块内存
int kmem_cache_destroy(kmem_cache_t * cachep)
在从链中删除缓存之前,销毁所有slab中的所有对象并释放所有关联的内存
以上就是用于缓存的Slab Allocator API
Caches
对于每种要缓存的对象,都存在一个缓存。有关正在运行的系统上可用的高速缓存的完整列表,请运行cat / proc / slabinfo。该文件提供了有关缓存的一些基本信息。该文件的输出摘录如下:
[root@node34 ~]# cat /proc/slabinfo |grep orcafs
slabinfo - version: 1.1 (SMP)
kmem_cache 80 80 248 5 5 1 : 252 126
urb_priv 0 0 64 0 0 1 : 252 126
tcp_bind_bucket 15 226 32 2 2 1 : 252 126
inode_cache 5714 5992 512 856 856 1 : 124 62
dentry_cache 5160 5160 128 172 172 1 : 252 126
mm_struct 240 240 160 10 10 1 : 252 126
vm_area_struct 3911 4480 96 112 112 1 : 252 126
size-64(DMA) 0 0 64 0 0 1 : 252 126
size-64 432 1357 64 23 23 1 : 252 126
size-32(DMA) 17 113 32 1 1 1 : 252 126
size-32 850 2712 32 24 24 1 : 252 126
每个列字段都对应于struct kmem_cache_s
结构中的一个字段。上面摘录中列出的列是:
- name 易读的名称,例如“ tcp_bind_bucket”;
- num-active-objs 正在使用的对象数;
- total-objs 总共有多少个对象可用,包括未使用的对象;
- obj-size 每个对象的大小,通常很小;
- num-active-slabs包含活动对象的slab数;
- total-slabs总共有多少块slab;
- num-pages-per-slab 创建一个slab(通常为1个)所需的页面.
如果像示例摘录中那样启用了SMP,则在冒号之后将显示另外两列。它们引用了第8.5节中描述的每个CPU缓存。这些列是:
- limit 这是池将其一半分配给全局空闲池之前可以拥有的空闲对象的数量;
- batchcount没有可用对象时在一个块中分配给处理器的对象数