各位有没有发现这两个 function 很类似。是的,它们唯一的差别就在于
. Y/ Y/ J' Q2 o* H # F7 e4 U) s F& N# Y" b" c! v! E
current->state = …( y3 u( J# u% i/ J4 a
) d. j% E: w- m
这一行而已。之前,我们有说过,interruptible_sleep_on() 可以被 signal 中断,所以,其 current->state 被设为 TASK_INTERRUPTIBLE.而 sleep_on() 没办法被中断,所以 current->state 设为 TASK_UNINTERRUPTIBLE.接下来,我们只看 interruptible_sleep_on() 就好了。毕竟它们两的差异只在那一行而已。
! K% j6 z& i4 C4 P1 N, Z3 b
6 k+ ? ?: d6 C" Y7 }0 Y0 ?; e 在 sched.c 里,SLEEP_ON_VAR 是一个 macro,其实它只是定义两个区域变量出来而已。
& L( K# |# k% j8 b, P( X ? ( O% \. H* E9 ?. _- ~, `& x' {/ Z
#defineSLEEP_ON_VAR
$ v# P/ E/ y C
6 S7 n9 x( I! C3 F unsigned long flags;! K/ f+ A1 K; ]- j
Q- z; ~9 I" Z4 w" v$ q$ k7 T$ T' ` struct wait_queue wait;
6 B% o' s8 L! Y: P2 @
) P. A- ^6 A a2 c 刚才我也说过,current 这个变量是指到目前正在执行的 process 的 task_struct 结构。所以 current->state = TASK_INTERRUPTIBLE 会设定在呼叫 interruptible_sleep_on() 的 process 身上。至于 SLEEP_ON_HEAD 做的事,则是将 current 的值放到 SLEEP_ON_VAR 宣告的 wait 变量里,并把 wait 放到 interruptible_sleep_on() 的参数所属的 wait_queue list 中。/ Y8 U, M( w! P/ k3 }/ ]
- a* W* M1 V8 }
#defineSLEEP_ON_HEAD
7 d. o+ x( F( u- W+ u8 A4 U * b7 j4 o; ~+ z3 |* i
wait.task = current;
- j# ~" y- E1 i8 t+ V) c
9 {# R* ~6 \7 Y8 D$ @ write_lock_irqsave(&waitqueue_lock,flags);
: H: Y7 G% w" A$ i2 ~( q
7 h8 N% {# |1 L+ j __add_wait_queue(p,&wait);- Z& g% [3 d7 y, F
/ F( v- V9 }$ O( |) @+ P' V write_unlock(&waitqueue_lock);1 ]( `4 b5 @3 s. \3 @
4 G. i( H$ |8 N2 F- ?9 z) r wait 是在 SLEEP_ON_VAR 中宣告的区域变量。其 task 字段被设成呼叫 interruptible_sleep_on() 的 process.至于 waitqueue_lock 这个变量是一个 spin lock. waitqueue_lock 是用来确保同一时间只能有一个 writer.但同一时间则可以有好几个 reader.也就是说 waitqueue_lock 是用来保证 critical section 的 mutual exclusive access.) u5 c/ Y) y8 z+ Y$ P8 j! \! A
; y7 w$ Q) k+ L9 s" W$ u
unsigned long flags;0 N3 [2 ]( y( X3 k" N7 ^
' X' L' _( |9 x% H, r) U write_lock_irqsave(&waitqueue_lock,flags);
$ G& G( I. m; O% w$ B, Y- ^( U$ r( g9 e9 o 4 i' s7 D! g( P, {5 o) b: o0 A3 a
…critical section …
8 D$ b! P2 y, A% J# g ) e1 U. m h" E% G; e3 X% k
write_unlock(&waitqueue_lock)' t! ]# ]* }( G3 l
0 l' [7 `6 E( {: {3 \; b M
学过 OS 的人应该知道 critical section 的作用是什么,如有需要,请自行参考 OS 参考书。在 critical section 里只做一件事,就是将 wait 这个区域变量放到 p 这个 wait_queue list 中。 p 是 user 在呼叫 interruptible_sleep_on() 时传进来的,它的型别是 struct wait_queue **.在此, critical section 只呼叫 __add_wait_queue()。
+ y" @6 j, i& {. i " N$ l E. L% j6 C* o" P0 d( J
extern inline void __add_wait_queue(struct wait_queue ** p,
+ \/ S4 C' w" M- y; B3 I' n1 }! f2 i * y, e/ x! S. N& k4 C2 J4 ]
struct wait_queue * wait)
' H( N! h( z" e; \8 r* s a2 v3 f5 x$ }" r; o4 o
{/ S0 b2 d D& [; C4 E
5 c1 M- ^: G/ `
wait->next = *p ? : WAIT_QUEUE_HEAD(p);3 N! M6 M% @; Q" R; _ l+ B
: `+ U( ]* f' Q1 Y4 `8 y7 F
*p = wait;
% |$ _/ u0 O7 N/ U8 \ # X3 d6 {7 |8 a) I; A
}
5 y9 w7 ^# K$ J( N* K7 p 8 {8 b( [7 q, R1 r2 X/ X
__add_wait_queue() 是一个inline function,定义在 中。WAIT_QUEUE_HEAD()是个很有趣的 macro,待会我们再讨论。现在只要知道它会传回这个 wait_queue 的开头就可以了。所以,__add_wait_queue() 的意思就是要把 wait 放到 p 所属的 wait_queue list 的开头。但是,大家还记得吗? 在上面的例子里,一开始我们是把 write_wq 设为 NULL.也就是说 *p 是 NULL.所以,当 *p 是 NULL 时,6 [- b- T% h3 O0 G
9 ]: v- ^) t9 b. l d wait->next = WAIT_QUEUE_HEAD(p) p' i3 ^; A3 V
- w4 U6 _' V0 T 是什么意思呢?
7 \5 `1 E. }* n# f4 h/ A% r ) Z9 c6 W/ R3 C3 x
所以,现在,我们来看一下 WAIT_QUEUE_HEAD() 是怎么样的一个 macro,它是定义在 里。' D) l5 h: j& X* H
7 O% H- d* f; n2 Y( U, s. [
#define WAIT_QUEUE_HEAD(x) ((struct wait_queue *)((x)-1))
( Q4 p2 j7 ^; d' }5 r ) y2 I# v# V' C
x 型别是 struct wait_queue **,因为是一个 pointer,所以大小是 4 byte.因此,若 x 为 100 的话,那 ((x)-1) 就变成 96.如下图所示。 WAIT_QUEUE_HEAD(x) 其实会传回 96,而且将其转型为 struct wait_queue*,各位可以看看。原本的 wait_queue* 只配制在 100-104 之间。现在 WAIT_QUEUE_HEAD(x) 却直接传回96,但是 96-100 这块位置根本没有被我们配置起来。更妙的事。由于 x 是一个 wait_queue list 的开头,我们始终不会用到 96-100 这块,我们只会直接使用到 100-104 这块内存。这也算是 wait_queue 一项比较奇怪的 implementation 方式吧。下面有三张图,第一张表示我们宣告了一个 wait_queue* 的变量,地址在 100.另外还有一个 wait_queue 的变量,名叫 wait.第二张图是我们呼叫 interruptible_sleep_on() 之后得到的结果。第三张则是我们又宣告一个 wait_queue,名叫 ano_wait,将 ano_wait 放到 wait_queue list 后的结果就第三张图所显示的。 |