>object_size, KASAN_SHADOW_SCALE_SIZE),
KASAN_KMALLOC_REDZONE);
}
关于 slab 对象内存布局的核心逻辑封装在 setup_object_debug 函数中:
// 定义在文件:/include/linux/poison.h
#define SLUB_RED_INACTIVE 0xbb
static void setup_object_debug(struct kmem_cache *s, struct page *page,
void *object)
{
// SLAB_STORE_USER:存储最近访问该对象的 owner 信息,方便 bug 追踪
// SLAB_RED_ZONE:在 slub 中对象内存区域的前后填充分别填充一段 red zone 区域,防止内存访问越界
// __OBJECT_POISON:在对象内存区域中填充一些特定的字符,表示对象特定的状态。比如:未被分配状态
if (!(s->flags & (SLAB_STORE_USER|SLAB_RED_ZONE|__OBJECT_POISON)))
return;
// 初始化对象内存,比如填充 red zone,以及 poison
init_object(s, object, SLUB_RED_INACTIVE);
// 设置 SLAB_STORE_USER 起作用,初始化访问对象的所有者相关信息
init_tracking(s, object);
}
init_object 函数主要针对 slab 对象的内存区域进行布局,这里包括对 red zone 的填充,以及 POISON 对象的 object size 区域。
// 定义在文件:/include/linux/poison.h
#define SLUB_RED_INACTIVE 0xbb
// 定义在文件:/include/linux/poison.h
#define POISON_FREE 0x6b /* for use-after-free poisoning */
#define POISON_END 0xa5 /* end-byte of poisoning */
static void init_object(struct kmem_cache *s, void *object, u8 val)
{
// p 为真正存储对象的内存区域起始地址(不包含填充的 red zone)
u8 *p = object;
// red zone 位于真正存储对象内存区域 object size 的左右两侧,分别有一段 red zone
if (s->flags & SLAB_RED_ZONE)
// 首先使用 0xbb 填充对象左侧的 red zone
// 左侧 red zone 区域为对象的起始地址到 s->red_left_pad 的长度
memset(p - s->red_left_pad, val, s->red_left_pad);
if (s->flags & __OBJECT_POISON) {
// 将对象的内容用 0x6b 填充,表示该对象在 slub 中还未被使用
memset(p, POISON_FREE, s->object_size - 1);
// 对象的最后一个字节用 0xa5 填充,表示 POISON 的末尾
p[s->object_size - 1] = POISON_END;
}
// 在对象内存区域 object size 的右侧继续用 0xbb 填充右侧 red zone
// 右侧 red zone 的位置为:对象真实内存区域的末尾开始一个字长的区域
// s->object_size 表示对象本身的内存占用,s->inuse 表示对象在 slub 管理体系下的真实内存占用(包含填充字节数)
// 通常会在对象内存区域末尾处填充一个字长大小的 red zone 区域
// 对象右侧 red zone 区域后面紧跟着的就是 freepointer
if (s->flags & SLAB_RED_ZONE)
memset(p + s->object_size, val, s->inuse - s->object_size);
}
内核首先会用 0xbb 来填充对象左侧 red zone,长度为 kmem_cache-> red_left_pad。
随后内核会用 0x6b 填充 object size 内存区域,并用 0xa5 填充该区域的最后一个字节。object size 内存区域正是真正存储对象的区域。
最后用 0xbb 来填充对象右侧 red zone,右侧 red zone 的起始地址为:p + s->object_size,长度为:s->inuse - s->object_size。如下图所示:
总结
本文我们基于 slab cache 的完整的架构,近一步深入到内核源码中详细介绍了 slab cache 关于内存分配的完整流程:
我们可以看到 slab cache 内存分配的整个流程分为 fastpath 快速路径和 slowpath 慢速路径。
其中在 fastpath 路径下,内核会直接从 slab cache 的本地 cpu 缓存中获取内存块,这是最快的一种方式。
在本地 cpu 缓存没有足够的内存块可供分配的时候,内核就进入到了 slowpath 路径,而 slowpath 下又分为多种情况:
- 从本地 cpu 缓存 partial 列表中分配
- 从 NUMA 节点缓存中分配,其中涉及到了对本地 cpu 缓存的填充。
- 从伙伴系统中重新申请 slab
最后我们介绍了 slab 所在内存页的详细初始化流程,其中包括了对 slab freelist 链表的初始化,以及 slab 对象的初始化。
好了本文的内容到这里就结束了,感谢大家的收看,我们下篇文章见~~~