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

Linux中kfree内存回收函数的实现

内核内存释放函数kfree

void kfree (const void *objp)
{kmem_cache_t *c;unsigned long flags;if (!objp)return;local_irq_save(flags);kfree_debugcheck(objp);c = GET_PAGE_CACHE(virt_to_page(objp));__cache_free(c, (void*)objp);local_irq_restore(flags);
}

1. 代码详细解释

void kfree (const void *objp)
  • objp: 要释放的对象指针,由 kmalloc分配
  • 无返回值

1.1. NULL 指针检查

if (!objp)return;

安全防护:如果传入空指针,直接返回

  • 避免对 NULL 指针操作导致系统崩溃

1.2. 保存并禁用中断

local_irq_save(flags);

关键操作

  • local_irq_save(flags): 保存当前中断状态到 flags,并禁用中断
  • 为什么需要:防止在释放过程中被中断打断,保证操作的原子性
  • 保护 slab 缓存数据结构的一致性

1.3. 调试检查

kfree_debugcheck(objp);

调试功能:在调试模式下进行各种完整性检查

1.4. 获取对象所属的缓存

c = GET_PAGE_CACHE(virt_to_page(objp));

关键查找

  • virt_to_page(objp): 将对象虚拟地址转换为对应的物理页面描述符
  • GET_PAGE_CACHE(): 从页面描述符中获取关联的 slab 缓存

内存关联回顾

// 在 set_slab_attr 中建立的关联:
// page->lru.next = (struct list_head*)cachep;
// 现在通过 GET_PAGE_CACHE 反向查找

1.5. 实际释放操作

__cache_free(c, (void*)objp);

核心释放:将对象释放回对应的 slab 缓存

  • c: 对象所属的 slab 缓存
  • (void*)objp: 要释放的对象指针(去掉 const 限定)

1.6. 恢复中断状态

local_irq_restore(flags);

清理操作:恢复之前保存的中断状态

  • 重新启用中断(如果之前是启用的)
  • 保证系统中断响应的正常性

slab 缓存释放函数__cache_free

static inline void __cache_free (kmem_cache_t *cachep, void* objp)
{struct array_cache *ac = ac_data(cachep);check_irq_off();objp = cache_free_debugcheck(cachep, objp, __builtin_return_address(0));if (likely(ac->avail < ac->limit)) {STATS_INC_FREEHIT(cachep);ac_entry(ac)[ac->avail++] = objp;return;} else {STATS_INC_FREEMISS(cachep);cache_flusharray(cachep, ac);ac_entry(ac)[ac->avail++] = objp;}
}

1. 代码详细解释

static inline void __cache_free (kmem_cache_t *cachep, void* objp)
  • cachep: 对象所属的 slab 缓存
  • objp: 要释放的对象指针
  • 无返回值

1.1. 获取每CPU缓存

struct array_cache *ac = ac_data(cachep);

关键操作

  • ac_data(cachep): 获取当前CPU的本地缓存数组
  • 每个CPU有自己独立的对象缓存,避免锁竞争

1.2. 中断状态检查

check_irq_off();

验证:确保中断已被禁用

  • 调用者(kfree)应该已经调用了 local_irq_save
  • 保证在原子上下文中操作,防止并发问题

1.3. 调试检查

objp = cache_free_debugcheck(cachep, objp, __builtin_return_address(0));

调试功能

  • cache_free_debugcheck: 进行各种释放前的调试检查
  • __builtin_return_address(0): 获取调用者的返回地址,用于调试追踪
  • 可能返回调整后的对象指针(调试模式下)

1.4. 快速路径:每CPU缓存未满

if (likely(ac->avail < ac->limit)) {STATS_INC_FREEHIT(cachep);ac_entry(ac)[ac->avail++] = objp;return;
}

优化路径(最常见情况):

  • likely(ac->avail < ac->limit): 提示编译器每CPU缓存通常未满
  • STATS_INC_FREEHIT: 统计缓存命中次数
  • ac_entry(ac)[ac->avail++] = objp: 将对象添加到每CPU缓存数组
  • 直接返回,操作完成

