持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第24天,点击查看活动详情
伙伴系统在分配内存时是以物理页面为单位的,在实际中有很多内存需求是以字节为单位的,那么如果我们需要分配以字节为单位的小内存块,该如何分配呢?slab分配器就是用来解决小内存块分配问题的,也是内存分配中非常重要的角色之一。
slab分配器最终还使用伙伴系统来分配实际的物理页面,只不过slab分配器在这些连续的物理页面上实现了自己的机制,以此来对小内存块进行管理。
slab分配器产生的背景
内核常常需要分配几十字节的小内存块,若为其分配一个物理页面,则非常浪费内存。早期Linux内核实现了以2n字节为大小的内存块分配机制,这个机制非常类似于伙伴系统。这个简单的机制虽然减少了内存浪费,但是并不高效。 一个更好的机制是Sun公司发明的slab机制,最早实现在Solaris 2.4操作系统中。slab机制有如下新特性。
把分配的内存块当作对象(object)来看待。对象可以自定义构造函数(constructor)和析构函数(destructor)来初始化对象的内容并释放对象的内容。 slab对象被释放之后不会马上丢弃而是继续保留在内存中,可能稍后会被用到,这样不需要重新向伙伴系统申请内存。 slab机制可以根据特定大小的内存块来创建slab描述符,如内存中常见的数据结构、打开文件对象等,这样可以有效地避免内存碎片的产生,也可以快速获得频繁访问的数据结构。另外,slab机制也支持按2n字节大小分配内存块。 slab机制创建了多层的缓冲池,充分利用了空间换时间的思想,未雨绸缪,有效地解决了效率问题。
slab分配器提供如下接口函数来创建、释放slab描述符和分配缓存对象。
#创建slab描述符
struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void *))
#释放slab描述符
void kmem_cache_destroy(struct kmem_cache *s)
#分配缓存对象
void *kmem_cache_alloc(struct kmem_cache *, gfp_t flags);
#释放缓存对象
void kmem_cache_free(struct kmem_cache *, void *);
kmem_cache_create()函数中有如下参数。
- name:slab描述符的名称。
- size:缓存对象的大小。
- align:缓存对象需要对齐的字节数。
- flags:分配掩码。
- ctor:对象的构造函数。
在Intel显卡驱动中就大量使用kmem_cache_create()来创建自己的slab描述符。
drivers/gpu/drm/i915/i915_gem.c>
#创建名为"i915_gem_object"的slab描述符
void
i915_gem_load(struct drm_device *dev)
{
...
dev_priv->slab =
kmem_cache_create("i915_gem_object",
sizeof(struct drm_i915_gem_object), 0,
SLAB_HWCACHE_ALIGN,
NULL);
...
}
void *i915_gem_object_alloc(struct drm_device *dev)
{
#分配缓存对象
return kmem_cache_zalloc(dev_priv->slab, GFP_KERNEL);
}
另外一个大量使用slab机制的接口函数是kmalloc()。kmem_cache_create()函数用于创建自己的缓存描述符,kmalloc()函数用于创建通用的缓存,类似于用户空间中C标准库malloc()函数。