从 Linux 内核内存管理框架图可知:slab/slub/slob 都是构建在伙伴系统之上的小内存块管理机制。

伙伴系统以 page(通常为 4KB)为单位进行物理内存分配。但在大量场景中——如频繁创建销毁 struct inode、task_struct 或 sk_buff 等固定尺寸内核对象——申请整页内存既低效又易造成内部碎片。slab 分配器正是为此而生:它复用伙伴系统分配的连续页面,在其上实现细粒度、无锁化、缓存友好的对象池管理。
本章将围绕以下主线展开:
- 静态认知:从
struct kmem_cache 等核心数据结构出发,理解 slab 的组织逻辑;
- 生命周期:完整梳理
kmem_cache_create → kmem_cache_alloc → kmem_cache_free → kmem_cache_destroy 四阶段行为;
- 底层联动:解析
kmalloc 如何基于 slab 实现字节级通用分配,并揭示其与 slab_caches 全局链表的映射关系。
需要强调的是:slab 分配器本身不直接管理物理页,而是依赖伙伴系统提供底层页面支撑。它所做的,是在这些连续物理页之上构建一套高效、可追踪、支持调试的“对象工厂”。
slab 分配器相关核心函数
struct kmem_cache *kmem_cache_create(const char *, size_t, size_t,
unsigned long, void (*)(void *));
// 创建 slab 描述符 kmem_cache,此时并未真正分配物理内存
void *kmem_cache_alloc(struct kmem_cache *, gfp_t flags);
// 从指定 slab 缓存中分配一个对象
void kmem_cache_free(struct kmem_cache *, void *);
// 将对象归还至对应 slab 缓存
void kmem_cache_destroy(struct kmem_cache *);
// 销毁 slab 描述符(需确保所有对象已释放)

关键问题解析
Q:每个 slab 由多少个页面组成?
A:每个 slab 至少占用 1 个物理页面,最多不超过 2^MAX_ORDER(通常为 2^10 = 1024 页,即 4MB)。具体页数由 gfporder 字段决定,该值在 kmem_cache_create 时通过 calculate_slab_order() 动态计算得出。
Q:slab 所需物理内存何时分配?
A:kmem_cache_create() 仅初始化描述符,不分配页;首次调用 kmem_cache_alloc() 且本地 CPU 缓存、共享缓存及 slabs_partial/slabs_free 链表均无空闲对象时,才触发 cache_grow(),向伙伴系统申请 2^gfporder 个页面,并挂入 slabs_free 链表。
Q:slab 中空闲对象过多,是否回收?如何回收?
A:是,有两种主动回收路径:
- 按需回收:当本地缓冲池
ac->avail > ac->limit 时,kmem_cache_free() 会主动批量释放 batchcount 个对象;若某 slab 中所有对象均空闲且无活跃引用,内核可将其整体销毁并归还页面;
- 定时扫描:内核注册了
delayed_work 任务 cache_reap(),周期性遍历所有 kmem_cache,对长期闲置的 slab 进行清理。
Q:cache colouring(着色)的作用是什么?
A:使不同 slab 上相同偏移位置的对象起始地址在 L1 Cache Line 中错开,避免多个对象映射到同一 cache line 导致的伪共享(false sharing),从而提升多核并发访问性能。
另一个典型应用是 Per-CPU 本地对象缓冲池,其优势在于:
- 对象尽可能在创建它的 CPU 上被复用(提高 cache locality);
- 访问无需加锁(lock-free),显著降低竞争开销。
一、slab 相关核心数据结构

