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分配)

#嵌入式##嵌入式笔面经分享##牛客在线求职答疑中心##牛客创作赏金赛#
全部评论
mark
点赞 回复 分享
发布于 05-30 14:24 北京
讲得好
点赞 回复 分享
发布于 05-30 14:20 北京
mark
点赞 回复 分享
发布于 05-30 14:11 北京
mark
点赞 回复 分享
发布于 05-30 14:04 北京

相关推荐

评论
1
2
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务