EDA365电子论坛网

标题: Linux内核里的“智能指针” (续) [打印本页]

作者: pulbieup    时间: 2021-4-27 18:12
标题: Linux内核里的“智能指针” (续)
, x# o$ ?$ I" a, N" g! c( l
在上一篇文章:Linux内核里的“智能指针”里介绍了Linux内核如何使用引用计数来更加安全的管理内存,本文承接前篇,主要介绍几点使用kref时的注意事项。
5 o, e+ i& b8 [3 v8 u! m
1 Q  Z6 z/ o7 i% ULinux内核文档kref.txt罗列了三条规则,我们在使用kref时必须遵守。
4 V# k- f, k/ o8 z6 L0 ]: {& q- [0 B( ]
规则一:' K9 i/ U! A- r* q# i/ G* E

+ E6 Q. U3 z: kIf 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;
; K, M. h; k. f, E/ t, D4 A" m
规则二:
0 ]( V, B4 f0 L5 n4 Q6 `& s  O: D/ `; k2 p# B3 ?2 g. |8 V3 I' ]
When you are done with a pointer, you must call kref_put();# [! }  c( c% I7 c9 K, A( ^; b2 u

  B4 U* e" S  S& p, v4 s- A) }规则三:
$ |: N& j' z% S
: b* u/ p$ X! {$ }2 V0 D3 X0 j6 ?- A8 TIf 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().2 f* k/ @" e5 x5 b8 d  m" S

. y% v+ K0 K; R( O 6 F, E0 s& x& K& U" S( G. D

8 t2 ^# G5 ]1 J* C5 h3 J* P* o# S对于规则一,其实主要是针对多条执行路径(比如另起一个线程)的情况。如果是在单一的执行路径里,比如把指针传递给一个函数,是不需要使用kref_get的。看下面这个例子:7 z* p" H  A) U6 D9 N4 M
% ^7 R$ \9 U* C: T: ?+ y# ?  D

4 \* e7 @* N  o2 q# l, ?4 q/ @7 k. |' o) M' C+ c! V4 r7 }9 S; c3 b
    您是不是觉得call_something前后的一对kref_get和kref_put很多余呢?obj并没有逃出我们的掌控,所以它们确实是没有必要的。. y( ?/ C/ e* x" h$ f9 V

: W# s' Z3 t+ }; {4 M4 L但是当遇到多条执行路径的情况就完全不一样了,我们必须遵守规则一。下面是摘自内核文档里的一个例子:
! s& Q$ n) o5 H; Y2 {  |7 H5 i
1 _" ~# Y1 H1 N4 s( `
. P, Z( f. _; b( N6 r6 a+ V- ~$ b: ~4 Z- I
     因为我们并不知道线程more_data_handling何时结束,所以要用kref_get来保护我们的数据。, d' a  ~6 h6 _6 S% t

+ V" v* P) Z5 w5 j& Q2 ^, I注意规则一里的那个单词“before",kref_get必须是在传递指针之前进行,在本例里就是在调用kthread_run之前就要执行kref_get,否则,何谈保护呢?
( q% E! ?$ c5 ?+ x0 R& D  z6 Z
2 ^" S+ X. U( ?9 q' s$ W, z. F
( a# @) h; p- U0 y5 b# z" o" i
/ w6 ~3 f( F! k- Y6 [对于规则二我们就不必多说了,前面调用了kref_get,自然要配对使用kref_put。+ a  R0 T# f- g  k# v, d

  m* T. F8 }/ h! W' J   v: i7 D$ ~$ b# c
3 ?  B$ A6 |- n9 X" ]
规则三主要是处理遇到链表的情况。我们假设一个情景,如果有一个链表摆在你的面前,链表里的节点是用引用计数保护的,那你如何操作呢?首先我们需要获得节点的指针,然后才可能调用kref_get来增加该节点的引用计数。根据规则三,这种情况下我们要对上述的两个动作串行化处理,一般我们可以用mutex来实现。请看下面这个例子:
' d- {; b% R2 g8 D& K. `" D6 _% _8 \) Z

3 A- N: @3 N& R0 S
* I8 a' p, G6 {3 N* a. z1 M    这个例子里已经用mutex来进行保护了,假如我们把mutex拿掉,会出现什么情况?记住,我们遇到的很可能是多线程操作。如果线程A在用container_of取得entry指针之后、调用kref_get之前,被线程B抢先执行,而线程B碰巧又做的是kref_put的操作,当线程A恢复执行时一定会出现内存访问的错误,所以,遇到这种情况一定要串行化处理。0 y$ B% x1 D) d& n

2 h$ v2 L& S# }% j' |: K
; V+ W+ K& U; L: m+ X; j; f+ ^- T1 r, k
我们在使用kref的时候要严格遵循这三条规则,才能安全有效的管理数据。
作者: ExxNEN    时间: 2021-4-27 18:45
Linux内核里的“智能指针” (续)
作者: xiaogegepcb    时间: 2021-4-28 18:32
Linux内核里的“智能指针” (续)




欢迎光临 EDA365电子论坛网 (https://bbs.eda365.com/) Powered by Discuz! X3.2