struct kmem_cache:slab 描述符主干
这是整个 slab 缓存的元数据容器,定义于 include/linux/slub_def.h:
struct kmem_cache {
struct array_cache __percpu *cpu_cache;
/* 1) Cache tunables. Protected by slab_mutex */
unsigned int batchcount; // CPU 本地缓存为空时,从 shared/partial/free 链表批量填充的对象数
unsigned int limit; // 本地缓存空闲对象上限,超限则触发释放
unsigned int shared;
unsigned int size; // 对齐后的对象大小(bytes)
struct reciprocal_value reciprocal_buffer_size;
/* 2) touched by every alloc & free from the backend */
unsigned int flags; // 分配掩码(如 SLAB_HWCACHE_ALIGN)
unsigned int num; // 每个 slab 中的对象数量
/* 3) cache_grow/shrink */
unsigned int gfporder; // 占用 2^gfporder 个页面
gfp_t allocflags; // 页面分配标志(如 GFP_DMA)
size_t colour; // cache colouring 可选值个数
unsigned int colour_off; // cache line 大小(通常为 64B)
struct kmem_cache *freelist_cache;
unsigned int freelist_size;
void (*ctor)(void *obj); // 对象构造函数(可选)
/* 4) cache creation/removal */
const char *name; // 缓存名称(如 "kmalloc-64"),可见于 /proc/slabinfo
struct list_head list; // 全局 slab_caches 链表节点
int refcount; // 引用计数(用于安全销毁)
int object_size; // 用户请求的对象原始大小
int align; // 显式对齐要求
/* 5) statistics */
#ifdef CONFIG_DEBUG_SLAB
unsigned long num_active;
unsigned long num_allocations;
unsigned long high_mark;
unsigned long grown;
unsigned long reaped;
unsigned long errors;
unsigned long max_freeable;
unsigned long node_allocs;
unsigned long node_frees;
unsigned long node_overflow;
atomic_t allochit;
atomic_t allocmiss;
atomic_t freehit;
atomic_t freemiss;
int obj_offset;
#endif
#ifdef CONFIG_MEMCG_KMEM
struct memcg_cache_params memcg_params;
#endif
struct kmem_cache_node *node[MAX_NUMNODES]; // NUMA 节点对应的 slab 管理结构
};
✅ 提示:slab 是 Linux 内核 内存管理 的关键子系统之一,其设计深刻体现了对局部性原理与并发效率的权衡。
struct array_cache:Per-CPU 本地对象缓冲池
每个 CPU 拥有一个独立的 array_cache,用于暂存近期分配/释放的对象,避免频繁访问全局链表:
struct array_cache {
unsigned int avail; // 当前可用对象数
unsigned int limit; // 最大允许缓存数
unsigned int batchcount;
unsigned int touched; // 标记是否被访问过(影响回收策略)
void *entry[]; // 对象指针数组(柔性数组)
};
struct kmem_cache_node:NUMA 节点级 slab 管理
针对 NUMA 架构,每个内存节点维护独立的 slab 链表,避免跨节点访问延迟:
struct kmem_cache_node {
spinlock_t list_lock;
#ifdef CONFIG_SLAB
struct list_head slabs_partial; // 部分对象已分配
struct list_head slabs_full; // 所有对象均已分配
struct list_head slabs_free; // 所有对象均空闲
unsigned long free_objects; // 三链表中空闲对象总数
unsigned int free_limit; // 允许的最大空闲对象数
unsigned int colour_next; // per-node cache coloring 索引
struct array_cache *shared; // 多 CPU 共享的缓冲池
struct alien_cache **alien; // 其他节点的缓存副本
unsigned long next_reap;
int free_touched;
#endif
#ifdef CONFIG_SLUB
unsigned long nr_partial;
struct list_head partial;
#ifdef CONFIG_SLUB_DEBUG
atomic_long_t nr_slabs;
atomic_long_t total_objects;
struct list_head full;
#endif
#endif
};
SLAB 常用标志位(flags)
#define SLAB_DEBUG_FREE 0x00000100UL /* DEBUG: Perform (expensive) checks on free */
#define SLAB_RED_ZONE 0x00000400UL /* DEBUG: Red zone objs in a cache */
#define SLAB_POISON 0x00000800UL /* DEBUG: Poison objects */
#define SLAB_HWCACHE_ALIGN 0x00002000UL /* Align objs on cache lines */
#define SLAB_CACHE_DMA 0x00004000UL /* Use GFP_DMA memory */
#define SLAB_STORE_USER 0x00010000UL /* DEBUG: Store the last owner for bug hunting */
#define SLAB_PANIC 0x00040000UL /* Panic if kmem_cache_create() fails */
二、创建 slab 描述符
kmem_cache_create() 是 slab 生命周期的起点,其核心职责是填充 struct kmem_cache 并将其加入全局 slab_caches 链表。
主要参数说明
| 参数 |
含义 |
name |
缓存名称(如 "my_slab_cache"),将出现在 /proc/slabinfo 中 |
size |
对象大小(bytes) |
align |
对齐要求(0 表示默认对齐) |
flags |
分配标志(如 SLAB_HWCACHE_ALIGN) |
ctor |
构造函数指针(可为 NULL) |
函数调用流程概览
kmem_cache_create
└── do_kmem_cache_create
└── __kmem_cache_create
├── calculate_slab_order ← 计算所需 page 数与每 slab 对象数
└── setup_cpu_cache ← 初始化 CPU 本地缓存参数(limit/batchcount)
关键源码片段(精简)
struct kmem_cache *
kmem_cache_create(const char *name, size_t size, size_t align,
unsigned long flags, void (*ctor)(void *))
{
struct kmem_cache *s;
// 检查是否存在同名/同规格缓存(避免重复创建)
s = __kmem_cache_alias(name, size, align, flags, ctor);
if (s)
goto out_unlock;
s = do_kmem_cache_create(cache_name, size, size,
calculate_alignment(flags, align, size),
flags, ctor, NULL, NULL);
return s;
}
static struct kmem_cache *
do_kmem_cache_create(const char *name, size_t object_size, size_t size,
size_t align, unsigned long flags, void (*ctor)(void *),
struct mem_cgroup *memcg, struct kmem_cache *root_cache)
{
struct kmem_cache *s;
s = kmem_cache_zalloc(kmem_cache, GFP_KERNEL); // 分配 kmem_cache 结构体
// 填充字段...
s->name = name;
s->object_size = object_size;
s->size = size;
s->align = align;
s->ctor = ctor;
err = __kmem_cache_create(s, flags); // 核心初始化逻辑
list_add(&s->list, &slab_caches); // 加入全局链表
return s;
}
__kmem_cache_create 核心逻辑
该函数完成对齐计算、着色配置、gfporder 推导、CPU 缓存初始化等关键步骤:
- 对齐处理:依次考虑
BYTES_PER_WORD、REDZONE_ALIGN、用户传入 align,取最大值作为最终对齐单位;
- off-slab 判定:若对象尺寸较大(≥
PAGE_SIZE >> 5),则将 slab 管理结构(freelist)单独存放于额外页面,以节省 slab 内部空间;
- cache colouring:根据
cache_line_size()(通常为 64B)计算 colour_off 和 colour,确保对象起始地址在 cache line 中错开;
- CPU 缓存参数设置:调用
setup_cpu_cache() 设置 limit、batchcount,并分配 array_cache。
slab_state:slab 初始化状态机
内核使用枚举类型标识 slab 子系统的启动阶段:
enum slab_state {
DOWN, /* 尚未启用 slab 功能 */
PARTIAL, /* SLUB:kmem_cache_node 已就绪 */
PARTIAL_NODE, /* SLAB:kmalloc node 结构可用 */
UP, /* slab 缓存已可用,但部分高级特性未启用 */
FULL /* 完全初始化完成 */
};
calculate_slab_order:确定 slab 规模
该函数从 gfporder = 0(4KB)开始尝试,逐级增大,直到满足:
- 每 slab 至少容纳 1 个对象;
- 对象总数不超过
SLAB_OBJ_MAX_NUM(255);
- 剩余空间(
left_over)足够支持 cache colouring。
static size_t calculate_slab_order(struct kmem_cache *cachep,
size_t size, size_t align, unsigned long flags)
{
for (gfporder = 0; gfporder <= KMALLOC_MAX_ORDER; gfporder++) {
unsigned int num;
size_t remainder;
cache_estimate(gfporder, size, align, flags, &remainder, &num);
if (!num) continue; // 不满足最小对象数,继续尝试更大页
if (num > SLAB_OBJ_MAX_NUM) break;
// ... 满足条件则记录
cachep->num = num;
cachep->gfporder = gfporder;
left_over = remainder;
if (left_over * 8 <= (PAGE_SIZE << gfporder)) break; // 着色空间充足
}
return left_over;
}
cache_estimate:估算单 slab 容量
计算给定 2^gfporder 页面下,能容纳多少个 size 大小的对象,以及剩余可用于着色的空间:
static void cache_estimate(unsigned long gfporder, size_t buffer_size,
size_t align, int flags, size_t *left_over,
unsigned int *num)
{
size_t slab_size = PAGE_SIZE << gfporder;
int nr_objs;
if (flags & CFLGS_OFF_SLAB) {
nr_objs = slab_size / buffer_size;
*num = nr_objs;
*left_over = slab_size - nr_objs * buffer_size;
} else {
nr_objs = calculate_nr_objs(slab_size, buffer_size,
sizeof(freelist_idx_t), align);
size_t mgmt_size = calculate_freelist_size(nr_objs, align);
*num = nr_objs;
*left_over = slab_size - nr_objs * buffer_size - mgmt_size;
}
}
三、分配 slab 对象
kmem_cache_alloc() 是用户获取 slab 对象的入口,其执行全程关闭本地中断,保证原子性。

