kref 与引用计数
这页讲的是:内核里很多对象并不是“谁先用完谁就随手 free”,因为同一对象可能被很多路径同时持有。学这页的重点,是理解引用计数不是小技巧,而是生命周期安全的核心规则之一。
这块是什么
kref 与引用计数,讲的是当一个对象会被多个子系统、回调、异步工作或用户入口同时指向时,内核如何通过“还有多少人正在用它”来决定它何时仍然有效、何时才真正可以释放。它把对象生命从“看上去好像没人用了”变成“系统明确知道现在能不能收”。
可以把它理解成:一把公共会议室钥匙被多人借用,只要还有人没还,就不能把锁芯拆掉。真正能收走,得等最后一个借用者也放手。
它负责什么
保护对象生命周期
- 让对象在仍被使用时继续存活
- 避免“有人还在用,东西已经被释放”
- 让释放时机更明确
- 降低悬空指针和 use-after-free 风险
衔接多条执行路径
- 同一对象可能被同步和异步路径同时持有
- 不同模块可能共享同一对象引用
- 引用计数让这些关系能被统一表达
- 避免只靠口头约定猜谁还在用
支撑安全释放
- 最后一个引用放下时才触发真正清理
- 把释放与仍在使用之间切出明确边界
- 帮助模块退出、设备移除和后台清理更安全
- 让生命周期从感觉问题变成可度量关系
为什么不能只靠“我觉得这里应该没人用了”
| 如果只靠感觉 | 会怎样 | 引用计数的价值 |
|---|
| 对象可能被多个地方共享 | 某条路径提前释放,别的路径就会踩空。 | 明确记录还有多少活跃持有者。 |
| 异步工作还没结束 | 看起来主流程已经走完,后台却还会回来访问。 | 把“后台还在用”也计入对象存活条件。 |
| 模块和驱动正处于退出中 | 退出逻辑容易和仍在运行的路径打架。 | 让最后释放时机更晚、更安全。 |
| 回调关系复杂 | 对象到底归谁释放会变得模糊。 | 用统一规则替代含糊约定。 |
关键概念
| 概念 | 现在怎么理解 |
|---|
| 引用计数 | 记录某个对象当前仍被多少活跃路径持有。 |
| kref | 内核里常见的引用计数辅助机制,可先理解成管理对象存活的一把统一尺子。 |
| 获取引用 | 表示“我现在也要使用这个对象,你先别释放它”。 |
| 放下引用 | 表示“我用完了,我这一份持有关系可以去掉”。 |
| 最终释放 | 只有最后一个引用离开时,真正清理对象才安全。 |
为什么重要
- 它是理解很多对象模型、驱动生命周期和模块退出安全性的关键。
- 很多难查的 bug,根子都在“对象还被用着,却已经被释放”或“没人用了,却永远不释放”。
- 理解这层后,你会更容易把 workqueue、remove、资源清理和对象关系串起来。
- 它让“谁拥有对象”和“对象什么时候该死”从模糊感觉变成系统规则。
常见误解
- 误解一:引用计数只是内存管理小技巧。实际上它直接决定对象生命周期安全。
- 误解二:只要主路径结束了,对象就该释放。实际上异步和共享路径常常还在用。
- 误解三:引用计数解决了所有并发问题。实际上它只解决存活关系,不替代锁和同步。
它不负责什么
- 它不替代互斥和锁;对象活着不等于对象状态就能被随便并发修改。
- 它不定义对象业务语义,只负责“还该不该活着”。
- 它不自动清理所有资源设计错误,引用关系本身仍要设计清楚。
和其他模块的关系
| 相关模块 | 关系 |
|---|
| Core API | 引用计数本身就是公共底座里最重要的生命周期工具之一。 |
| 模块加载与生命周期 | 模块退出前常要确认对象引用都已释放,否则退出会不安全。 |
| probe、remove 与资源清理 | 设备撤场时尤其容易暴露“对象还在被谁引用”的问题。 |
| workqueue / 异步执行 | 后台路径最容易让对象看似该死、其实还活着。 |
读完这页后,你应该能回答
- 为什么引用计数在内核里不是可有可无的细节?
- 为什么“对象还活着”和“对象现在能不能并发修改”是两件事?
- 为什么模块退出、驱动 remove 和异步回调常常会把引用计数问题暴露出来?
后面适合继续问:kref 和 kobject 的关系怎么理解?为什么引用计数容易和异步回调缠在一起?什么情况下会出现泄漏而不是过早释放?