1.5. 慢速路径:每CPU缓存已满

} else {STATS_INC_FREEMISS(cachep);cache_flusharray(cachep, ac);ac_entry(ac)[ac->avail++] = objp;
}

缓存刷新路径

  • STATS_INC_FREEMISS: 统计缓存未命中次数
  • cache_flusharray(cachep, ac): 将部分对象从每CPU缓存刷新到共享slab
  • 然后将被释放的对象添加到腾出空间的每CPU缓存

每CPU缓存刷新函数cache_flusharray

static void cache_flusharray (kmem_cache_t* cachep, struct array_cache *ac)
{int batchcount;batchcount = ac->batchcount;
#if DEBUGBUG_ON(!batchcount || batchcount > ac->avail);
#endifcheck_irq_off();spin_lock(&cachep->spinlock);if (cachep->lists.shared) {struct array_cache *shared_array = cachep->lists.shared;int max = shared_array->limit-shared_array->avail;if (max) {if (batchcount > max)batchcount = max;memcpy(&ac_entry(shared_array)[shared_array->avail],&ac_entry(ac)[0],sizeof(void*)*batchcount);shared_array->avail += batchcount;goto free_done;}}free_block(cachep, &ac_entry(ac)[0], batchcount);
free_done:
#if STATS{int i = 0;struct list_head *p;p = list3_data(cachep)->slabs_free.next;while (p != &(list3_data(cachep)->slabs_free)) {struct slab *slabp;slabp = list_entry(p, struct slab, list);BUG_ON(slabp->inuse);i++;p = p->next;}STATS_SET_FREEABLE(cachep, i);}
#endifspin_unlock(&cachep->spinlock);ac->avail -= batchcount;memmove(&ac_entry(ac)[0], &ac_entry(ac)[batchcount],sizeof(void*)*ac->avail);
}

1. 代码详细解释

static void cache_flusharray (kmem_cache_t* cachep, struct array_cache *ac)
  • cachep: slab 缓存描述符
  • ac: 要刷新的每CPU缓存

1.1. 批量大小设置

int batchcount;
batchcount = ac->batchcount;

批量确定

  • batchcount: 每次刷新操作处理的对象数量

1.2. 调试验证

#if DEBUGBUG_ON(!batchcount || batchcount > ac->avail);
#endif

调试检查

  • !batchcount: 批量大小不能为0
  • batchcount > ac->avail: 批量不能超过可用对象数
  • 在调试模式下触发错误如果条件不满足

1.3. 锁保护

check_irq_off();
spin_lock(&cachep->spinlock);

并发控制

  • check_irq_off(): 确认中断已禁用
  • spin_lock(&cachep->spinlock): 获取缓存锁,保护共享数据结构

1.4. 尝试共享缓存转移

if (cachep->lists.shared) {struct array_cache *shared_array = cachep->lists.shared;int max = shared_array->limit-shared_array->avail;if (max) {if (batchcount > max)batchcount = max;memcpy(&ac_entry(shared_array)[shared_array->avail],&ac_entry(ac)[0],sizeof(void*)*batchcount);shared_array->avail += batchcount;goto free_done;}
}

共享缓存优先

  • 检查是否存在共享每CPU缓存
  • 计算共享缓存剩余空间 max
  • 如果有空间,批量复制对象到共享缓存
  • 成功后跳转到清理步骤

1.5. 回退到slab释放

free_block(cachep, &ac_entry(ac)[0], batchcount);

直接释放:如果共享缓存不可用或已满,直接将对象释放回slab列表

1.6. 统计信息更新

#if STATS{int i = 0;struct list_head *p;p = list3_data(cachep)->slabs_free.next;while (p != &(list3_data(cachep)->slabs_free)) {struct slab *slabp;slabp = list_entry(p, struct slab, list);BUG_ON(slabp->inuse);i++;p = p->next;}STATS_SET_FREEABLE(cachep, i);}
#endif

统计功能

  • 统计完全空闲的slab数量
  • 遍历 slabs_free 链表计数
  • 验证空闲slab确实没有使用中的对象
  • 更新统计信息

