a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 91|回复: 1

[综合辅导] Linux系统管理:内核等待队列机制介绍3

[复制链接]
发表于 2012-8-4 12:07:07 | 显示全部楼层 |阅读模式
wait_queue 是定义在 里,我们可以先看看它的数据结构是怎么样:# ]# B0 L. F8 |, s
    3 `' H  X6 Z5 ?& R* R: W
    struct wait_queue {
8 l6 H9 x- C4 w# e5 k9 f# t   
7 ~5 A3 j: U' i1 H    struct task_struct * task;2 j! }2 \7 ?; [0 m1 V+ f8 l
    2 S" G! d6 |1 q% r4 t, m
    struct wait_queue * next;
* m5 P3 W) q: d( c* r! Z0 {. g/ I   
' G) b5 _9 v& Q6 L) W) a& y0 ~    };
9 x) ?5 ]+ w5 @2 B4 T   
8 M% p! ?6 @  G$ N# B" R* F* m+ ~0 `    很简单是吧。这个结构里面只有二个字段,一个是 task_struct 的 pointer,另一个则是 wait_queue 的 pointer.很明显的,我们可以看出 wait_queue 其实就是一个 linked list,而且它还是一个 circular linked list. 其中 task_struct 就是用来指呼叫 sleep_on 等 function的 process.在 Linux 里,每一个 process 是由一个 task_struct 来描叙。task_struct 是一个很大的的结构,在此我们不会讨论。Linux 里有一个 global variable,叫 current,它会指到目前正在执行的 process 的 task_struct 结构。这也就是为什么当 process 呼叫 system call,切换到 kernel 时,kernel 会知道是那个 process 呼叫的。
; P& ]" Q2 q' q* F! ^, ]  i    ' b. u/ w2 h+ w& `! ]! q6 Y9 D% e
    好,我们现在来看看 interruptible_sleep_on() 和 sleep_on() 是如何做的。这两个 function 都是位于 /usr/src/linux/kernel/sched.c 里。5 k1 w6 k# j( k
   
) K1 b3 Z6 c: L  U4 b! ~  K: j& _    void interruptible_sleep_on(struct wait_queue **p)3 r& ~; p: ?) s
   
$ S6 N& {7 ^# o! e    {
# n$ L: c7 B6 e* b! N4 z    ( C# ^" h) ^* t% a! ^! i" E, @% L
    SLEEP_ON_VAR
, G8 k' Z* G7 s. o, }# C5 l" N1 E   
$ n% O1 i8 a3 w- j: _    current->state = TASK_INTERRUPTIBLE;
: y& z* S8 I5 Y; b' U' y* F+ Q' \   
( k, l* V5 E. Z5 U$ A* Y8 u    SLEEP_ON_HEAD
. q7 q  F( A/ {  o1 O/ {6 V   
8 m" T0 G4 b3 w. T1 X1 ^! n& k( t5 s! B    schedule();
* s) ?. x! \2 J# s* c, e   
( c& v! i* _' n1 T/ i- V    SLEEP_ON_TAIL
6 J1 w0 m6 r, V! l8 t( e    + l: r5 Q+ L9 n0 r1 K8 D/ n
    }
' T' g; b0 r- @: L! P/ N3 A   
/ W& |- _0 z) g) _( a& g    void sleep_on(struct wait_queue **p)
1 @* M, o, h: N3 D- I; z6 n8 n    / Y7 k5 Y  s  y: T
    {
) F$ k- A9 k7 l   
; H6 ~- [  @4 V* G    SLEEP_ON_VAR' ~- A* A& F% F% t
   
8 y* [5 C: J0 S# y    current->state = TASK_UNINTERRUPTIBLE;
4 V  H% g8 Y; D" C! H' g2 J   
5 ~5 e$ v3 D6 W: q' A, C    SLEEP_ON_HEAD
" c) H0 o% J/ _, d5 R" Z* B+ m    7 y" E/ A' k2 x, M0 V8 C  ]
    schedule();
& ]6 E* f" n5 |/ b    5 A/ T$ V  ^4 X: j2 b* M
    SLEEP_ON_TAIL
/ T8 a9 t$ R# o+ u    8 c0 g7 `3 J3 M5 ?2 @
    }
回复

使用道具 举报

 楼主| 发表于 2012-8-4 12:07:08 | 显示全部楼层

Linux系统管理:内核等待队列机制介绍3

各位有没有发现这两个 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 后的结果就第三张图所显示的。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-5-17 09:29 , Processed in 0.358775 second(s), 23 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表