a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 213|回复: 4

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

[复制链接]
发表于 2012-8-4 12:07:07 | 显示全部楼层 |阅读模式
flag = 0;! b  \7 o8 K7 A: c* s+ \
   
; r3 \) ?& V8 ]4 i5 |# x    wp = rp = buf;; V& u% F' `  ]' j8 c0 K. N+ r- \9 [& }+ i
    1 ~, M1 t' [8 }2 [& `
    result = register_chrdev( 54,"buf",&buf_fops );( g2 i- [1 f8 M. `7 `2 N
    : O3 Z) K' S0 m( ^! j6 A) ^
    if ( result < 0 ) {* u7 Q8 {2 m" e  F5 P- }9 v0 @5 X
   
+ p3 Y5 w8 `2 `$ A( F    printk( "buf: cannot get major 54 " );
) v+ s5 P8 x$ Z* c4 F   
( |! E, v( [: P4 ]1 i* Q; P    return result;! j, i  J+ S" i  a
   
0 f! q& |) }: E4 Y0 ]/ q9 S    }  }0 [$ e- B) f0 I
   
: a8 {- _# Z5 c& u; M$ Y    return 0;
- q5 A7 S# n/ N7 t    - B2 e; u9 S. d% {' N* V
    init_buf() 做的事就是去注册一个 character device driver.在注册一个 character device driver 之前,必须要先准备一个型别为 file_operations 结构的变量,file_operations 里包含了一些 function pointer.driver 的作者必须自己写这些 function.并将 function address 放到这个结构里。如此一来,当 user 去读取这个 device 时,kernel 才有办法去呼叫对应这个 driver 的 function.其实,简要来讲。character device driver 就是这么一个 file_operations 结构的变量。file_operations 定义在 这个档案里。它的 prototype 在 kernel 2.2.1 与以前的版本有些微的差异,这点是需要注意的地方。- L- ?% `) U. `  J" i8 }/ J
    5 ~- D0 B$ s/ k: e2 r; M* a
    register_chrdev() 看名字就大概知道是要注册 character device driver.第一个参数是此 device 的 major number.第二个是它的名字。名字你可以随便取。第三个的参数就是一个 file_operations 变量的地址。init_module() 必须要传回 0,module 才会被加载。" D& o9 q& g8 W3 e$ H
   
2 V) M0 b" A6 `' @6 _4 k6 L1 Z    在 cleanup_module() 的部分,我们也是只呼叫 buf_clean() 而已。它做的事是 unregister 的动作。
( f0 [9 a: t0 {! [! Q2 ]/ U   
' }5 f( h8 l, H( T, L0 V7 W7 W* W    if ( unregister_chrdev( 54,"buf" ) ) {! x# x+ ^* K" c  c. d* \
   
% r8 s3 x( x1 K2 |/ s2 o8 S: y    printk( "buf: unregister_chrdev error " );
' E- q0 f; u6 x: `! P) X( d   
8 z9 _7 d8 t7 p6 |& b! l    }
$ p8 M. `' }7 c3 r9 U. M9 x    % C. h; o# J5 J  R/ l8 j5 p
    也就是将原本记录在 device driver table 上的资料洗掉。第一个参数是 major number.第二个则是此 driver 的名称,这个名字必须要跟 register_chrdev() 中所给的名字一样才行。' t9 [) N/ `& \8 x7 C
   
) ~) y* i2 b6 W    现在我们来看看此 driver 所提供的 file_operations 是那些。5 Q( g4 B5 m( Y. k6 G0 p+ F! ]
   
0 ~7 L* p7 M0 ?  W- I% Z( i7 q    static struct file_operations buf_fops = {
9 \  ?% ?; X6 g& I( Q" Q8 ?3 U   
6 ?+ q$ Y' [4 o5 b" b' v( w    NULL, /* lseek */
6 a! C; }+ e: C6 j, A: |* N; P    ) m* S' g4 w7 L
    buf_read,4 B- Y" _3 ?/ c0 h$ a; s
    - ~$ m2 H; i, K
    buf_write,
  [; }* V! n9 I5 R% R- U6 i* v    % Q+ u% R) V9 c3 w+ F
    NULL, /* readdir */+ a4 }3 i! r/ _# u
   
$ r- K% |; C) n    NULL, /* poll */8 o; l  L7 ~% d3 s$ I
    , F4 m: k3 m/ \) r; i$ V+ s" E
    NULL, /* ioctl */2 S# t! h+ v+ c
    + n" e  o- f( d9 x9 B7 m/ t6 C
    NULL, /* mmap */) w) c+ G- w2 R9 }7 \
    # C" T* e5 J# g: s
    buf_open, /* open */