1.7. 清理和压缩

spin_unlock(&cachep->spinlock);
ac->avail -= batchcount;
memmove(&ac_entry(ac)[0], &ac_entry(ac)[batchcount],sizeof(void*)*ac->avail);

最终清理

  • 释放缓存锁
  • 更新每CPU缓存的可用计数
  • 使用 memmove 压缩缓存数组,移除已刷新的对象

安全内存移动函数memmove

void * memmove(void * dest,const void *src,size_t count)
{char *tmp, *s;if (dest <= src) {tmp = (char *) dest;s = (char *) src;while (count--)*tmp++ = *s++;}else {tmp = (char *) dest + count;s = (char *) src + count;while (count--)*--tmp = *--s;}return dest;
}

1. 代码详细解释

void * memmove(void * dest, const void *src, size_t count)
  • dest: 目标内存地址
  • src: 源内存地址
  • count: 要移动的字节数
  • 返回: 目标内存地址 dest

1.1. 指针变量声明

char *tmp, *s;
  • tmp: 用于遍历目标内存的指针
  • s: 用于遍历源内存的指针
  • 使用 char* 类型以便按字节操作

1.2. 重叠情况判断

if (dest <= src) {

关键判断:检查目标地址是否在源地址之前或相同

  • 这决定了复制方向,防止数据被覆盖

1.3. 正向复制(目标在源之前)

tmp = (char *) dest;
s = (char *) src;
while (count--)*tmp++ = *s++;

从前往后复制

  • 初始化指针到各自内存的起始位置
  • 循环 count 次,每次复制一个字节
  • 指针递增,从低地址向高地址复制

1.4. 反向复制(目标在源之后)

} else {tmp = (char *) dest + count;s = (char *) src + count;while (count--)*--tmp = *--s;}

从后往前复制

  • 初始化指针到各自内存的末尾位置(地址 + count)
  • 循环 count 次,每次复制一个字节
  • 指针递减,从高地址向低地址复制

1.5. 返回结果

return dest;

返回目标地址,支持链式调用

批量对象释放到slab的函数free_block

