元素码农
基础
UML建模
数据结构
算法
设计模式
网络
TCP/IP协议
HTTPS安全机制
WebSocket实时通信
数据库
sqlite
postgresql
clickhouse
后端
rust
go
java
php
mysql
redis
mongodb
etcd
nats
zincsearch
前端
浏览器
javascript
typescript
vue3
react
游戏
unity
unreal
C++
C#
Lua
App
android
ios
flutter
react-native
安全
Web安全
测试
软件测试
自动化测试 - Playwright
人工智能
Python
langChain
langGraph
运维
linux
docker
工具
git
svn
🌞
🌙
目录
▶
进程管理
▶
进程调度
调度策略
完全公平调度器
实时调度机制
▶
进程通信
信号机制
共享内存
管道与命名管道
▶
进程状态
上下文切换
僵尸进程处理
进程生命周期
▶
内存管理
▶
虚拟内存
页表机制
地址空间布局
TLB管理
▶
物理内存
伙伴系统
slab分配器
内存碎片处理
▶
文件系统
▶
VFS抽象层
inode机制
文件描述符
挂载机制
▶
具体文件系统
Ext4日志机制
Btrfs特性
Proc文件系统
▶
设备驱动
▶
驱动模型
设备树解析
sysfs接口
热插拔处理
▶
网络栈
▶
协议栈架构
sk_buff结构
Netfilter框架
套接字实现
▶
内核同步机制
▶
锁机制
自旋锁实现
RCU机制
Mutex与信号量
▶
启动过程
▶
引导阶段
GRUB交互
内核解压缩
早期初始化
▶
系统调用
▶
调用机制
陷入门实现
vsyscall优化
安全审计
▶
虚拟化和容器支持
▶
容器技术
命名空间隔离
cgroups实现
OverlayFS原理
发布时间:
2025-03-23 10:45
↑
☰
# Linux Slab分配器详解 Slab分配器是Linux内核中的一种内存分配器,专门用于管理内核对象的分配和回收。它通过预先分配固定大小的对象缓存来提高内存分配效率,减少内存碎片。本文将详细介绍Linux系统中的Slab分配器机制。 ## 基本概念 ### Slab结构 1. 缓存对象 ```c struct kmem_cache { struct array_cache *array[NR_CPUS]; // 每CPU缓存 unsigned int batchcount; // 批量分配数量 unsigned int limit; // 缓存限制 unsigned int shared; // 共享缓存 unsigned int size; // 对象大小 unsigned int flags; // 标志位 const char *name; // 缓存名称 int refcount; // 引用计数 // ... }; ``` 2. Slab描述符 ```c struct slab { struct list_head list; // 链表节点 unsigned long colouroff; // 着色偏移 void *s_mem; // 对象数组 unsigned int inuse; // 使用计数 kmem_bufctl_t free; // 空闲对象 }; ``` ### 缓存管理 1. 缓存创建 ```c 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 = kmalloc(sizeof(struct kmem_cache), GFP_KERNEL); if (!s) return NULL; // 初始化缓存 if (kmem_cache_init(s, name, size, align, flags, ctor)) { kfree(s); return NULL; } return s; } ``` 2. 缓存销毁 ```c void kmem_cache_destroy(struct kmem_cache *s) { // 检查引用计数 if (s->refcount) return; // 释放所有slab kmem_cache_shrink(s); // 释放缓存描述符 kfree(s); } ``` ## 内存分配 ### 对象分配 1. 基本分配函数 ```c void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags) { void *ret = NULL; struct array_cache *ac; // 从CPU缓存分配 ac = cpu_cache_get(cachep); if (likely(ac->avail)) { ac->touched = 1; ret = ac->entry[--ac->avail]; goto out; } // CPU缓存为空,从slab分配 ret = cache_alloc_refill(cachep, flags); out: return ret; } ``` 2. 通用分配 ```c void *kmalloc(size_t size, gfp_t flags) { struct kmem_cache *cachep; void *ret; // 选择合适的缓存 cachep = kmalloc_slab(size); if (unlikely(ZERO_OR_NULL_PTR(cachep))) return cachep; // 从缓存分配对象 ret = kmem_cache_alloc(cachep, flags); return ret; } ``` ### 分配策略 1. CPU缓存 ```c static void *cpu_cache_get(struct kmem_cache *cachep) { struct array_cache *ac; void *ret; // 获取当前CPU的缓存 ac = cpu_cache_get_local(cachep); if (likely(ac->avail)) { // 从CPU缓存分配 ret = ac->entry[--ac->avail]; return ret; } // CPU缓存为空,从共享缓存或slab分配 return cache_alloc_refill(cachep, GFP_KERNEL); } ``` 2. Slab分配 ```c static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags) { struct array_cache *ac; int batchcount; ac = cpu_cache_get_local(cachep); batchcount = cachep->batchcount; // 批量分配对象 while (batchcount--) { struct page *page; void *obj; // 获取新的slab页面 page = get_first_slab(cachep, flags); if (!page) break; // 从slab分配对象 obj = slab_get_obj(cachep, page); if (!obj) break; // 加入CPU缓存 ac->entry[ac->avail++] = obj; } return ac->entry[--ac->avail]; } ``` ## 内存释放 ### 对象释放 1. 基本释放函数 ```c void kmem_cache_free(struct kmem_cache *cachep, void *objp) { struct array_cache *ac; // 获取CPU缓存 ac = cpu_cache_get(cachep); // 检查缓存是否已满 if (likely(ac->avail < ac->limit)) { // 加入CPU缓存 ac->entry[ac->avail++] = objp; return; } // CPU缓存已满,释放到slab slab_free(cachep, objp); } ``` 2. 通用释放 ```c void kfree(const void *objp) { struct kmem_cache *c; unsigned long flags; if (unlikely(ZERO_OR_NULL_PTR(objp))) return; // 获取对象所属的缓存 c = virt_to_cache(objp); // 释放对象 kmem_cache_free(c, (void *)objp); } ``` ### 释放策略 1. CPU缓存释放 ```c static void free_block(struct array_cache *ac, void **objpp, int nr_objects) { int i; // 批量释放对象到CPU缓存 for (i = 0; i < nr_objects; i++) { void *objp = objpp[i]; // 加入CPU缓存 ac->entry[ac->avail++] = objp; } } ``` 2. Slab释放 ```c static void slab_free(struct kmem_cache *cachep, void *objp) { struct page *page; // 获取对象所在的页面 page = virt_to_head_page(objp); // 将对象加入slab的空闲链表 slab_free_obj(cachep, page, objp); // 检查slab是否空闲 if (page->inuse == 0) { // 释放空闲slab slab_destroy(cachep, page); } } ``` ## 性能优化 ### 缓存优化 1. 对象对齐 ```c static unsigned int cache_estimate(unsigned long gfporder, size_t size, size_t align) { unsigned int num; size_t slab_size; // 计算slab大小 slab_size = PAGE_SIZE << gfporder; // 考虑对齐要求 size = ALIGN(size, align); // 计算每个slab可容纳的对象数 num = slab_size / size; return num; } ``` 2. 着色机制 ```c static void cache_init_objs(struct kmem_cache *cachep, struct page *page) { void *objp; int i; // 计算着色偏移 objp = page->s_mem + cachep->colour_off; // 初始化对象 for (i = 0; i < cachep->num; i++) { if (cachep->ctor) cachep->ctor(objp); objp += cachep->size; } } ``` ### 内存回收 1. 缓存收缩 ```c int kmem_cache_shrink(struct kmem_cache *cachep) { int ret = 0; struct list_head *slabs_free; // 获取空闲slab链表 slabs_free = &cachep->slabs_free; // 释放空闲slab while (!list_empty(slabs_free)) { struct page *page; // 获取空闲页面 page = list_first_entry(slabs_free, struct page, lru); // 释放页面 list_del(&page->lru); __free_pages(page, cachep->gfporder); ret++; } return ret; } ``` 2. 紧急回收 ```c void kmem_cache_reap(struct kmem_cache *cachep) { struct array_cache *ac; int node; // 回收CPU缓存 for_each_online_node(node) { ac = cachep->array[node]; if (ac && ac->avail) { // 释放CPU缓存中的对象 free_block(ac, ac->entry, ac->avail); ac->avail = 0; } } // 收缩缓存 kmem_cache_shrink(cachep); } ``` ## 调试支持 ### 内存检查 1. 边界检查 ```c static void cache_debug_set_redzone(struct kmem_cache *cachep, void *objp) { unsigned char *redzone; // 设置前后红区 redzone = objp - cachep->red_left_pad; memset(redzone, RED_INACTIVE, cachep->red_left_pad); redzone = objp + cachep->object_size; memset(redzone, RED_INACTIVE, cachep->red_right_pad); } ``` 2. 内存泄漏检测 ```c static void cache_debug_free(struct kmem_cache *cachep, void *objp) { // 检查对象状态 if (cachep->flags & SLAB_RED_ZONE) { if (!check_redzone(cachep, objp)) { // 红区被破坏 slab_error(cachep, "red zone overflow"); } } // 检查重复释放 if (cachep->flags & SLAB_STORE_USER) { if (is_object_free(cachep, objp)) { // 对象已经被释放 slab_error(cachep, "double free"); } } } ``` ### 统计信息 1. 缓存统计 ```c struct kmem_cache_stats { unsigned long num_active; // 活动对象数 unsigned long num_allocations; // 分配次数 unsigned long num_frees; // 释放次数 unsigned long num_slabs; // slab页面数 }; void cache_stats(struct kmem_cache *cachep, struct kmem_cache_stats *stats) { // 收集统计信息 stats->num_active = cachep->num_active; stats->num_allocations = cachep->num_allocations; stats->num_frees = cachep->num_frees; stats->num_slabs = cachep->num_slabs; } ``` 2. 性能监控 ```bash # 查看slab信息 cat /proc/slabinfo # 查看内存使用情况 cat /proc/meminfo | grep Slab ``` ## 最佳实践 1. 缓存使用 - 选择合适的对象大小 - 避免频繁创建销毁缓存 - 合理设置批量分配数量 2. 内存管理 - 及时释