Linux内核里的“智能指针” (续)
2010-12-03 22:13 wwang 阅读(5041) 评论(4) 编辑 收藏 举报在上一篇文章《Linux内核里的智能指针》里介绍了Linux内核如何使用引用计数来更加安全的管理内存,本文承接前篇,主要介绍几点使用kref时的注意事项。
Linux内核文档kref.txt罗列了三条规则,我们在使用kref时必须遵守。
规则一:
If you make a non-temporary copy of a pointer, especially if it can be passed to another thread of execution, you must increment the refcount with kref_get() before passing it off;
规则二:
When you are done with a pointer, you must call kref_put();
规则三:
If the code attempts to gain a reference to a kref-ed structure without already holding a valid pointer, it must serialize access where a kref_put() cannot occur during the kref_get(), and the structure must remain valid during the kref_get().
对于规则一,其实主要是针对多条执行路径(比如另起一个线程)的情况。如果是在单一的执行路径里,比如把指针传递给一个函数,是不需要使用kref_get的。看下面这个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 | kref_init(&obj->ref); // do something here // ... kref_get(&obj->ref); call_something(obj); kref_put(&obj->ref); // do something here // ... kref_put(&obj->ref); |
您是不是觉得call_something前后的一对kref_get和kref_put很多余呢?obj并没有逃出我们的掌控,所以它们确实是没有必要的。
但是当遇到多条执行路径的情况就完全不一样了,我们必须遵守规则一。下面是摘自内核文档里的一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | struct my_data { . . struct kref refcount; . . }; void data_release( struct kref *ref) { struct my_data *data = container_of(ref, struct my_data, refcount); kfree(data); } void more_data_handling( void *cb_data) { struct my_data *data = cb_data; . . do stuff with data here . kref_put(&data->refcount, data_release); } int my_data_handler( void ) { int rv = 0; struct my_data *data; struct task_struct *task; data = kmalloc( sizeof (*data), GFP_KERNEL); if (!data) return -ENOMEM; kref_init(&data->refcount); kref_get(&data->refcount); task = kthread_run(more_data_handling, data, "more_data_handling" ); if (task == ERR_PTR(-ENOMEM)) { rv = -ENOMEM; goto out; } . . do stuff with data here . out: kref_put(&data->refcount, data_release); return rv; } |
因为我们并不知道线程more_data_handling何时结束,所以要用kref_get来保护我们的数据。
注意规则一里的那个单词“before",kref_get必须是在传递指针之前进行,在本例里就是在调用kthread_run之前就要执行kref_get,否则,何谈保护呢?
对于规则二我们就不必多说了,前面调用了kref_get,自然要配对使用kref_put。
规则三主要是处理遇到链表的情况。我们假设一个情景,如果有一个链表摆在你的面前,链表里的节点是用引用计数保护的,那你如何操作呢?首先我们需要获得节点的指针,然后才可能调用kref_get来增加该节点的引用计数。根据规则三,这种情况下我们要对上述的两个动作串行化处理,一般我们可以用mutex来实现。请看下面这个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | static DEFINE_MUTEX(mutex); static LIST_HEAD(q); struct my_data { struct kref refcount; struct list_head link; }; static struct my_data *get_entry() { struct my_data *entry = NULL; mutex_lock(&mutex); if (!list_empty(&q)) { entry = container_of(q.next, struct my_q_entry, link); kref_get(&entry->refcount); } mutex_unlock(&mutex); return entry; } static void release_entry( struct kref *ref) { struct my_data *entry = container_of(ref, struct my_data, refcount); list_del(&entry->link); kfree(entry); } static void put_entry( struct my_data *entry) { mutex_lock(&mutex); kref_put(&entry->refcount, release_entry); mutex_unlock(&mutex); } |
这个例子里已经用mutex来进行保护了,假如我们把mutex拿掉,会出现什么情况?记住,我们遇到的很可能是多线程操作。如果线程A在用container_of取得entry指针之后、调用kref_get之前,被线程B抢先执行,而线程B碰巧又做的是kref_put的操作,当线程A恢复执行时一定会出现内存访问的错误,所以,遇到这种情况一定要串行化处理。
我们在使用kref的时候要严格遵循这三条规则,才能安全有效的管理数据。
作者:wwang
出处:http://www.cnblogs.com/wwang
本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
--------懒人评论(请勿重复点击)--------
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· SQL Server 2025 AI相关能力初探
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库