8 {$ V# y- }0 v/ m8 {0 a1 l   
( R: q! b, w% [" s7 Z% a( p    NULL, /* flush */) h2 d1 ]( N( v
   
! Z% |9 e% ~5 e: ~0 I7 k/ e& x% a    buf_release, /* release */. G* @# @2 J! f8 K% z
   
8 S' N" U% p; w7 ^( e7 C6 d    NULL, /* fsync */
+ S' m* x# a/ O* {) G% P   
6 `" C; H6 ?( C1 a    NULL, /* fasync */
4 b- M' k7 j- w! B3 ?, z) X    4 u8 S" [( L2 A" U8 ~
    NULL, /* check_media_change */
, x, S8 L& n6 i, O% ^# I   
$ U( W1 _- T( R    NULL, /* revalidate */
% Y! x8 \# W: V2 g3 C3 G, |    & ^: z& I( R" ?" c" q* I* Q
    NULL /* lock */
) l  i' p# f: W" ~    ( @" \2 z0 I% P6 ~) c) o
    };
回复

使用道具 举报

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

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

在此,我们只打算 implement buf_read(),buf_write(),buf_open,和 buf_release()等 function 而已。当 user 对这个 device 呼叫 open() 的时候,buf_open() 会在最后被 kernel 呼叫。相同的,当呼叫 close(),read(),和 write() 时,buf_release(),buf_read(),和 buf_write() 也都会分别被呼叫。首先,我们先来看看 buf_open()。
/ ^) X0 q" H* G  j' j$ C5 E. B   
' e3 U0 F/ L; ~" h5 k. ]8 K    static int buf_open( struct inode *inode,struct file *filp ), H+ R5 Q  D: p7 t2 `% g
    ( i- f; A2 U' W* p4 T* z- @
    MOD_INC_USE_COUNT;' e  K# d5 E  ?- i; ^$ s
   
8 g! k# X! J) |+ x, v8 A& R) A% J    return 0;
1 N6 Y/ x( d% }# }    & L+ o( X( z" a0 E4 e
    }4 b& e: }' V6 T+ q
   
" r2 ^7 p0 l& ^; X; H+ P    buf_open() 做的事很简单。就是将此 module 的 use count 加一。这是为了避免当此 module 正被使用时不会被从 kernel 移除掉。相对应的,在 buf_release() 中,我们应该要将 use count 减一。就像开启档案一样。有 open(),就应该要有对应的 close() 才行。如果 module 的 use count 在不为 0 的话,那此 module 就无法从 kernel 中移除了。
: `, C/ X( {# N; q$ q. Y" q4 [8 H   
$ `# N# ~& @' k! s    static int buf_release( struct inode *inode,struct file *filp )4 B8 \4 [5 d- K9 v
    # X& l$ R3 S& g2 B: F
    {
7 z6 O. Z  N8 J3 T' |3 `9 D   
  f+ j& J$ }3 V0 t    MOD_DEC_USE_COUNT;' W$ M2 H" P' Q
   
- @& o* x5 L" R9 i1 Q    return 0;% g8 M9 r3 R. z% r0 l
    8 W( P6 k$ s7 X- ~( Q% f( A
    }
( u( ~* \% h: b3 M" h   
$ x7 G$ }. K; O) J6 C) p. k. i    接下来,我们要看一下buf_read()和buf_write()。
" J% M- u. v6 w   
; t& Y  ?% n0 N    static ssize_t buf_read( struct file *filp,char *buf,size_t count,
. w( k: \( s, o8 V2 I: t   
6 F! z: Q+ l' u* v% C" I    loff_t *ppos )/ K- k3 G5 Z+ ^6 z9 d' r) c- u
   
  b# v3 b5 f/ a. ?! z' p    {
5 u- c2 Z. Z& [4 c& i3 Z    5 m8 i6 {/ O0 M0 N$ o% m9 P
    return count;- t  |& A5 q, h2 z8 V# i) G( r: V2 |
   
9 K5 `* k/ l$ }2 U    }
5 e1 {5 w5 s1 E- b   
6 P& I) S; R  C# C* \    static ssize_t buf_write( struct file *filp,const char *buf,
& C# c0 K& S( J- ~  l   
7 e- _2 o6 N% C. ^6 y. H    size_t count,loff_t *ppos )
% _8 G$ z$ O+ m- {8 N& q/ F    " E3 U& Z% ^# K) j  F0 {
    {
6 j2 m' U6 S0 C' A# E    3 r& E2 \6 P+ {: w! z
    return count;/ m+ O+ s4 ^) Y; j0 [
    4 S' @4 S9 }8 Q; b
    }% T) {! z2 c$ @
   
+ Z  g/ y3 g' U3 e. O    在此,我们都只是回传 user 要求读取或写入的字符数目而已。在此,我要说明一下这些参数的意义。filp 是一个 file 结构的 pointer.也就是指我们在 /dev 下所产生的 buf 档案的 file 结构。当我们呼叫 read() 或 write() 时,必须要给一个 buffer 以及要读写的长度。Buf 指的就是这个 buffer,而 count 指的就是长度。至于 ppos 是表示目前这个档案的 offset 在那里。这个值对普通档案是有用的。也就是跟 lseek() 有关系。由于在这里是一个 drvice.所以 ppos 在此并不会用到。有一点要小心的是,上面参数 buf 是一个地址,而且还是一个 user space 的地址,当 kernel 呼叫 buf_read() 时,程序在位于 kernel space.所以你不能直接读写资料到 buf 里。必须先切换 FS 这个 register 才行。
回复 支持 反对

使用道具 举报

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

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

Makefile& o: j+ ~3 A8 o0 t- A) X
   
, n6 r- B9 d9 J- S    P = buf
' t! |+ J& F9 v5 e/ ^  K0 M6 M    1 P) E- j- c" v
    OBJ = buf.o% x& z5 K- p$ G2 L
   
: w2 Q# \: V" l) _! @3 Y" b    INCLUDE = -I/usr/src/linux/include/linux
+ u! Q& S( ^9 E1 j$ x    & e+ m& y! N% f* m; P
    CFLAGS = -D__KERNEL__ -DMODVERSIONS -DEXPORT_SYMTAB -O $(INCLUDE)
0 G# d, T: @2 n  m2 R   
5 @3 w0 z: b+ p  |1 W. x    -include /usr/src/linux/include/linux/modversions.h
( ~+ K0 m8 f( M" w; E) |    9 w( e- m  z) a) N& Y6 J
    CC = gcc
1 ]# o( G/ G/ C4 f) C   
0 \/ N1 T  G* C% k5 R    $(P): $(OBJ)
3 }+ T+ D, h; j    / [( P6 Z" A$ Q9 h! I( H+ P9 z
    ld -r $(OBJ) -o $(P)。o
: ?7 i* l% W. h0 P# V3 w   
, w, H& e- V/ N9 y* p    .c.o:
+ Z  ^% _+ @6 P  [7 [8 f   
/ i* e) c# c7 U% Y/ G    $(CC) -c $(CFLAGS) $
回复 支持 反对

使用道具 举报

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

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

if ( ( rp != wp ) && ( count > 0 ) )
! s( J! x8 G% Q; I   
1 ^9 F% c: V, W: V* m    goto repeate_reading;, z' B6 Y' w: N" S
   
7 }0 w" L( b' ?" \5 e4 v    flag = 0;
( ?- x7 N. N5 B/ E; R3 W9 e   
* Q1 W. S6 U  Y0 [# c    wake_up_interruptible( &write_wq );
3 ^# [$ o/ _5 w, R4 @1 Y    , @7 x: ?8 C2 @9 [- v
    return nRead;2 z! l' C( |5 u7 K
   
0 g# ^" ]" Q7 i$ I/ u% B! k    }
  g6 x* X2 m' o2 l* W% E# O    & m  H% N+ B1 E# ]2 ?
    在前头我有提到,buf 的地址是属于 user space 的。在 kernel space 中,你不能像普通写到 buffer 里一样直接将资料写到 buf 里,或直接从 buf 里读资料。Linux 里使用 FS 这个 register 来当作 kernel space 和 user space 的切换。所以,如果你想手动的话,可以这样做:
% t& Z, S) V) I# d# T5 x/ Z   
- r. j) v* [  v! `' l. g: R    mm_segment_t fs;4 T* t% f; e, Y) Y6 y8 _' O
   
+ q1 }# Q0 z0 ?0 {, ?    fs = get_fs();
8 o/ r: g9 ~% ^+ `   
9 q7 D& ~) I$ r5 f7 b! q4 A1 j    set_fs( USER_DS );
3 Y9 n4 j3 _% ^( ~7 v    ( e0 ]5 g* e, {+ ?/ K$ R2 _
    write_data_to_buf( buf );
- y4 s5 ^  _1 O$ ]7 {   
3 i( {  J' G: H# H; c    set_fs( fs );0 P) P5 w1 Q& R* V
    $ a! Q, p2 b- |
    也就是先切换到 user space,再写资料到 buf 里。之后记得要切换回来 kernel space.这种自己动手的方法比较麻烦,所以 Linux 提供了几个 function,可以让我们直接在不同的 space 之间做资料的搬移。诚如各位所见,copy_to_user() 就是其中一个。# D' i& m' I/ w
   
) U/ ^! z. _, s3 c7 |. v3 S3 {    copy_to_user( to,from,n );
0 u' `2 F  ^5 l  [    6 B+ u3 H! Z' s7 D% }: k+ a
    copy_from_user( to,from,n );- ~# I' f6 t2 m7 S3 B* J
    ( r4 b+ ~3 @3 T2 @
    顾名思义,copy_to_user() 就是将资料 copy 到 user space 的 buffer 里,也就是从 to 写到 from,n 为要 copy 的 byte 数。相同的,copy_from_user() 就是将资料从 user space 的 from copy 到位于 kernel 的 to 里,长度是 n bytes.在以前的 kernel 里,这两个 function 的前身是 memcpy_tofs() 和 memcpy_fromfs(),不知道为什么到了 kernel 2.2.1之后,名字就被改掉了。至于它们的程序代码有没有更改就不太清楚了。至于到那一版才改的。我没有仔细去查,只知道在 2.0.36 时还没改,到了 2.2.1 就改了。这两个 function 是 macro,都定义在 里。要使用前记得先 include 进来。
/ n  F; L* f% W/ |7 _. k  Y" d6 W* b   
( [( s# w3 B1 [. G    相信 buf_read() 的程序代码应当不难了解才对。不知道各位有没有看到,在buf_read() 的后面有一行的程序,就是6 y, N, J" n( W8 ]/ T" Z/ O& M* Y
    ! N6 j. p: o; I, I
    wake_up_interruptible( &write_wq );
9 t8 |. l9 ^7 `* |  v# H   
2 C, i2 i+ ~4 R/ z( I' q& X8 s    write_wq 是我们用来放那些想要写资料到 buffer,但 buffer 已满的 process.这一行的程序会将挂在此 queue 上的 process 叫醒。当 queue 是空的时,也就是当 write_wq 为 NULL 时,wake_up_interruptible() 并不会造成任何的错误。接下来,我们来看看更改后的 buf_write()。
9 H2 x1 d$ g* l: P$ @) D& U    : D) V% m' R- Z( ~& F7 Y9 A( Q
    static ssize_t buf_write( struct file *filp,const char *buf,size_t count,loff_t *ppos )
3 F& z$ b( p2 I" B& v2 f   
  V6 n. S# V/ {' P& x3 D; T8 J    {' X9 F. a8 O- _
    / B! s! B9 Y9 A. c2 i
    int num,nWrite;
, P; ^6 q! }0 o    ( Y% K: u" o' p, O4 i
    nWrite = 0;. ?8 ^, @3 [$ {& E  t+ m
   
  c, r7 \1 p1 a. R: a( ]% h+ Z6 }. t    while ( ( wp == rp ) && flag ) {
3 }! s9 W3 ?8 ~4 |    ; F5 }* Y, B  G
    interruptible_sleep_on( &write_wq );2 c4 e9 |$ e" H- M8 `
    8 C! Q+ G8 Y* n* Q
    }- H: V, {8 w2 Q
   
- ~$ P8 v/ d2 W    repeate_writing:& s* H5 {+ F) O& ]
   
7 e, L5 N; o" L! h( J% U    if ( rp > wp ) {5 {& B6 [2 K1 F# Y4 q
    & E/ l2 ]( H# F0 [8 \. ~& H
    num = min( count,( int ) ( rp - wp ) );
! ^$ P2 e0 D  E, {* N    & ?0 C  I) m) g. k6 k' f
    }: H5 F' a5 X/ i/ J0 O% u
   
. i8 ?$ w8 ~) t8 b6 M% H    else {
- a) j( W; V5 n& `, Q    * u8 r* z, h, e( c  F0 S
    num = min( count,( int ) ( buffer + BUF_LEN - wp ) );
* f' L+ O$ e2 B7 [7 T   
; H& s% K' Q: E4 j  e6 R' i3 Y    }
( m# T7 d+ a& [" b  M7 |. [% ?: N    . S& T) J; y. q
    copy_from_user( wp,buf,num );
# O" N% C% T' j, }! q3 Q6 g    & z% c2 g4 {1 |& A
    wp += num;
* @1 n, y5 ]% o4 m5 C4 j# \% w   
& |/ t/ [/ J- }' Y9 n: n    count -= num;, U! V- x) }7 S' p* V
   
) E& x8 _; i) s6 x+ h2 S3 F. u; K    nWrite += num;
' ?$ F8 A2 l, I5 k& x: O   
. ^$ k6 k" M" D, Y! r3 ?) Q( x    if ( wp == ( buffer + BUF_LEN ) ) {
3 ]3 U  d* ~* D, J      j$ p# }  c- W7 w6 d: M& O% G5 K
    wp = buffer;
回复 支持 反对

使用道具 举报

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

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

}) f7 d# }9 F' M% M' p, J4 q) N
    5 z* D! \! O. k" ]$ B$ X
    if ( ( wp != rp ) && ( count > 0 ) ) {0 p" u: ]" i7 b# k6 N4 S
   
; W2 g+ J9 `1 |! U  Y7 A    goto repeate_writing;8 |! O& i8 h- L9 A$ ~2 M( |( L
    : }- N: h+ c! l9 S8 m" x" N  q
    }
  M0 Q) |' S$ @1 t1 }* Y, M/ K0 v   
  f2 _; n8 z5 U6 [* l% A    flag = 1;( a2 V: V! ~3 c3 y% }! N+ J( \
   
4 W3 v* L4 o: L- b1 `    return nWrite;
- t% P+ j  f. V   
- M  C  U% E) |9 o: |  n7 d    }
& }% E5 B( S* d( b   
, h0 e5 L: D5 ^1 L    我们把 process 丢到 write_wq 的动作放在 buf_write() 里。当 buffer 已满时,就直接将 process 丢到 write_wq 里。
. I, P8 T2 W9 D3 }    ( A: H" }; G2 ]5 g. Y& y! R
    while ( ( wp == rp ) && flag ) {9 W& e$ i  X' J/ U; A# I$ h8 r4 z% u; ?
    / K0 o! w, n- e$ G( M  R0 v
    interruptible_sleep_on( &write_wq );
9 h1 c+ x  l+ |* U   
5 v0 j4 ~8 |/ P2 i0 Z) Q% J/ K    }
( a4 g  h/ Y1 w0 t: p    5 @# B6 K7 {, M9 C4 {; G9 r  S( b
    好了。现在程序已经做了一些修改。再重新 make 一次,利用 insmod 将 buf.o 载到 kernel 里就行了。接着,我们就来试验一下是不是真正做到 block IO.
; t" D" @) _3 a! T0 U# o0 K! W    : Q. X( e) h1 ^6 ~) M6 u
    # cd /dev& ~" y0 U% H! a- f" ~
    1 z. n3 e& H, {/ n( h
    # ls -l ~/WWW-HOWTO" p; `, \1 S% ~% Q6 z2 n2 _
   
9 [& n1 |- g0 [7 M& G- l; b; _$ V    -rw-r--r-- 1 root root 23910 Apr 14 16:50 /root/WWW-HOWTO+ |5 n; ?( L1 J$ u% e' N/ q
    : z7 J1 A( E7 B9 z7 V
    # cat ~/WWW-HOWTO > buf
/ X! g( c1 Y  W    4 `  A9 K' {4 B
    执行到这里,应该会被 block 住。现在,我们再开一个 shell 出来。0 E1 J" \$ B3 g+ C# X. c
   
% c( S6 R- T8 g- l+ R0 G2 k6 E    # cd /dev# K9 Q: e  T' r) u+ n$ e" |3 T5 W& ]
   
7 g4 P( V, E* R  \7 o  ^2 s    # cat buf
+ Q8 s5 j2 u* L2 x, }7 ~# p0 s: O    + a! e. n7 \# _0 [
    …( contents of WWW-HOWTO ) …skip …( D. ~1 D5 A. m- f
    - Y# Y: {) S7 d) v+ ?( q8 |9 r
    此时,WWW-HOWTO 的内容就会出现了。而且之前 block 住的 shell 也已经回来了。最后,试验结束,可以下
* D& l3 R" o) I2 a# O% j, c    5 \# Y; N0 t  I: Y' o4 z. B
    # rmmod buf
% M% ^  g7 c7 p; L. Y   
; _' p9 q4 D5 [& a1 R" U+ C; Q    将 buf 这个 module 从 kernel 中移除。以上跟各位介绍的就是 wait_queue 的使用。希望能对各位有所助益。
0 O( _$ c/ p& `# s! z4 ~0 G. F    0 N/ u0 S* h% B# }+ |2 s
    我想对某些人来讲,会使用一个东西就够了。然而对某些人来讲,可能也很希望知道这项东西是如何做出来的。至少我就是这种人。在下面,我将为各位介绍 wait_queue 的 implementation.如果对其 implementation 没兴趣,以下这一段就可以略过不用看了。
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-5-21 15:29 , Processed in 0.184630 second(s), 29 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

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