Linux内核 内存管理(6)字节分配机制(slab/malloc)
当在内核中需要分配几十个字节的小块内存的时候,怎么办?
1.struct kmem_cache数据结构
https://elixir.bootlin.com/linux/v6.6/source/include/linux/slab_def.h#L12
全局缓存描述符,定义对象属性,协调跨节点分配策略
cpu_cache:每个 CPU 维护的本地缓存,用于无锁快速分配 / 释放。避免多 CPU 竞争同一锁,提升并发性能。 batchcount:与全局 Slab 同步时的批量处理数量。例如,本地缓存不足时一次性从全局获取 batchcount 个对象。 limit:每个 CPU 缓存的最大对象数,超过则放回全局 Slab。 shared:用于多核系统 size:对象实际占用字节数(含对齐填充)。 reciprocal_buffer_size:用于快速除法的倒数优化值(加速计算)。 flags:控制缓存行为的标志,例如: SLAB_TYPESAFE_BY_RCU:启用 RCU 安全释放。 SLAB_RECLAIM_ACCOUNT:计入内存回收统计。 num:每个 Slab 可容纳的对象数量(取决于 size 和页大小) gfporder:分配 Slab 时请求的连续页数量(2^gfporder 页)。 例如,gfporder=0 表示 1 页,gfporder=1 表示 2 页。 allocflags:内存分配标志(如 GFP_DMA 用于 DMA 设备内存)。 colour:Slab 着色的颜色数,用于减少缓存行冲突。 colour_off:着色的字节偏移量,错开不同 Slab 的对象地址。 node:指向各 NUMA 节点的 kmem_cache_node 数组,每个节点维护本地 Slab 链表。 实现内存局部性优化(优先使用本地内存)。
2.struct kmem_cache_node
https://elixir.bootlin.com/linux/v6.6/source/mm/slab.h#L776
kmem_cache_node 是 NUMA 节点级的缓存描述符,每个 kmem_cache 包含多个 kmem_cache_node(每个 NUMA 节点一个)。如图就是kmem_cache里的kmem_cache_node链表
kmem_cache(全局) ├─ node[0] → kmem_cache_node(NUMA 节点 0) │ ├─ slabs_partial(部分满的 Slab 链表) │ ├─ slabs_full(已满的 Slab 链表) │ └─ slabs_free(空闲的 Slab 链表) ├─ node[1] → kmem_cache_node(NUMA 节点 1) │ └─ ... └─ ...
3.struct array_cache
https://elixir.bootlin.com/linux/v6.6/source/mm/slab.c#L185
用来建立本地对象缓存池,每个CPU一个,有利于:
让对象尽可能的使用同一个CPU上的cache,有利于提高效率
不需要额外的自旋锁,避免锁的争用
struct array_cache { unsigned int avail; // 当前可用的对象数量 unsigned int limit; // 缓存的最大对象数量(实际存储在per_cpu_pages中) unsigned int batchcount; // 与全局Slab同步时的批量处理数量 unsigned int touched; // 标记是否有CPU使用过此缓存(用于内存回收) void *entry[]; // 对象指针数组(柔性数组成员,实际大小动态分配) };
如图就是kmem_cache里的array_cache
CPU请求分配对象 ──► 检查本地array_cache(无锁) │ ├─ 缓存有对象 ──► 直接分配(LIFO) │ └─ 缓存为空 ──► 从当前NUMA节点的kmem_cache_node获取 │ ├─ 从slabs_partial分配 ──► 更新Slab状态 │ └─ 无可用Slab ──► 创建新Slab(从伙伴系统分配)
4.内存管理组件关系图
(加上pdf里的图)
+------------------+ +------------------+ +------------------+ | struct kmem_cache |───►| struct per_cpu_pages |───►| struct array_cache | | (缓存描述符) | | (每CPU页缓存) | | (每CPU对象缓存) | +------------------+ +------------------+ +------------------+ │ │ │ │ │ └─ entry[]: 对象指针数组 │ │ │ └─ cpu_slab: 指向array_cache │ ├──────────────────────────► +------------------+ │ struct kmem_cache_node │ │ (NUMA节点缓存描述符) │ +------------------+ │ ├── slabs_partial: 部分满Slab链表 ├── slabs_full: 已满Slab链表 └── slabs_free: 空闲Slab链表
5.kmalloc
https://elixir.bootlin.com/linux/v6.6/source/include/linux/slab.h#L590
根据所分配的尺寸大小,size大会走 kmalloc_large->__kmalloc_large_node->alloc_pages_node (页分配)
size小会走kmalloc_trace->__kmem_cache_alloc_node->slab_alloc_node(slab分配)
#嵌入式##嵌入式笔面经分享##牛客在线求职答疑中心##牛客创作赏金赛#