分配路径优先级(自上而下)
- 本地 CPU 缓存 (
ac->entry[]):最快路径,无锁;
- 共享缓存 (
n->shared):跨 CPU 共享池,需加锁;
- partial 链表:已有部分对象被分配的 slab;
- free 链表:全部对象空闲的 slab;
- 全新分配 (
cache_grow):向伙伴系统申请新页,初始化 slab 并加入 slabs_free。
关键源码逻辑(简化)
static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags)
{
struct array_cache *ac = cpu_cache_get(cachep);
if (likely(ac->avail)) {
ac->touched = 1;
objp = ac_get_obj(cachep, ac, flags, false);
if (objp) return objp;
}
return cache_alloc_refill(cachep, flags, force_refill);
}
static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags,
bool force_refill)
{
struct kmem_cache_node *n;
struct array_cache *ac;
int node = numa_mem_id();
ac = cpu_cache_get(cachep);
n = get_node(cachep, node);
spin_lock(&n->list_lock);
// 1. 尝试从 shared 缓存迁移
if (n->shared && transfer_objects(ac, n->shared, batchcount))
goto alloc_done;
// 2. 尝试从 partial/free 链表分配
while (batchcount > 0) {
struct list_head *entry = n->slabs_partial.next;
if (entry == &n->slabs_partial) {
entry = n->slabs_free.next;
if (entry == &n->slabs_free)
goto must_grow; // 链表全空,需新分配
}
struct page *page = list_entry(entry, struct page, lru);
// 从 page 中取出对象,填入 ac->entry[]
...
}
must_grow:
// 3. 触发 cache_grow()
x = cache_grow(cachep, flags | GFP_THISNODE, node, NULL);
...
}
四、释放 slab 对象
kmem_cache_free() 是对象归还的终点,同样全程关闭本地中断。

