当前位置: 首页 > news >正文

Linux中slab缓存初始化kmem_cache_init函数和定时回收函数的实现

SLAB 分配器的初始化kmem_cache_init

void __init kmem_cache_init(void)
{size_t left_over;struct cache_sizes *sizes;struct cache_names *names;/** Fragmentation resistance on low memory - only use bigger* page orders on machines with more than 32MB of memory.*/if (num_physpages > (32 << 20) >> PAGE_SHIFT)slab_break_gfp_order = BREAK_GFP_ORDER_HI;/* Bootstrap is tricky, because several objects are allocated* from caches that do not exist yet:* 1) initialize the cache_cache cache: it contains the kmem_cache_t*    structures of all caches, except cache_cache itself: cache_cache*    is statically allocated.*    Initially an __init data area is used for the head array, it's*    replaced with a kmalloc allocated array at the end of the bootstrap.* 2) Create the first kmalloc cache.*    The kmem_cache_t for the new cache is allocated normally. An __init*    data area is used for the head array.* 3) Create the remaining kmalloc caches, with minimally sized head arrays.* 4) Replace the __init data head arrays for cache_cache and the first*    kmalloc cache with kmalloc allocated arrays.* 5) Resize the head arrays of the kmalloc caches to their final sizes.*//* 1) create the cache_cache */init_MUTEX(&cache_chain_sem);INIT_LIST_HEAD(&cache_chain);list_add(&cache_cache.next, &cache_chain);cache_cache.colour_off = cache_line_size();cache_cache.array[smp_processor_id()] = &initarray_cache.cache;cache_cache.objsize = ALIGN(cache_cache.objsize, cache_line_size());cache_estimate(0, cache_cache.objsize, cache_line_size(), 0,&left_over, &cache_cache.num);if (!cache_cache.num)BUG();cache_cache.colour = left_over/cache_cache.colour_off;cache_cache.colour_next = 0;cache_cache.slab_size = ALIGN(cache_cache.num*sizeof(kmem_bufctl_t) +sizeof(struct slab), cache_line_size());/* 2+3) create the kmalloc caches */sizes = malloc_sizes;names = cache_names;while (sizes->cs_size) {/* For performance, all the general caches are L1 aligned.* This should be particularly beneficial on SMP boxes, as it* eliminates "false sharing".* Note for systems short on memory removing the alignment will* allow tighter packing of the smaller caches. */sizes->cs_cachep = kmem_cache_create(names->name,sizes->cs_size, ARCH_KMALLOC_MINALIGN,(ARCH_KMALLOC_FLAGS | SLAB_PANIC), NULL, NULL);/* Inc off-slab bufctl limit until the ceiling is hit. */if (!(OFF_SLAB(sizes->cs_cachep))) {offslab_limit = sizes->cs_size-sizeof(struct slab);offslab_limit /= sizeof(kmem_bufctl_t);}sizes->cs_dmacachep = kmem_cache_create(names->name_dma,sizes->cs_size, ARCH_KMALLOC_MINALIGN,(ARCH_KMALLOC_FLAGS | SLAB_CACHE_DMA | SLAB_PANIC),NULL, NULL);sizes++;names++;}/* 4) Replace the bootstrap head arrays */{void * ptr;ptr = kmalloc(sizeof(struct arraycache_init), GFP_KERNEL);local_irq_disable();BUG_ON(ac_data(&cache_cache) != &initarray_cache.cache);memcpy(ptr, ac_data(&cache_cache), sizeof(struct arraycache_init));cache_cache.array[smp_processor_id()] = ptr;local_irq_enable();ptr = kmalloc(sizeof(struct arraycache_init), GFP_KERNEL);local_irq_disable();BUG_ON(ac_data(malloc_sizes[0].cs_cachep) != &initarray_generic.cache);memcpy(ptr, ac_data(malloc_sizes[0].cs_cachep),sizeof(struct arraycache_init));malloc_sizes[0].cs_cachep->array[smp_processor_id()] = ptr;local_irq_enable();}/* 5) resize the head arrays to their final sizes */{kmem_cache_t *cachep;down(&cache_chain_sem);list_for_each_entry(cachep, &cache_chain, next)enable_cpucache(cachep);up(&cache_chain_sem);}/* Done! */g_cpucache_up = FULL;/* Register a cpu startup notifier callback* that initializes ac_data for all new cpus*/register_cpu_notifier(&cpucache_notifier);/* The reap timers are started later, with a module init call:* That part of the kernel is not yet operational.*/
}