static void free_block(kmem_cache_t *cachep, void **objpp, int nr_objects)
{int i;check_spinlock_acquired(cachep);/* NUMA: move add into loop */cachep->lists.free_objects += nr_objects;for (i = 0; i < nr_objects; i++) {void *objp = objpp[i];struct slab *slabp;unsigned int objnr;slabp = GET_PAGE_SLAB(virt_to_page(objp));list_del(&slabp->list);objnr = (objp - slabp->s_mem) / cachep->objsize;check_slabp(cachep, slabp);
#if DEBUGif (slab_bufctl(slabp)[objnr] != BUFCTL_FREE) {printk(KERN_ERR "slab: double free detected in cache '%s', objp %p.\n",cachep->name, objp);BUG();}
#endifslab_bufctl(slabp)[objnr] = slabp->free;slabp->free = objnr;STATS_DEC_ACTIVE(cachep);slabp->inuse--;check_slabp(cachep, slabp);/* fixup slab chains */if (slabp->inuse == 0) {if (cachep->lists.free_objects > cachep->free_limit) {cachep->lists.free_objects -= cachep->num;slab_destroy(cachep, slabp);} else {list_add(&slabp->list,&list3_data_ptr(cachep, objp)->slabs_free);}} else {/* Unconditionally move a slab to the end of the* partial list on free - maximum time for the* other objects to be freed, too.*/list_add_tail(&slabp->list,&list3_data_ptr(cachep, objp)->slabs_partial);}}
}

1. 代码详细解释

static void free_block(kmem_cache_t *cachep, void **objpp, int nr_objects)
  • cachep: slab缓存描述符
  • objpp: 对象指针数组
  • nr_objects: 要释放的对象数量

1.1. 锁验证和统计更新

int i;
check_spinlock_acquired(cachep);
cachep->lists.free_objects += nr_objects;
  • 验证已持有缓存锁
  • 更新全局空闲对象统计

1.2. 遍历释放每个对象

for (i = 0; i < nr_objects; i++) {void *objp = objpp[i];struct slab *slabp;unsigned int objnr;

循环处理每个要释放的对象

1.3. 查找对象所属的slab

slabp = GET_PAGE_SLAB(virt_to_page(objp));
list_del(&slabp->list);
  • 通过对象地址找到对应的slab结构
  • 将slab从当前列表中移除(后续根据状态重新添加)

1.4. 计算对象索引

objnr = (objp - slabp->s_mem) / cachep->objsize;

对象索引计算

  • objp - slabp->s_mem: 对象在slab内的偏移量
  • 除以对象大小得到对象索引

1.5. 调试检查

check_slabp(cachep, slabp);
#if DEBUG
if (slab_bufctl(slabp)[objnr] != BUFCTL_FREE) {printk(KERN_ERR "slab: double free detected in cache '%s', objp %p.\n",cachep->name, objp);BUG();
}
#endif
  • 检查slab完整性
  • 调试模式下检测双重释放

1.6. 更新空闲链表

slab_bufctl(slabp)[objnr] = slabp->free;
slabp->free = objnr;

空闲链表操作

  • 将释放的对象添加到空闲链表头部
  • 更新slab的空闲指针

1.7. 更新统计信息

STATS_DEC_ACTIVE(cachep);
slabp->inuse--;
  • 减少活跃对象计数
  • 减少slab内使用中对象计数

1.8. slab状态管理和重新分类

if (slabp->inuse == 0) {if (cachep->lists.free_objects > cachep->free_limit) {cachep->lists.free_objects -= cachep->num;slab_destroy(cachep, slabp);} else {list_add(&slabp->list,&list3_data_ptr(cachep, objp)->slabs_free);}
} else {list_add_tail(&slabp->list,&list3_data_ptr(cachep, objp)->slabs_partial);
}

根据slab状态决定:

  • 完全空闲且超过限制:销毁slab
  • 完全空闲:添加到空闲列表
  • 部分使用:添加到部分使用列表尾部
http://www.dtcms.com/a/470675.html

相关文章:

  • 北京网站开发飞沐如何做网站的教程二维码
  • 6自由度模拟地震振动台试验系统
  • 东莞官方网站温州seo收费
  • 千亿级赛道,Robobus 赛道中标新加坡自动驾驶巴士项目的“确定性机会”
  • 滴滴自动驾驶张博:坚持负责任的科技创新,积极探索新型就业空间
  • 建设公司怎么做网站运营模拟建筑2022手机版
  • 网站设计价格网站建设与管理专业
  • (六) Dotnet在AI控制台案例启用遥测数据与工具函数调用
  • 生产线操作工行为识别方案
  • Windows下快速安装Composer教程
  • 游戏交易网站开发莱芜网红
  • 静态网站怎么更新哪些网站是用织梦做的
  • (项目管理系列课程)项目规划阶段:项目范围管理-创建WBS
  • app的制作流程图苏州优化件
  • 图生3D技术解析:从二维平面到立体世界的智能飞跃
  • 东莞黄江建设银行网站做wordpress总结
  • 网站 pinghei做爰全过程免费狐狸网站
  • 网站常用的优化方法有哪些网页设计模板html代码ie
  • 国内如何升级GitHub Copilot到专业版
  • 小说网站开发流程具体火车头wordpress免登录发布
  • 济南中建设计院 官方网站米课wordpress建站
  • 从指令遵循到价值对齐:医疗大语言模型的进阶优化、对齐与工具集成综合技术白皮书
  • 个人商城网站备案医院网站源码php
  • 基于螳螂虾优化的LSTM深度学习网络模型(MShOA-LSTM)的一维时间序列预测算法matlab仿真
  • 合肥肥东网站建设河南河南省住房和城乡建设厅网站
  • 网站经营网络备案信息wordpress更换域名2017
  • 鸿蒙纯血ArkTS经典蓝牙(SPP)
  • php项目网站建设方案书佛山百度快速排名优化
  • 七彩喜艾灸机器人:当千年中医智慧遇上现代科技
  • 接待机器人与访客系统对接技术解析