释放流程要点
- 定位 slab 描述符:通过
cache_from_obj() 从对象虚拟地址反查所属 kmem_cache;
- 归还至本地缓存:调用
ac_put_obj() 将对象插入 ac->entry[];
- 触发回收:若
ac->avail >= ac->limit,则调用 cache_flusharray() 批量归还对象至 slabs_partial 或 slabs_free;
- NUMA 优化:非 NUMA 系统跳过
cache_free_alien(),避免不必要的跨节点访问。
关键源码片段
void kmem_cache_free(struct kmem_cache *cachep, void *objp)
{
unsigned long flags;
cachep = cache_from_obj(cachep, objp); // 通过对象地址找到所属缓存
local_irq_save(flags);
__cache_free(cachep, objp, _RET_IP_);
local_irq_restore(flags);
}
static inline void __cache_free(struct kmem_cache *cachep, void *objp,
unsigned long caller)
{
struct array_cache *ac = cpu_cache_get(cachep);
if (ac->avail < ac->limit) {
STATS_INC_FREEHIT(cachep);
} else {
STATS_INC_FREEMISS(cachep);
cache_flusharray(cachep, ac); // 归还至全局链表
}
ac_put_obj(cachep, ac, objp); // 插入本地缓存
}
static inline struct kmem_cache *cache_from_obj(struct kmem_cache *s, void *x)
{
struct page *page = virt_to_head_page(x);
struct kmem_cache *cachep = page->slab_cache;
if (slab_equal_or_root(cachep, s))
return cachep;
return s;
}
五、kmalloc 分配函数原理
kmalloc() 是内核中最常用的通用内存分配接口,其底层完全基于 slab 分配器实现。
设计原则
- 按需映射:不同尺寸请求映射到预创建的
kmalloc-xx 缓存;
- 对齐保障:所有分配均对齐至
L1_CACHE_BYTES(通常 64B),避免 cache 伪共享;
- 快速路径:关键函数(如
slab_alloc)标记为 __always_inline,减少调用开销。
kmalloc 尺寸分级策略
| 尺寸范围 |
映射方式 |
示例缓存 |
| ≤ 192B |
查表 size_index[] |
kmalloc-64, kmalloc-128, kmalloc-192 |
| 192B ~ 8KB |
fls(size - 1) 计算 order |
kmalloc-256, kmalloc-512, ..., kmalloc-8192 |
| > 8KB |
kmalloc_large() → 直接调用伙伴系统 |
— |
其中 KMALLOC_SHIFT_LOW = 6(64B)、KMALLOC_SHIFT_HIGH = 13(8KB)、KMALLOC_SHIFT_MAX = 23(8MB)。
size_index 映射表(重映射后)
size_index[0]=6 /* 8 */ → kmalloc-64
size_index[1]=6 /* 16 */ → kmalloc-64
...
size_index[7]=6 /* 64 */ → kmalloc-64
size_index[8]=7 /* 72 */ → kmalloc-128
...
size_index[15]=7 /* 128 */ → kmalloc-128
size_index[16]=2 /* 136 */ → kmalloc-192
...
size_index[23]=2 /* 192 */ → kmalloc-192
/proc/slabinfo 输出解读(节选)
kmalloc-8192 12 12 8192 4 8 : tunables 0 0 0 : slabdata 3 3 0
kmalloc-4096 61 88 4096 8 8 : tunables 0 0 0 : slabdata 11 11 0
kmalloc-192 441 441 192 21 1 : tunables 0 0 0 : slabdata 21 21 0
kmalloc-128 1280 1280 128 32 1 : tunables 0 0 0 : slabdata 40 40 0
kmalloc-64 4416 4416 64 64 1 : tunables 0 0 0 : slabdata 69 69 0
字段含义(从左至右):
- 缓存名;
active_objs:当前已分配对象数;
num_objs:总对象数;
objsize:对象大小(bytes);
objperslab:每 slab 对象数;
pagesperslab:每 slab 占用页数;
tunables 后:limit、batchcount、sharedfactor;
slabdata 后:active_slabs、num_slabs、sharedavail。
🔍 /proc/slabinfo 是诊断内核内存泄漏与碎片化的第一手资料,建议结合 CONFIG_DEBUG_SLAB 使用。
六、slab 描述符创建实验
通过编写内核模块,可直观验证 slab 生命周期。
6.1 实验代码 slab_demo.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/init.h>
/* 自定义一个用于 slab 分配的对象结构 */
struct my_object {
int id;
char name[32];
};
/* 全局的 slab 描述符指针 */
static struct kmem_cache *my_cachep;
/* 可选:对象的构造函数,在创建 slab 时自动调用 */
static void my_obj_ctor(void *obj)
{
struct my_object *my_obj = (struct my_object *)obj;
my_obj->id = 0;
memset(my_obj->name, 0, sizeof(my_obj->name));
pr_info("slab_demo: Object constructed\n");
}
/* 模块初始化函数 */
static int __init slab_demo_init(void)
{
struct my_object *obj1, *obj2;
pr_info("slab_demo: Module init\n");
/* 1. 创建 slab 描述符 */
my_cachep = kmem_cache_create("my_slab_cache",
sizeof(struct my_object),
0,
SLAB_HWCACHE_ALIGN,
my_obj_ctor);
if (!my_cachep) {
pr_err("slab_demo: Failed to create slab cache\n");
return -ENOMEM;
}
pr_info("slab_demo: Slab cache created successfully\n");
/* 2. 分配两个 slab 对象 */
obj1 = kmem_cache_alloc(my_cachep, GFP_KERNEL);
if (!obj1) {
pr_err("slab_demo: Failed to allocate obj1\n");
kmem_cache_destroy(my_cachep);
return -ENOMEM;
}
obj2 = kmem_cache_alloc(my_cachep, GFP_KERNEL);
if (!obj2) {
pr_err("slab_demo: Failed to allocate obj2\n");
kmem_cache_free(my_cachep, obj1);
kmem_cache_destroy(my_cachep);
return -ENOMEM;
}
/* 使用对象 */
obj1->id = 1;
strncpy(obj1->name, "Object 1", sizeof(obj1->name) - 1);
pr_info("slab_demo: Obj1: id=%d, name=%s\n", obj1->id, obj1->name);
obj2->id = 2;
strncpy(obj2->name, "Object 2", sizeof(obj2->name) - 1);
pr_info("slab_demo: Obj2: id=%d, name=%s\n", obj2->id, obj2->name);
/* 3. 释放对象 */
kmem_cache_free(my_cachep, obj1);
kmem_cache_free(my_cachep, obj2);
pr_info("slab_demo: Objects freed\n");
return 0;
}
/* 模块退出函数 */
static void __exit slab_demo_exit(void)
{
pr_info("slab_demo: Module exit\n");
/* 4. 销毁 slab 描述符 */
if (my_cachep) {
kmem_cache_destroy(my_cachep);
pr_info("slab_demo: Slab cache destroyed\n");
}
}
module_init(slab_demo_init);
module_exit(slab_demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Slab Demo");
MODULE_DESCRIPTION("A simple demo for kmem_cache creation");
6.2 Makefile
obj-m += slab_demo.o
# 内核源码路径,这里使用当前运行内核的源码
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
6.3 操作步骤
-
编译模块
make
成功后生成 slab_demo.ko。
-
加载模块
sudo insmod slab_demo.ko
-
查看日志
dmesg | tail
输出应包含:
slab_demo: Module init
slab_demo: Object constructed
slab_demo: Object constructed
slab_demo: Slab cache created successfully
slab_demo: Obj1: id=1, name=Object 1
slab_demo: Obj2: id=2, name=Object 2
slab_demo: Objects freed
-
检查 /proc/slabinfo
cat /proc/slabinfo | grep my_slab_cache
示例输出:
my_slab_cache 2 2 64 64 1 : tunables 0 0 0 : slabdata 1 1 0
-
卸载模块
sudo rmmod slab_demo
日志中将出现:
slab_demo: Module exit
slab_demo: Slab cache destroyed
此时 my_slab_cache 条目从 /proc/slabinfo 中消失。
七、slab 分配器调试接口
/proc/slabinfo 详解(开启 CONFIG_DEBUG_SLAB)
slabinfo - version: 2.1 (statistics)
# name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
各字段含义:
active_objs:当前已分配对象数;
num_objs:该缓存总对象数;
objsize:对象实际大小(不含管理开销);
objperslab:每个 slab 中的对象数量;
pagesperslab:每个 slab 占用物理页数;
tunables:limit(本地缓存上限)、batchcount(批量迁移数)、sharedfactor(共享因子);
slabdata:active_slabs(部分对象已分配的 slab 数)、num_slabs(总 slab 数)、sharedavail(共享缓存中可用对象数)。
八、kmem 相关 Tracepoint
内核提供了丰富的 tracepoint 用于监控内存分配行为,位于 include/trace/events/kmem.h。
8.1 kmalloc/kfree 跟踪事件
kfree
kmalloc
kmalloc_node
典型 trace 输出:
sh-647 [001] .... 843.814154: kmalloc: call_site=c012fbec ptr=ee042600 bytes_req=200 bytes_alloc=256 gfp_flags=GFP_KERNEL|GFP_ZERO
sh-647 [001] .... 843.816029: kfree: call_site=c0176744 ptr=(null)
sh-647 [001] .... 843.816143: kfree: call_site=c012ebdc ptr=ee042600
💡 ptr 是关联 kmalloc 与 kfree 的关键线索;若存在 kmalloc 无对应 kfree,即为潜在内存泄漏。
可通过 patch 改造 call_site 显示函数名(%pf)提升可读性:
- TP_printk("call_site=%lx ptr=%p ...", __entry->call_site, ...)
+ TP_printk("call_site=%pf ptr=%p ...", __entry->call_site, ...)
改造后输出:
sh-646 [001] .... 102.511304: kmalloc: call_site=do_execveat_common ptr=ee0dd400 ...
sh-646 [001] .... 102.513996: kfree: call_site=do_execveat_common ptr=ee0dd400
8.2 kmem_cache_alloc/kmem_cache_free 事件
kmem_cache_alloc
kmem_cache_alloc_node
kmem_cache_free
用途与 kmalloc 类似,适用于自定义 slab 缓存的跟踪。
8.3 Page 级分配事件
mm_page_alloc
mm_page_alloc_zone_locked
mm_page_free
mm_page_free_batched
8.4 Per-CPU 分配器事件
mm_page_alloc_zone_locked
mm_page_pcpu_drain
8.5 外部碎片事件
mm_page_alloc_extfrag
📚 更多细节请查阅内核文档:Documentation/trace/events-kmem.txt。