函数功能概述

kmem_cache_init 是 SLAB 分配器的核心初始化函数,负责在内核启动早期建立内存缓存管理系统。它通过一个复杂的引导过程来创建管理其他缓存的基础缓存结构

代码详细分析

1. 内存碎片抵抗设置

if (num_physpages > (32 << 20) >> PAGE_SHIFT)slab_break_gfp_order = BREAK_GFP_ORDER_HI;
  • 目的:根据系统内存大小设置 SLAB 分配器的页面分配策略
  • 逻辑:如果物理内存页数超过 32MB(转换为页数),则使用更大的页面阶数
  • 作用:在内存充足的系统上使用更大的连续页面,减少碎片

2. 初始化缓存链

init_MUTEX(&cache_chain_sem);
INIT_LIST_HEAD(&cache_chain);
list_add(&cache_cache.next, &cache_chain);
  • init_MUTEX:初始化保护缓存链的信号量(互斥锁)
  • INIT_LIST_HEAD:初始化缓存链表的头节点
  • list_add:将 cache_cache 添加到缓存链表中
  • 作用:建立缓存管理的基础数据结构

3. 初始化 cache_cache

cache_cache.colour_off = cache_line_size();
cache_cache.array[smp_processor_id()] = &initarray_cache.cache;
cache_cache.objsize = ALIGN(cache_cache.objsize, cache_line_size());
  • colour_off:设置缓存着色偏移量为缓存行大小,避免缓存伪共享
  • array:为当前 CPU 设置初始的每 CPU 缓存数组
  • objsize:对齐对象大小到缓存行边界

4. 计算缓存参数

cache_estimate(0, cache_cache.objsize, cache_line_size(), 0,&left_over, &cache_cache.num);
if (!cache_cache.num)BUG();
  • cache_estimate:计算一个页面 slab 中可以容纳的对象数量
  • left_over:计算 slab 中剩余的空间
  • BUG():如果无法容纳任何对象,触发内核错误

5. 设置缓存颜色和大小

cache_cache.colour = left_over/cache_cache.colour_off;
cache_cache.colour_next = 0;
cache_cache.slab_size = ALIGN(cache_cache.num*sizeof(kmem_bufctl_t) +sizeof(struct slab), cache_line_size());
  • colour:计算可用的颜色数量(缓存着色)
  • colour_next:初始化下一个颜色索引
  • slab_size:计算 slab 的总大小(包括管理数据和对象)

6. 创建 kmalloc 缓存

