Linux内核里的“智能指针” (续)
2011-12-01 19:02
387 查看
在上一篇文章《Linux内核里的智能指针》里介绍了Linux内核如何使用引用计数来更加安全的管理内存,本文承接前篇,主要介绍几点使用kref时的注意事项。
Linux内核文档kref.txt罗列了三条规则,我们在使用kref时必须遵守。
规则一:
Ifyoumakeanon-temporarycopyofapointer,especiallyifitcanbepassedtoanotherthreadofexecution,youmustincrementtherefcountwithkref_get()beforepassingitoff;
规则二:
Whenyouaredonewithapointer,youmustcallkref_put();
规则三:
Ifthecodeattemptstogainareferencetoakref-edstructurewithoutalreadyholdingavalidpointer,itmustserializeaccesswhereakref_put()cannotoccurduringthekref_get(),andthestructuremustremainvalidduringthekref_get().
对于规则一,其实主要是针对多条执行路径(比如另起一个线程)的情况。如果是在单一的执行路径里,比如把指针传递给一个函数,是不需要使用kref_get的。看下面这个例子:
viewsourceprint?
您是不是觉得call_something前后的一对kref_get和kref_put很多余呢?obj并没有逃出我们的掌控,所以它们确实是没有必要的。
但是当遇到多条执行路径的情况就完全不一样了,我们必须遵守规则一。下面是摘自内核文档里的一个例子:
viewsourceprint?
因为我们并不知道线程more_data_handling何时结束,所以要用kref_get来保护我们的数据。
注意规则一里的那个单词“before",kref_get必须是在传递指针之前进行,在本例里就是在调用kthread_run之前就要执行kref_get,否则,何谈保护呢?
对于规则二我们就不必多说了,前面调用了kref_get,自然要配对使用kref_put。
规则三主要是处理遇到链表的情况。我们假设一个情景,如果有一个链表摆在你的面前,链表里的节点是用引用计数保护的,那你如何操作呢?首先我们需要获得节点的指针,然后才可能调用kref_get来增加该节点的引用计数。根据规则三,这种情况下我们要对上述的两个动作串行化处理,一般我们可以用mutex来实现。请看下面这个例子:
viewsourceprint?
这个例子里已经用mutex来进行保护了,假如我们把mutex拿掉,会出现什么情况?记住,我们遇到的很可能是多线程操作。如果线程A在用container_of取得entry指针之后、调用kref_get之前,被线程B抢先执行,而线程B碰巧又做的是kref_put的操作,当线程A恢复执行时一定会出现内存访问的错误,所以,遇到这种情况一定要串行化处理。
我们在使用kref的时候要严格遵循这三条规则,才能安全有效的管理数据。
Linux内核文档
规则一:
Ifyoumakeanon-temporarycopyofapointer,especiallyifitcanbepassedtoanotherthreadofexecution,youmustincrementtherefcountwithkref_get()beforepassingitoff;
规则二:
Whenyouaredonewithapointer,youmustcallkref_put();
规则三:
Ifthecodeattemptstogainareferencetoakref-edstructurewithoutalreadyholdingavalidpointer,itmustserializeaccesswhereakref_put()cannotoccurduringthekref_get(),andthestructuremustremainvalidduringthekref_get().
对于规则一,其实主要是针对多条执行路径(比如另起一个线程)的情况。如果是在单一的执行路径里,比如把指针传递给一个函数,是不需要使用kref_get的。看下面这个例子:
01 | kref_init(&obj->ref); |
02 |
03 | //dosomethinghere |
04 | //... |
05 |
06 | kref_get(&obj->ref); |
07 | call_something(obj); |
08 | kref_put(&obj->ref); |
09 |
10 | //dosomethinghere |
11 | //... |
12 |
13 | kref_put(&obj->ref); |
但是当遇到多条执行路径的情况就完全不一样了,我们必须遵守规则一。下面是摘自内核文档里的一个例子:
01 | struct
|
02 | { |
03 | . |
04 | . |
05 | struct krefrefcount; |
06 | . |
07 | . |
08 | }; |
09 |
10 | void data_release( struct
|
11 | { |
12 | struct my_data*data=container_of(ref, struct my_data,refcount); |
13 | kfree(data); |
14 | } |
15 |
16 | void more_data_handling( void
|
17 | { |
18 | struct my_data*data=cb_data; |
19 | . |
20 | . do stuffwithdatahere |
21 | . |
22 | kref_put(&data->refcount,data_release); |
23 | } |
24 |
25 | int my_data_handler( void ) |
26 | { |
27 | int rv=0; |
28 | struct my_data*data; |
29 | struct task_struct*task; |
30 | data=kmalloc( sizeof (*data),GFP_KERNEL); |
31 | if (!data) |
32 | return -ENOMEM; |
33 | kref_init(&data->refcount); |
34 |
35 | kref_get(&data->refcount); |
36 | task=kthread_run(more_data_handling,data, "more_data_handling" ); |
37 | if (task==ERR_PTR(-ENOMEM)){ |
38 | rv=-ENOMEM; |
39 | goto out; |
40 | } |
41 |
42 | . |
43 | . do stuffwithdatahere |
44 | . |
45 | out: |
46 | kref_put(&data->refcount,data_release); |
47 | return rv; |
48 | } |
注意规则一里的那个单词“before",kref_get必须是在传递指针之前进行,在本例里就是在调用kthread_run之前就要执行kref_get,否则,何谈保护呢?
对于规则二我们就不必多说了,前面调用了kref_get,自然要配对使用kref_put。
规则三主要是处理遇到链表的情况。我们假设一个情景,如果有一个链表摆在你的面前,链表里的节点是用引用计数保护的,那你如何操作呢?首先我们需要获得节点的指针,然后才可能调用kref_get来增加该节点的引用计数。根据规则三,这种情况下我们要对上述的两个动作串行化处理,一般我们可以用mutex来实现。请看下面这个例子:
01 | static
|
02 | static
|
03 | struct
|
04 | { |
05 | struct krefrefcount; |
06 | struct list_headlink; |
07 | }; |
08 |
09 | static struct my_data*get_entry() |
10 | { |
11 | struct my_data*entry=NULL; |
12 | mutex_lock(&mutex); |
13 | if (!list_empty(&q)){ |
14 | entry=container_of(q.next, struct my_q_entry,link); |
15 | kref_get(&entry->refcount); |
16 | } |
17 | mutex_unlock(&mutex); |
18 | return entry; |
19 | } |
20 |
21 | static void release_entry( struct kref*ref) |
22 | { |
23 | struct my_data*entry=container_of(ref, struct my_data,refcount); |
24 |
25 | list_del(&entry->link); |
26 | kfree(entry); |
27 | } |
28 |
29 | static void put_entry( struct my_data*entry) |
30 | { |
31 | mutex_lock(&mutex); |
32 | kref_put(&entry->refcount,release_entry); |
33 | mutex_unlock(&mutex); |
34 | } |
我们在使用kref的时候要严格遵循这三条规则,才能安全有效的管理数据。
相关文章推荐
- Linux内核里的“智能指针”
- Linux内核里的“智能指针”
- Linux内核里的智能指针
- Linux内核里的“智能指针”
- Linux内核里的“智能指针”
- C++ 引用 指针 智能指针 拷贝构造
- webkit内存管理1:智能指针
- 动态内存与智能指针
- C++Primer学习:智能指针与动态内存(2)
- 智能指针的标准之争:Boost vs. Loki
- 智能指针CComPtr 和 CComQIPtr
- 智能指针
- Android 智能指针详解 -- wp
- C++11-智能指针-shared_ptr
- 实现智能指针
- Boost 智能指针
- 使用智能指针要注意的若干事项
- C++中智能指针的设计和使用
- 智能指针(auto_ptr 和 shared_ptr)
- c++的智能指针