Linux内存管理(3) - slab分配器和kmalloc

本文详细探讨Linux内核的slab分配器,解释了slab如何管理和优化小于一页内存的分配,以及slab缓存的创建、初始化、分配和释放过程。此外,文章还介绍了kmalloc函数如何利用slab分配器来分配内存,强调了slab在内存管理中的高效性和缓存特性。
摘要由CSDN通过智能技术生成

本文目的在于分析Linux内存管理机制的slab分配器。内核版本为2.6.31。

1. SLAB分配器

内核需要经常分配内存,我们在内核中最常用的分配内存的方式就是kmalloc了。前面讲过的伙伴系统只支持按页分配内存,但这个单位太大了,有时候我们并不需要这么大的内存,比如我想申请128字节的空间,如果直接使用伙伴系统则需分配4KB的一整页,这显然是浪费。

slab分配器将页拆分为更小的单位来管理,来满足小于一页的内存需求。它将连续的几个页划分出更小的部分拿来分配相同类型的内存对象,对象的位置尽量做到某种对齐要求(如处理器的L1高速缓存行对齐)以便不对CPU高速缓存和TLB带来显著影响。slab把一类对象统一管理,例如,划出两个页的内存,将其分成n小份用来分配一类对象的实例,同时slab也维护一些通用的对象,用来供kmalloc使用。

提供小块内存并不是slab分配器的唯一任务,由于结构上的特点,它也用作一个缓存,主要针对经常分配内存并释放的对象。slab分配器将释放的内存保存在一个内部列表中,并不马上返还给伙伴系统。在请求为该类对象分配一个实例时,会使用最近释放的内存块,这样就不必触及伙伴系统以缩短分配消耗的时间,另外由于该内存块是“新”的,其驻留在CPU高速缓存的概率也会较高。

在下面的代码分析中,你会看到slab的这些“手段”是如何实现的。

先说一下“slab着色(slab coloring)”机制,是用来防止高速缓存冲突的:相同类型的slab、对象很有可能被保存到相同的CPU cache的缓存行中,经常使用的对象被放到CPU cache中,这当然使我们想要的,但如果两个不同的对象每次都被放到相同的缓存行中,那交替的读取这两个对象,会导致缓存行的内容不断的被更新,也就无法体现缓存的好处了。不过我的内核版本是2.6.31,已经没有slab着色机制了,所以大家看到coloring什么的就不要再纠结了。

内核中还提供了slob和slub两种替代品,因为slab的结构很复杂,并且需要使用太多额外的空间去管理一类对象。关于这两个替代品我就不多说了,slub在性能和缓存占用方面都要优于slab,并且在一些嵌入式设备上会看到内核使用slub。

2. SLAB分配器的实现

2.1 SLAB分配器初始化

系统启动时slab分配器初始化的函数为kmem_cache_init()和kmem_cache_init_late()。函数名中的“cache”是指slab分配器,我们也称作slab缓存,注意,它与CPU中的高速缓存没有关系,但上面讲到slab利用了高速缓存的特性。下面的分析中,我会使用“slab缓存”这种叫法。

kmem_cache_init()函数为分配slab对象准备最基本的环境。在分析这个函数之前,我们先看一个内核中创建slab缓存的例子:

static struct kmem_cache *nf_conntrack_cachep __read_mostly;
 
nf_conntrack_cachep= kmem_cache_create("nf_conntrack",
                     sizeof(struct nf_conn),
                     0, SLAB_DESTROY_BY_RCU, NULL);

上面的代码在内核协议栈的链接跟踪模块中创建struct nf_conn的slab缓存,这个slab缓存用于分配struct nf_conn对象。

当想申请一个struct nf_conn结构的对象时,使用kmem_cache_alloc()函数进行分配。

struct nf_conn *ct;
ct =kmem_cache_alloc(nf_conntrack_cachep, gfp);

可以看到,创建和使用slab缓存是非常方便的。在/proc/slabinfo文件中可以看到内核中所创建的slab缓存。

kmem_cache_create()用于创建一个slab缓存,在哪里创建呢,kmem_cache_init()函数的工作就是初始化用于创建slab缓存的slab缓存。也就是说,一个slab缓存也应该是通过函数kmem_cache_create()来创建的,但是很容易想到,内核中的第一个slab缓存肯定不能通过这个函数来创建,在内核中使用一个编译时生成的静态变量作为第一个slab缓存。

slab缓存用一个struct kmem_cache结构来描述。内核中的第一个slab缓存定义如下:

static struct kmem_cache cache_cache = {
    .batchcount = 1,
    .limit = BOOT_CPUCACHE_ENTRIES,
    .shared = 1,
    .buffer_size = sizeof(struct kmem_cache),
    .name = "kmem_cache",
};

系统中所有的slab缓存都被放入一个全局链表中:

staticstruct list_head cache_chain;

接下来我们来分析kmem_cache_init()函数,它的实现分为下面几个步骤:

1.   创建cache_cache,它将用于分配系统中除了它自身以外的所有slab缓存的kmem_cache对象。

2.   创建可以分配struct arraycache_init和struct kmem_list3的slab cache。先创建这两个通用cache的原因后面会讲到,他们也供kmalloc使用。这两个cache是通过kmem_cache_create()创建的,因为cache_cache已经可用了。这一步之后,将slab_early_init置为0。

3.   使用kmem_cache_create()创建剩余的通用cache,“剩余”是相对第2步中的两个cache,他们都是可以供kmalloc使用的。这些通用cache的名字和对象大小见下面表格。

4.   为cache_cache.array[]和malloc_sizes[INDEX_AC].cs_cachep->array[]重新分配空间。

5.   为cache_cache.nodelists[]、malloc_sizes[INDEX_AC].cs_cachep-> nodelists[]和malloc_sizes[INDEX_L3].cs_cachep-> nodelists[]重新分配空间。

通用cache的名字和对象大小用两个数组来记录:malloc_sizes[]和cache_names[]。

cache_names[]

malloc_sizes[]

size-32

32

size-64

64

size-96

96

size-128

128

……

……

NULL

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值