sizes = malloc_sizes;
names = cache_names;while (sizes->cs_size) {sizes->cs_cachep = kmem_cache_create(names->name,sizes->cs_size, ARCH_KMALLOC_MINALIGN,(ARCH_KMALLOC_FLAGS | SLAB_PANIC), NULL, NULL);
  • 遍历预定义的缓存大小数组 malloc_sizes
  • 为每种大小创建通用的 kmalloc 缓存
  • ARCH_KMALLOC_MINALIGN:确保缓存对齐
  • SLAB_PANIC:如果创建失败则内核崩溃

7. 处理 off-slab 控制结构

if (!(OFF_SLAB(sizes->cs_cachep))) {offslab_limit = sizes->cs_size-sizeof(struct slab);offslab_limit /= sizeof(kmem_bufctl_t);
}
  • sizes->cs_size:当前缓存的对象大小
  • sizeof(struct slab):slab 管理结构的大小
  • 结果:从对象大小中减去管理结构占用的空间,得到实际可用于存储对象的空间
  • sizeof(kmem_bufctl_t):每个对象控制结构的大小
  • 结果:计算一个 slab 中最多能容纳的对象数量

8. 创建 DMA 缓存

sizes->cs_dmacachep = kmem_cache_create(names->name_dma,sizes->cs_size, ARCH_KMALLOC_MINALIGN,(ARCH_KMALLOC_FLAGS | SLAB_CACHE_DMA | SLAB_PANIC),NULL, NULL);
  • 为每种大小创建专门的 DMA 缓存
  • SLAB_CACHE_DMA:标志表示该缓存用于 DMA 操作

9. 替换引导头数组

ptr = kmalloc(sizeof(struct arraycache_init), GFP_KERNEL);
local_irq_disable();
BUG_ON(ac_data(&cache_cache) != &initarray_cache.cache);
memcpy(ptr, ac_data(&cache_cache), sizeof(struct arraycache_init));
cache_cache.array[smp_processor_id()] = ptr;
local_irq_enable();
  • 用动态分配的数组替换静态的引导期数组
  • 禁用中断确保操作原子性
  • 复制原有数据到新分配的内存
  • 更新缓存指针指向新数组

10. 启用每 CPU 缓存

down(&cache_chain_sem);
list_for_each_entry(cachep, &cache_chain, next)enable_cpucache(cachep);
up(&cache_chain_sem);
  • 获取缓存链信号量
  • 遍历所有缓存,为每个缓存启用 CPU 缓存
  • enable_cpucache:调整每 CPU 缓存到优化大小

11. 完成初始化

g_cpucache_up = FULL;
register_cpu_notifier(&cpucache_notifier);
  • 设置全局状态标志为完全初始化
  • 注册 CPU 通知回调,处理新 CPU 的热插拔

关键设计要点

  1. 引导过程复杂性:函数需要处理"鸡生蛋"问题 - 使用缓存来创建缓存本身
  2. 渐进式初始化:从静态数据过渡到动态分配的内存
  3. 性能优化:缓存对齐、缓存着色、每 CPU 缓存
  4. 错误处理:使用 SLAB_PANIC 确保关键缓存创建成功
  5. 可扩展性:支持 CPU 热插拔和动态缓存调整

CPU 热插拔通知回调cpucache_notifier

static struct notifier_block cpucache_notifier = { &cpuup_callback, NULL, 0 };
static int __devinit cpuup_callback(struct notifier_block *nfb,unsigned long action,void *hcpu)
{long cpu = (long)hcpu;kmem_cache_t* cachep;switch (action) {case CPU_UP_PREPARE:down(&cache_chain_sem);list_for_each_entry(cachep, &cache_chain, next) {struct array_cache *nc;nc = alloc_arraycache(cpu, cachep->limit, cachep->batchcount);if (!nc)goto bad;spin_lock_irq(&cachep->spinlock);cachep->array[cpu] = nc;cachep->free_limit = (1+num_online_cpus())*cachep->batchcount+ cachep->num;spin_unlock_irq(&cachep->spinlock);}up(&cache_chain_sem);break;case CPU_ONLINE:start_cpu_timer(cpu);break;
#ifdef CONFIG_HOTPLUG_CPUcase CPU_DEAD:/* fall thru */case CPU_UP_CANCELED:down(&cache_chain_sem);list_for_each_entry(cachep, &cache_chain, next) {struct array_cache *nc;spin_lock_irq(&cachep->spinlock);/* cpu is dead; no one can alloc from it. */nc = cachep->array[cpu];cachep->array[cpu] = NULL;cachep->free_limit -= cachep->batchcount;free_block(cachep, ac_entry(nc), nc->avail);spin_unlock_irq(&cachep->spinlock);kfree(nc);}up(&cache_chain_sem);break;
#endif}return NOTIFY_OK;
bad:up(&cache_chain_sem);return NOTIFY_BAD;
}

函数功能概述

cpuup_callback 是 CPU 热插拔事件的处理函数,负责在 CPU 上线或下线时动态调整 SLAB 分配器的每 CPU 缓存结构,确保内存分配在多 CPU 环境下的正确性和性能

函数工作流程图

CPU热插拔事件触发↓
switch(action) 根据事件类型处理↓
CPU_UP_PREPARE: CPU上线准备├─ 遍历所有缓存├─ 为新CPU分配array_cache├─ 更新缓存指针和限制└─ 完成准备↓
CPU_ONLINE: CPU上线完成└─ 启动CPU定时器↓
CPU_DEAD/CPU_UP_CANCELED: CPU下线├─ 遍历所有缓存  ├─ 清理该CPU的array_cache├─ 释放内存对象└─ 更新限制↓
返回通知结果

代码详细分析

1. 函数定义和参数

static int __devinit cpuup_callback(struct notifier_block *nfb,unsigned long action,void *hcpu)
{long cpu = (long)hcpu;kmem_cache_t* cachep;
  • nfb: 通知块指针(这里未使用)
  • action: 事件类型(CPU上线、下线等)
  • hcpu: 指向CPU编号的指针
  • cpu = (long)hcpu: 将CPU指针转换为long类型的CPU编号
  • cachep: 用于遍历所有缓存的指针

2. CPU 上线准备阶段 (CPU_UP_PREPARE)

case CPU_UP_PREPARE:down(&cache_chain_sem);
  • 目的: 在新 CPU 上线之前进行准备工作
  • down(&cache_chain_sem): 获取缓存链信号量,确保操作原子性
        list_for_each_entry(cachep, &cache_chain, next) {struct array_cache *nc;nc = alloc_arraycache(cpu, cachep->limit, cachep->batchcount);if (!nc)goto bad;
  • list_for_each_entry: 遍历缓存链中的所有缓存
  • alloc_arraycache: 为新 CPU 分配每 CPU 缓存数组
  • 参数:
    • cpu: CPU 编号
    • cachep->limit: 该缓存每 CPU 数组的大小限制
    • cachep->batchcount: 批量操作的对象数量
  • if (!nc) goto bad: 如果分配失败,跳转到错误处理
                spin_lock_irq(&cachep->spinlock);cachep->array[cpu] = nc;cachep->free_limit = (1+num_online_cpus())*cachep->batchcount+ cachep->num;spin_unlock_irq(&cachep->spinlock);
  • spin_lock_irq: 获取缓存自旋锁并禁用中断
  • cachep->array[cpu] = nc: 将新分配的每 CPU 缓存数组设置到缓存结构中
  • cachep->free_limit 计算:
    • num_online_cpus(): 当前在线 CPU 数量
    • (1+num_online_cpus())*cachep->batchcount: 所有 CPU 的批量操作容量
    • + cachep->num: 加上一个 slab 中的对象数量
      • 避免系统卡在"应该回收但无法回收"的状态
      • 回收后仍保持足够的缓存水平,避免性能波动
    • 作用: 动态调整空闲对象限制,适应变化的 CPU 数量
    • free_limit 是一个阈值,当全局空闲对象数量超过这个值时,系统会开始回收内存而不是继续缓存
  • spin_unlock_irq: 释放锁并恢复中断
        }up(&cache_chain_sem);break;
  • 结束缓存遍历循环
  • up(&cache_chain_sem): 释放缓存链信号量
  • break: 退出 switch 语句

3. CPU 上线完成阶段 (CPU_ONLINE)

case CPU_ONLINE:start_cpu_timer(cpu);break;
  • 目的: CPU 完全上线后的后续处理
  • start_cpu_timer(cpu): 启动该 CPU 的缓存回收定时器
  • 作用: 开始定期回收该 CPU 的闲置缓存对象

4. CPU 下线处理 (CONFIG_HOTPLUG_CPU)

#ifdef CONFIG_HOTPLUG_CPU
case CPU_DEAD:/* fall thru */
case CPU_UP_CANCELED:down(&cache_chain_sem);
  • #ifdef CONFIG_HOTPLUG_CPU: 只在支持 CPU 热插拔时编译
  • CPU_DEAD: CPU 已下线
  • CPU_UP_CANCELED: CPU 上线被取消
  • 两个case共享相同处理逻辑
  • down(&cache_chain_sem): 获取缓存链信号量
        list_for_each_entry(cachep, &cache_chain, next) {struct array_cache *nc;spin_lock_irq(&cachep->spinlock);/* cpu is dead; no one can alloc from it. */nc = cachep->array[cpu];cachep->array[cpu] = NULL;cachep->free_limit -= cachep->batchcount;
  • 遍历所有缓存清理下线 CPU 的资源
  • spin_lock_irq(&cachep->spinlock): 获取缓存锁
  • nc = cachep->array[cpu]: 获取该 CPU 的缓存数组指针
  • cachep->array[cpu] = NULL: 清空指针,防止后续访问
  • cachep->free_limit -= cachep->batchcount: 减少空闲限制,反映 CPU 数量的减少
                free_block(cachep, ac_entry(nc), nc->avail);spin_unlock_irq(&cachep->spinlock);kfree(nc);
  • free_block(cachep, ac_entry(nc), nc->avail):
    • ac_entry(nc): 获取缓存数组中的对象指针数组
    • nc->avail: 当前可用的对象数量
    • 作用: 将该 CPU 缓存中所有对象释放回全局池
  • spin_unlock_irq(&cachep->spinlock): 释放缓存锁
  • kfree(nc): 释放每 CPU 缓存数组结构本身的内存
        }up(&cache_chain_sem);break;
#endif

5. 返回值和错误处理

        return NOTIFY_OK;
bad:up(&cache_chain_sem);return NOTIFY_BAD;
  • NOTIFY_OK: 正常处理完成
  • bad: 标签: 错误处理路径(内存分配失败时)
  • NOTIFY_BAD: 通知处理失败

启动指定 CPU 的 SLAB 缓存回收定时器start_cpu_timer

static void __devinit start_cpu_timer(int cpu)
{struct work_struct *reap_work = &per_cpu(reap_work, cpu);/** When this gets called from do_initcalls via cpucache_init(),* init_workqueues() has already run, so keventd will be setup* at that time.*/if (keventd_up() && reap_work->func == NULL) {INIT_WORK(reap_work, cache_reap, NULL);schedule_delayed_work_on(cpu, reap_work, HZ + 3 * cpu);}
}

函数功能概述

start_cpu_timer 负责为特定 CPU 初始化并启动一个延迟工作项,用于定期回收该 CPU 的 SLAB 缓存中未使用的对象,防止内存长期闲置

代码详细分析

1. 获取每 CPU 的工作结构

struct work_struct *reap_work = &per_cpu(reap_work, cpu);
  • per_cpu(reap_work, cpu): 这是一个每 CPU 变量宏
  • 作用: 获取指定 CPU 的 reap_work 变量地址
  • 每 CPU 变量特点:
    • 每个 CPU 有自己独立的副本
    • 访问不需要加锁(因为其他 CPU 访问的是自己的副本)
  • reap_work: 工作队列结构,用于异步执行回收任务

3. 条件检查

if (keventd_up() && reap_work->func == NULL)

keventd_up()

  • 功能: 检查内核工作队列系统是否已初始化并运行
  • 返回: 布尔值,true 表示工作队列可用

reap_work->func == NULL

  • 检查: 工作结构的函数指针是否为 NULL
  • 目的: 防止重复初始化
  • 场景:
    • 如果 func == NULL: 表示工作项尚未初始化,需要初始化
    • 如果 func != NULL: 表示工作项已初始化,避免重复设置

4. 初始化工作结构

INIT_WORK(reap_work, cache_reap, NULL);

INIT_WORK

  • reap_work: 要初始化的工作结构指针
  • cache_reap: 实际执行的函数(SLAB 回收函数)
  • NULL: 传递给回收函数的数据(这里不需要)

5. 调度延迟工作

schedule_delayed_work_on(cpu, reap_work, HZ + 3 * cpu);

schedule_delayed_work_on 函数

  • 功能: 在指定 CPU 上调度一个延迟执行的工作项
  • 参数:
    • cpu: 目标 CPU 编号
    • reap_work: 要调度的工作结构
    • HZ + 3 * cpu: 延迟时间(jiffies)

延迟时间计算:HZ + 3 * cpu

  • HZ: 系统时钟频率,通常为 100(10ms tick)或 1000(1ms tick)
    • 如果 HZ=100,表示 1 秒 = 100 jiffies
  • 3 * cpu: 为每个 CPU 添加不同的偏移量
  • 实际延迟:
    • CPU0: HZ + 0
    • CPU1: HZ + 3
    • CPU2: HZ + 6

设计目的:

  1. 错开执行时间: 避免所有 CPU 同时进行缓存回收,减少性能抖动
  2. 负载均衡: 将回收操作分散到不同时间点

定期清理未使用的内存cache_reap

static void cache_reap(void *unused)
{struct list_head *walk;if (down_trylock(&cache_chain_sem)) {/* Give up. Setup the next iteration. */schedule_delayed_work(&__get_cpu_var(reap_work), REAPTIMEOUT_CPUC + smp_processor_id());return;}list_for_each(walk, &cache_chain) {kmem_cache_t *searchp;struct list_head* p;int tofree;struct slab *slabp;searchp = list_entry(walk, kmem_cache_t, next);if (searchp->flags & SLAB_NO_REAP)goto next;check_irq_on();spin_lock_irq(&searchp->spinlock);drain_array_locked(searchp, ac_data(searchp), 0);if(time_after(searchp->lists.next_reap, jiffies))goto next_unlock;searchp->lists.next_reap = jiffies + REAPTIMEOUT_LIST3;if (searchp->lists.shared)drain_array_locked(searchp, searchp->lists.shared, 0);if (searchp->lists.free_touched) {searchp->lists.free_touched = 0;goto next_unlock;}tofree = (searchp->free_limit+5*searchp->num-1)/(5*searchp->num);do {p = list3_data(searchp)->slabs_free.next;if (p == &(list3_data(searchp)->slabs_free))break;slabp = list_entry(p, struct slab, list);BUG_ON(slabp->inuse);list_del(&slabp->list);STATS_INC_REAPED(searchp);/* Safe to drop the lock. The slab is no longer* linked to the cache.* searchp cannot disappear, we hold* cache_chain_lock*/searchp->lists.free_objects -= searchp->num;spin_unlock_irq(&searchp->spinlock);slab_destroy(searchp, slabp);spin_lock_irq(&searchp->spinlock);} while(--tofree > 0);
next_unlock:spin_unlock_irq(&searchp->spinlock);
next:;}check_irq_on();up(&cache_chain_sem);/* Setup the next iteration */schedule_delayed_work(&__get_cpu_var(reap_work), REAPTIMEOUT_CPUC + smp_processor_id());
}

函数功能概述

cache_reap 是 SLAB 分配器的周期性回收函数,负责清理各 CPU 的本地缓存和全局空闲列表中未使用的内存 slab,防止内存长期闲置,同时平衡内存使用效率和分配性能

代码详细分析

1. 信号量尝试获取

if (down_trylock(&cache_chain_sem)) {/* Give up. Setup the next iteration. */schedule_delayed_work(&__get_cpu_var(reap_work), REAPTIMEOUT_CPUC + smp_processor_id());return;
}
  • down_trylock(&cache_chain_sem): 非阻塞方式尝试获取缓存链信号量
    • 成功获取返回 0
    • 获取失败返回非 0
  • 如果获取失败: 说明其他线程正在操作缓存链
  • schedule_delayed_work(&__get_cpu_var(reap_work), REAPTIMEOUT_CPUC + smp_processor_id()):
    • __get_cpu_var(reap_work): 获取当前 CPU 的回收工作结构
    • REAPTIMEOUT_CPUC + smp_processor_id(): 延迟时间加上 CPU ID 偏移
    • 目的: 稍后重试,避免阻塞
  • return: 直接返回,不执行本次回收

2. 遍历缓存链

list_for_each(walk, &cache_chain) {kmem_cache_t *searchp;struct list_head* p;int tofree;struct slab *slabp;searchp = list_entry(walk, kmem_cache_t, next);
  • list_for_each(walk, &cache_chain): 遍历缓存链表中的所有缓存
  • 变量声明:
    • searchp: 当前处理的缓存指针
    • p: 临时链表指针
    • tofree: 要释放的 slab 数量
    • slabp: slab 结构指针
  • searchp = list_entry(walk, kmem_cache_t, next):
    • 从链表节点获取包含它的 kmem_cache_t 结构
    • walklist_head 指针,通过 next 字段找到父结构

3. 跳过无需回收的缓存

if (searchp->flags & SLAB_NO_REAP)goto next;
  • SLAB_NO_REAP: 缓存标志位,表示该缓存不应被自动回收
  • 使用场景: 某些关键缓存(如缓存描述符自身)可能设置此标志
  • goto next: 跳过当前缓存,继续处理下一个

4. 中断状态检查和锁定

check_irq_on();
spin_lock_irq(&searchp->spinlock);
  • check_irq_on(): 调试检查,确保中断已开启
    • 在调试版本中可能检查中断状态
    • 生产版本可能是空宏
  • spin_lock_irq(&searchp->spinlock):
    • 获取缓存的自旋锁
    • 同时禁用本地中断(防止中断处理程序竞争)

5. 清空每 CPU 缓存

drain_array_locked(searchp, ac_data(searchp), 0);
  • ac_data(searchp): 获取当前 CPU 的每 CPU 缓存数组
  • drain_array_locked(searchp, array, 0):
    • 将每 CPU 缓存中的空闲对象转移到全局空闲列表
    • 参数 0 表示不强制清空,保留一些对象供快速分配

6. 回收时间间隔检查

if(time_after(searchp->lists.next_reap, jiffies))goto next_unlock;searchp->lists.next_reap = jiffies + REAPTIMEOUT_LIST3;
  • time_after(searchp->lists.next_reap, jiffies):
    • 检查是否到达下一次回收时间
    • 如果 next_reap > jiffies,说明还未到时间
  • goto next_unlock: 跳过本次回收,解锁并继续下一个缓存
  • searchp->lists.next_reap = jiffies + REAPTIMEOUT_LIST3:
    • 设置下一次回收时间
    • REAPTIMEOUT_LIST3 是回收间隔

7. 处理共享缓存和访问标记

if (searchp->lists.shared)drain_array_locked(searchp, searchp->lists.shared, 0);if (searchp->lists.free_touched) {searchp->lists.free_touched = 0;goto next_unlock;
}
  • 共享缓存处理: 如果存在共享每 CPU 缓存,也清空它
  • free_touched 检查:
    • 该标记表示最近有分配/释放活动
    • 如果被设置,说明缓存正在活跃使用,跳过本次回收
    • 清除标记以便下次检查

8. 计算要释放的 slab 数量

tofree = (searchp->free_limit+5*searchp->num-1)/(5*searchp->num);

详细解释:

  • 公式分析: 这是一种向上取整的除法
  • 计算逻辑: 释放约 1/5 的超限部分
  • 策略: 渐进式释放,避免一次性释放过多影响性能

9. 释放空闲 slab 循环

do {p = list3_data(searchp)->slabs_free.next;if (p == &(list3_data(searchp)->slabs_free))break;slabp = list_entry(p, struct slab, list);BUG_ON(slabp->inuse);list_del(&slabp->list);STATS_INC_REAPED(searchp);
  • list3_data(searchp)->slabs_free.next: 获取空闲 slab 链表第一个节点
  • 链表空检查: 如果指向链表头,说明没有空闲 slab,退出循环
  • slabp = list_entry(p, struct slab, list): 获取 slab 结构
  • BUG_ON(slabp->inuse): 调试检查,确保 slab 确实完全空闲
  • list_del(&slabp->list): 从空闲链表中移除
  • STATS_INC_REAPED(searchp): 增加回收统计计数

10. 安全释放 slab

searchp->lists.free_objects -= searchp->num;
spin_unlock_irq(&searchp->spinlock);
slab_destroy(searchp, slabp);
spin_lock_irq(&searchp->spinlock);
} while(--tofree > 0);
  • free_objects -= searchp->num: 更新空闲对象计数
  • 关键技巧: 释放锁后再销毁 slab
    • spin_unlock_irq(&searchp->spinlock): 释放锁,允许其他操作
    • slab_destroy(searchp, slabp): 实际释放内存页(可能耗时)
    • spin_lock_irq(&searchp->spinlock): 重新获取锁继续操作
  • 安全性: slab 已从链表移除,其他线程不会访问到
  • while(--tofree > 0): 继续释放直到达到目标数量

11. 清理和重新调度

next_unlock:spin_unlock_irq(&searchp->spinlock);
next:;
}
check_irq_on();
up(&cache_chain_sem);
/* Setup the next iteration */
schedule_delayed_work(&__get_cpu_var(reap_work), REAPTIMEOUT_CPUC + smp_processor_id());
  • 跳转标签:
    • next_unlock: 解锁当前缓存
    • next: 继续下一个缓存
  • up(&cache_chain_sem): 释放缓存链信号量
  • 最终重新调度:
    • 无论成功与否都安排下一次回收
    • 确保回收机制持续运行
http://www.dtcms.com/a/499418.html

相关文章:

  • 南头专业的网站建设公司厦门网站建设公司怎么选
  • 郑州市做网站的公司西安有什么好玩的地方吗
  • Java 大视界 -- 金融市场情绪预测与动态决策的 Java 大数据实战(2024 券商落地版 425)
  • 运维干货:Nginx 常用配置与问题排查指南
  • 条款16:保证const成员函数的线程安全性
  • 网站开发需求现在网站怎么备案
  • 巧用LEF实现row aware track规划
  • 大话数据结构之 <栈> 和<队列>(C语言)
  • Windows 系统的 Delivery Optimization后台用了几GB流量,如何暂停?
  • 基于ads1256的ADC控制实现
  • 建站之星破解版手机正规建网站企业
  • 建一个电商网站要多少钱wordpress及时聊天
  • 云端思维导图软件,多设备同步无压力
  • Python Web 开发:从框架到实战案例
  • 做网站每天任务及实训过程公司关于网站建设的通知
  • 网站联系方式修改织梦网站建设是在商标哪个类别
  • 网站管理员密码在哪里找个人做网站的
  • C# 中,依赖注入(DI)的实现方式
  • java微服务驱动的社区平台:友猫社区的功能模块与实现逻辑
  • Flask入门教程——李辉 第三章 关键知识梳理
  • 产品更新与重构策略:创新与稳定的平衡之道
  • 【微服务】(1) Spring Cloud 概述
  • 做外贸球衣用什么网站嘉兴做微网站
  • 京华建设科技有限公司网站中华建筑网校
  • 合肥市高新区2025年初中信息学竞赛试题T1-T4 C++ 有故事听[doge]
  • Day 13 root 相关说明--以 ANAEX01 为实例
  • [Linux]学习笔记系列 -- [kernel][lock]debug_locks
  • Linux中双向链表介绍
  • 建设网站的运行费包括什么地方企业做网站哪家公司好
  • 产品频繁重构:企业发展的双刃剑