|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
1 J9 {2 u* X1 ^
在前一章(Linux内核设计与实现之中断处理:https://www.eda365.com/thread-464742-1-1.html)也提到过,之所以中断会分成上下两部分,是由于中断对时限的要求非常高,需要尽快的响应硬件。( R' o" r) D9 _! E9 p! Z2 \/ R
& B/ J2 O4 O% W9 [+ v5 a主要内容:' h0 h4 O! h; p) {. c& B7 M
3 D- a; h1 B! q) b5 u* p
中断下半部处理6 h9 ?/ l& j, W. y/ e" F8 q# x# m
实现中断下半部的机制9 m- [6 I7 {7 ^$ T( a
总结中断下半部的实现. K+ k* X" x* y2 _
中断实现示例
% b n. j E9 _; L1 _) M) N
2 q F2 F% G! t
0 v+ P* `5 d7 u9 P1. 中断下半部处理
7 r9 S9 ~- N9 p! d那么对于一个中断,如何划分上下两部分呢?哪些处理放在上半部,哪些处理放在下半部?8 X, T+ }' m, z! G, @& W
, X* f! a/ {; q( D% d这里有一些经验可供借鉴:
' \+ l7 l0 `% H5 m
" s2 h, t# f# [如果一个任务对时间十分敏感,将其放在上半部
. V9 ?6 \9 ?& F) x/ x如果一个任务和硬件有关,将其放在上半部" o6 W5 a3 z9 q, Z
如果一个任务要保证不被其他中断打断,将其放在上半部
6 Q4 M: O$ I- ^6 _其他所有任务,考虑放在下半部8 _8 K+ H. k/ F9 R# ], q
) Y7 l$ \# W1 S4 B9 J
2 _3 `0 r6 L1 u4 W. q; Q2. 实现中断下半部的机制3 w" t" i7 @0 v$ Y; i; m0 [
实现下半部的方法很多,随着内核的发展,产生了一些新的方法,也淘汰了一些旧方法。. S1 O: H1 D1 x6 g4 [3 v5 C, l& p
/ B; \& n5 h y1 C& u6 `
目前使用最多的是以下3中方法9 c, d+ S" C: d# s" n
8 N$ z$ f& {% c7 d6 [
2.1 软中断/ v6 ^4 l3 B1 m
2.2 tasklet
& k9 v$ ?, R$ H2.3 工作队列
& r+ W n9 ]( ^$ `" L4 P2.1 软中断. q: d8 n6 F9 d2 Z( i) P
软中断的代码在:kernel/softirq.c
( |- {- e9 V) `$ @1 C$ ?* P' Y0 |% v$ i# q
( M, o& R1 O: r2 G
5 v! |# D: I7 T( p ^* C
软中断的流程如下:/ |8 }# a, T ~, {. W3 \4 N0 S: Y
, k# H( K/ f' U+ t fsoftirq8 B; r5 k, X$ ^' R# M( f0 Y% U
, K( I' i# _: r" k
流程图中几个步骤的说明:
7 f7 C' [- F U. g2 V! v& {4 B: c4 J8 T3 u/ o4 Y7 e6 K$ w
① 注册软中断的函数 open_softirq参见 kernel/softirq.c文件) Q7 P4 Q9 H- q; x$ A
8 _7 c4 R4 X4 ^6 E$ z; G
复制代码$ ]* n6 l6 o( V, U2 ^/ O j: f! s
/*
# A) y& V; Q( G$ W) E; |4 v: h * 将软中断类型和软中断处理函数加入到软中断序列中
( y' l7 }4 R3 C' E( q * @nr - 软中断类型- V$ A( \6 ^1 A$ w( M3 J7 Q, Z
* @(*action)(struct softirq_action *) - 软中断处理的函数指针
' M# \' ?& B0 f5 v9 {! { */
( P; Z) @5 z8 U( }4 M% j- Qvoid open_softirq(int nr, void (*action)(struct softirq_action *))
4 ?/ [' F* x" W/ Q, Z+ B* B3 B H{
1 B% S9 w. U: a: v* ? /* softirq_vec是个struct softirq_action类型的数组 */5 _; G, q/ l( p1 x
softirq_vec[nr].action = action;
# a2 K/ X& |" ~* r/ Q, } s}
" u' D+ Z& ^7 t1 _) v3 P复制代码 y( b$ o) Z, g- g
软中断类型目前有10个,其定义在 include/linux/interrupt.h 文件中:
/ s- }% P; k' _- E. G' h3 t
' {+ ~! A" R/ [! K! W- ?复制代码% B( {/ H- n9 L7 l; e
enum
5 d! Y2 [* ?( }! b) f) z9 e: B{
$ N% L* V' `! V# f; ` HI_SOFTIRQ=0,! u2 U+ Q0 I$ \& M- d: G
TIMER_SOFTIRQ,- Y4 x/ g$ |8 f4 G' P; A
NET_TX_SOFTIRQ,
/ f1 [ x+ [% J7 r5 ^ NET_RX_SOFTIRQ,
: g: k3 a$ `5 W) d6 `/ X BLOCK_SOFTIRQ,
4 ?" o' n- C Z8 Y1 W BLOCK_IOPOLL_SOFTIRQ,
; s" j1 M8 ~7 \. ~& c TASKLET_SOFTIRQ,: |2 Q) L0 K; P4 R% Z
SCHED_SOFTIRQ,
8 L. y5 h; S1 g5 E. b: s HRTIMER_SOFTIRQ,- X7 }9 Q, D' p" p* G8 s! O
RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */) K) s$ f6 J/ {; g
: r9 a1 Y2 A# k& G$ p NR_SOFTIRQS
6 `+ ^& Z. T! D};
. X0 N0 b* p: ?9 V6 F+ i% o7 ]复制代码
$ J+ h( |- f5 M! z" H" I" Y: Bstruct softirq_action 的定义也在 include/linux/interrupt.h 文件中
, {7 `; a5 K! K& i2 x, Q* V2 Y/ w0 }7 h9 I
复制代码) T% W3 S$ {& k6 A5 L5 a: k2 G3 X
/*
3 H, C, I0 B7 b- Q * 这个结构体的字段是个函数指针,字段名称是action7 X' S4 P# O! T1 v
* 函数指针的返回指是void型
) v- j$ B6 E1 c4 | * 函数指针的参数是 struct softirq_action 的地址,其实就是指向 softirq_vec 中的某一项) @7 \+ F( Z' P, b& X, b5 Y( k
* 如果 open_softirq 是这样调用的: open_softirq(NET_TX_SOFTIRQ, my_tx_action);+ q9 V! \: ~2 N5 c
* 那么 my_tx_action 的参数就是 softirq_vec[NET_TX_SOFTIRQ]的地址
( E# r' |( V) H u4 b9 ~ */" I7 E7 J7 n5 `2 A/ Q6 ?
struct softirq_action
9 d3 o" o' \+ ?# l/ ?% i4 T; [{2 L {1 K/ P8 K
void (*action)(struct softirq_action *);
* [$ p- C/ m% u# h5 n: ]6 z3 J% ]};+ p! Q4 \: @& v$ I
复制代码
) T5 O" w7 L( O0 y0 j, }( z- X& v* I : w( D0 `5 F; B3 j0 N
5 U, g4 O& B, q f( C% [. p: J0 ^② 触发软中断的函数 raise_softirq 参见 kernel/softirq.c文件+ \4 j6 n, T3 A; `/ r0 ~
7 A2 G2 M; A: l3 P9 \! \复制代码# z$ l+ `+ `0 W% M0 w0 k7 X q+ T: p
/*' h/ x4 k7 x6 x5 E; h/ o( p0 R
* 触发某个中断类型的软中断: {0 ], K$ }% u7 H9 W4 Q
* @nr - 被触发的中断类型
: A6 q+ r' G2 I( @/ u" k6 \ * 从函数中可以看出,在处理软中断前后有保存和恢复寄存器的操作7 n' {- @+ [3 u5 @, {$ h# D
*/
6 d& ?* y) \, J2 @' U$ T0 svoid raise_softirq(unsigned int nr)
' k: {3 N5 o5 a \{, \' B% s$ \* z( }8 y* [$ ~& f! z
unsigned long flags;
% Z4 E9 X/ K" d7 ?5 x% v' y3 w% D! @3 J* M) _6 E
local_irq_save(flags);
9 Y4 H1 t( I2 R* n& J4 g( K7 Q( { raise_softirq_irqoff(nr);5 V, n! e( l5 Y# R# D& m# ]
local_irq_restore(flags);! A- T( b( @+ S4 A
}
- o5 a, k+ O, _; `4 A& W$ ]复制代码
0 W, |, Q& E) F 6 `# p: E2 m* g( L1 m% d# \0 c
! p0 `. J* n' T2 l
③ 执行软中断 do_softirq 参见 kernel/softirq.c文件3 `/ s, D5 U9 t9 V
, X. h" U! n5 Q3 ^* t- P复制代码
* I" M* K* l7 K1 x, y' n0 Oasmlinkage void do_softirq(void)
2 \3 i- U& |# p% U2 z: I{
9 A" U% {! w5 j- o __u32 pending;
0 P* y' n$ T3 G# i' L" @4 V unsigned long flags;
0 _3 {: q. J' G
- r! S- @- [5 G4 `: C9 Q" r( p /* 判断是否在中断处理中,如果正在中断处理,就直接返回 */. k. }2 J P& E2 o3 u' @+ z
if (in_interrupt())
" Y5 }* `! e/ A6 o: {+ V return;' X4 o: x1 K) a m9 t8 o7 v
9 U$ x4 T) [& ]8 ]5 ~' F /* 保存当前寄存器的值 */0 P5 L( P0 ~6 P7 t- b
local_irq_save(flags);% ~5 \5 a }, I7 w- _
8 K1 o r& y3 ^0 y3 w
/* 取得当前已注册软中断的位图 */- _" P3 c1 ]2 \; \1 r+ Y' n
pending = local_softirq_pending();
) {5 [1 `0 u* a; v, p1 l, c) E2 f3 ]0 C
/* 循环处理所有已注册的软中断 */5 z- U" X; H* V& k
if (pending)
3 h3 C$ H6 n8 n. I! \! _7 { __do_softirq();: u) [9 Z4 c% C! |. e8 {
3 m5 ]7 _0 \; z0 M( O# d
/* 恢复寄存器的值到中断处理前 */4 N7 I4 B9 w/ Z1 p9 a& ^5 [8 }5 U
local_irq_restore(flags);3 C1 _. `! F: V, B1 z# Q# b# R
}4 d( \9 y9 u N7 ?
复制代码+ G9 i0 O# l: n" }9 p( v
& W2 ? _/ M& y- u9 e: P1 X7 U3 y1 _, Q+ a; \" S0 D- s$ T# D
④ 执行相应的软中断 - 执行自己写的中断处理
) t7 O! n! t; |# r! B
9 B$ ]( y m4 n2 Rlinux中,执行软中断有专门的内核线程,每个处理器对应一个线程,名称ksoftirqd/n (n对应处理器号): V: i& l+ i% j" R( d
/ r3 Z2 F$ \! C$ W7 f通过top命令查看我的单核虚拟机,CentOS系统中的ksoftirqd线程如下:
6 a2 c* n7 ?3 i" }6 A
7 ^; n6 E& ^; a% h7 H/ u[root@vbox ~]# top | grep ksoftirq
4 y+ a0 D, I/ k3 C* N7 T" `" N9 n 4 root 20 0 0 0 0 S 0.0 0.0 0:00.02 ksoftirqd/0) A7 c# ~0 I( v0 g, w$ Z
; b7 u! X, s! L
9 a1 q, P3 B/ U$ W3 A2.2 tasklet+ j7 ^" D, c& V) E8 |
tasklet也是利用软中断来实现的,但是它提供了比软中断更好用的接口(其实就是基于软中断又封装了一下),
# W4 ^* J) N: M- V2 L1 W
) {- M2 C$ C4 ?所以除了对性能要求特别高的情况,一般建议使用tasklet来实现自己的中断。/ s. c8 O; V5 _$ v1 A, p
9 m! \: d& P7 Q1 e; o
7 N0 r, B0 M+ i6 n0 x4 ~; W" C0 p z: I+ }
tasklet对应的结构体在 <linux/interrupt.h> 中
) ^4 N/ [1 _- e; v' E7 p D* G8 A3 D! ~
复制代码* I. U! t6 ]3 ]! C+ d) U. l1 S
struct tasklet_struct
: K7 J" ~" b; t5 T% S% M, c{$ O$ n8 j# I( ~9 P
struct tasklet_struct *next; /* 链表中的下一个tasklet */' I* w2 w; Q' w
unsigned long state; /* tasklet状态 */
. F5 |4 S# a0 J% R. \* v. ^; ] atomic_t count; /* 引用计数器 */
+ b8 n' D" r- M4 v$ g" \4 ^ f void (*func)(unsigned long); /* tasklet处理函数 */2 l+ q$ P! Z# m6 A" _; C
unsigned long data; /* tasklet处理函数的参数 */* c) | \8 T0 W; b5 p
};$ z1 |" p8 K, B
复制代码' L# |$ {5 P# q: l" a
tasklet状态只有3种值:
( F0 b! ^0 Z- h* m& A, I. t+ i. T- m' Q. l# j: C: |. Y
值 0 表示该tasklet没有被调度$ ]; d+ ]- M& z5 f
值 TASKLET_STATE_SCHED 表示该tasklet已经被调度/ W H$ ^( k- q7 S `
值 TASKLET_STATE_RUN 表示该tasklet已经运行# I" a9 G4 ^2 a7 X3 D- C
引用计数器count 的值不为0,表示该tasklet被禁止。
M$ D% H9 L4 s; m
1 e% C/ z% j" \9 f1 D3 _* y
/ X; a3 Z6 A: A8 V' ktasklet使用流程如下:
q1 k; b2 n5 q$ l" _5 u2 x; H6 Z1 N( P
1. 声明tasklet (参见<linux/interrupt.h>)
; ? ^+ Z9 M) Z; j. M$ x9 ]4 G' R9 s" R1 |1 ^0 i
复制代码
8 z& p6 w9 h9 v" q7 R( A/* 静态声明一个tasklet */
$ H% W, V! T" v% O#define DECLARE_TASKLET(name, func, data) \
7 s# k& b5 ?4 z! e* A" { dstruct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }, X- I# a6 R' i5 @2 B' u; G& o# `3 f9 G
9 t5 ]4 c! R5 E1 E# W- J7 y- s [+ a#define DECLARE_TASKLET_DISABLED(name, func, data) \
# Y) q) N" U8 ^7 ^" V( Z& Hstruct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
/ c0 H- b- {% l: {/ N+ m' N: h
# T( a( |. G8 p0 ]/ c/* 动态声明一个tasklet 传递一个tasklet_struct指针给初始化函数 */
5 W3 k' l# T+ k" c" ]1 U! kextern void tasklet_init(struct tasklet_struct *t,
( _$ z2 O" m4 j7 Y- P7 E! K void (*func)(unsigned long), unsigned long data);4 }& \1 Y6 Q/ e9 m3 M% S
复制代码7 m4 }3 j; x! {$ L, H6 g
2. 编写处理程序
/ V9 a. c+ o! U2 C/ B0 u$ i: C
' e& [3 @+ T0 ?# e4 l4 A$ t0 p4 f参照tasklet处理函数的原型来写自己的处理逻辑
% v8 F# V5 n- e
) F% ^. l( w+ n. l+ j( Mvoid tasklet_handler(unsigned long date)# ?# V: g' w* w2 a0 u G
3. 调度tasklet; X! j. I) o; M
( r3 @! F" }% O! [; l. n/ ]中断的上半部处理完后调度tasklet,在适当时候进行下半部的处理
, A, W) { h4 X2 }$ ^! ~7 P6 h7 g6 M5 [
tasklet_schedule(&my_tasklet) /* my_tasklet就是之前声明的tasklet_struct */$ S! w3 \: U7 B3 u' H( N
4 s8 ~1 l) ?3 h8 Y: q* x$ G
3 a+ V4 @! H8 ~7 [3 z0 g2.3 工作队列+ C$ K+ f0 r* p1 H+ ~" F" y
工作队列子系统是一个用于创建内核线程的接口,通过它可以创建一个工作者线程来专门处理中断的下半部工作。% s- e$ S) B6 s V4 x4 i: s! ` H
' _" o" Y! c% J3 e( {
工作队列和tasklet不一样,不是基于软中断来实现的。
/ h6 f' \/ |1 V# K$ R
0 W0 X3 {0 i' U. S! ?% f. E% _
$ ]4 u7 p; O# H' O5 A5 W
) h4 s, p# s: L4 i缺省的工作者线程名称是 events/n (n对应处理器号)。2 l- Z7 C& G: E" h
4 g8 G% D2 y) k6 Q/ ~
通过top命令查看我的单核虚拟机,CentOS系统中的events线程如下:2 p8 N% N9 ^9 J) @: p
( c6 l5 i. ^6 y f6 ?8 ?, S4 Q4 v[root@vbox ~]# top | grep event
3 S5 `2 ` y: d7 c 7 root 20 0 0 0 0 S 0.0 0.0 0:03.71 events/07 v0 ~7 X+ t) m, s2 U% q
% j& ~# L, Q3 @- k
' G1 ]! a G$ i: R% G: O2 W* i工作队列主要用到下面3个结构体,弄懂了这3个结构体的关系,也就知道工作队列的处理流程了。
* G$ y' E& g% g0 U4 W- q: M( `: S0 X) C0 I
复制代码
- ?& |; F' w0 C8 t: ?/* 在 include/linux/workqueue.h 文件中定义 */
. j1 r: n* W& W$ F) J# w/ {struct work_struct {0 l# M$ U, k9 o( \/ J
atomic_long_t data; /* 这个并不是处理函数的参数,而是表示此work是否pending等状态的flag */
! }1 b$ _: o& Q. q7 @#define WORK_STRUCT_PENDING 0 /* T if work item pending execution */& G! w& x9 s6 _/ \; H0 \0 T, `
#define WORK_STRUCT_FLAG_MASK (3UL)
8 i6 z$ \& ?' k* Y; G#define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)
; @* F! b" F& Q' I3 | ^ struct list_head entry; /* 中断下半部处理函数的链表 */
) Y/ R" A! g7 I work_func_t func; /* 处理中断下半部工作的函数 */
, `7 @% M$ M% A S8 l6 \: d#ifdef CONFIG_LOCKDEP
; ~7 A/ r( B' v3 k) N struct lockdep_map lockdep_map;
A, n/ Z( y& W* d#endif
. {: q9 w3 ^5 B8 Z3 ~. R};
: h* s. ` f/ {, u4 O! _" ^8 j2 F* x3 P' k, j0 x5 ]
/* 在 kernel/workqueue.c文件中定义! k4 \7 O2 J& N" s2 Y( E4 K
* 每个工作者线程对应一个 cpu_workqueue_struct ,其中包含要处理的工作的链表" k& y( s1 z! s! S
* (即 work_struct 的链表,当此链表不空时,唤醒工作者线程来进行处理)
9 F5 ~& N1 ]; b+ S7 R */% w9 N7 x& ^0 ~) Z
/*
* H6 z$ W! Z6 ^' ~. o n * The per-CPU workqueue (if single thread, we always use the first
; {" E8 T& V& f* t- h! Y * possible cpu).. g3 g r% B2 y6 r& i3 t
*// o9 X4 @( g* ]3 b
struct cpu_workqueue_struct {; }! y" O7 G$ [ L) i' K
0 R r7 x0 G3 K5 d5 }9 P spinlock_t lock; /* 锁保护这种结构 */6 ?; }! H' ^8 ~/ {6 e: o
8 @9 e* F0 z4 F: u' R( ~% B9 G
struct list_head worklist; /* 工作队列头节点 */
3 D8 Z8 m& i! g4 L/ _ wait_queue_head_t more_work;
+ z+ F, `9 `' q0 h struct work_struct *current_work;8 `; ^: Z5 Y, B9 G' S
0 {( M3 l0 w5 x3 q* a8 g) u struct workqueue_struct *wq; /* 关联工作队列结构 */6 B/ [% B- Y G* }
struct task_struct *thread; /* 关联线程 */) E, g; c* L+ `* V% [9 ^% u
} ____cacheline_aligned;$ t" }: e) k! ]' U
- o* b! S! Q% N k
/* 也是在 kernel/workqueue.c 文件中定义的
4 ?: u$ b, P0 l, i5 R% M H5 A: f' j; k* i * 每个 workqueue_struct 表示一种工作者类型,系统默认的就是 events 工作者类型" v% `+ [% W* v0 N, L
* 每个工作者类型一般对应n个工作者线程,n就是处理器的个数3 Q, z$ P; }- p2 J
*/
- A9 g L1 ?, n$ _/*
) [( x6 l4 |8 q# x" d7 W; ]' M * The externally visible workqueue abstraction is an array of r7 U4 }: T5 i! I! z8 @2 ?0 u8 b
* per-CPU workqueues:' G' N3 _+ ~& w3 ^
*/' ^' _9 `' I T6 W; n! @
struct workqueue_struct {3 W; Q; j; q2 u, D
struct cpu_workqueue_struct *cpu_wq; /* 工作者线程 */
2 |' v: M$ ~( I0 c- y/ g. B struct list_head list;- ^* D4 H5 `" T, C$ a% {. l
const char *name;
( p3 ?, s6 c& \+ |4 o4 h# m int singlethread;3 P5 ?9 k" J$ W1 S
int freezeable; /* Freeze threads during suspend */
0 u- l+ ~1 a. \% P5 S* v4 T int rt;; H6 e' A+ [0 `! V2 h0 [* f
#ifdef CONFIG_LOCKDEP# p+ p/ X# J. A! |/ _
struct lockdep_map lockdep_map;. [! o; I* y6 \+ e2 Q( K& X
#endif
W. H3 w" K$ M9 f" z};
# C' [, H/ }6 u4 s8 b" I8 p: A
. e- A9 i# D! S6 {% H* _
2 V6 _+ }( i+ [( l/ d: I使用工作者队列的方法见下图:& f/ X# V5 `& ]: I
) u1 {3 v ~9 X) }
3 ]2 D- i$ S9 Y5 O! [2 `1 E - \ s# O. K- n! W b) b
% {' u6 C! H1 Y0 b# V
① 创建推后执行的工作 - 有静态创建和动态创建2种方法
: E5 `" X d; c5 _# s$ q# L8 o/ y9 V& L, ~. H3 l6 m* t, e
复制代码
* ^. n% }# \ Y& V" l/* 静态创建一个work_struct
9 ?7 o# \9 n/ O- i# Q" U* l0 C * @n - work_struct结构体,不用事先定义8 _0 R( \( V. L' D! E: V9 ~' Z
* @F - 下半部处理函数7 y- H7 F3 g/ d: X" O4 S2 \
*/3 v: V' L! ~1 a2 l* s2 V
#define DECLARE_WORK(n, f) \1 H, |, o5 `3 P
struct work_struct n = __WORK_INITIALIZER(n, f)
4 [ a; u: \" S( T: }" O% [9 f) }$ w/ x9 X" w$ A
/* 动态创建一个 work_struct2 ?2 C5 y9 F3 J1 b [- n
* @_work - 已经定义好的一个 work_struct
' T! P+ f, l- x# `& |2 P& v * @_func - 下半部处理函数! o4 v# {3 v4 i4 B
*/" r$ M$ R9 u! h' r9 }
#ifdef CONFIG_LOCKDEP
9 B) E0 k- y( ]$ y6 E: J#define INIT_WORK(_work, _func) \; N1 s/ S: Q/ L0 z
do { \
0 C0 g) W" S. l. D static struct lock_class_key __key; \
+ R- D# o) Y4 i; y M8 z \0 g$ v% x% m4 A
(_work)->data = (atomic_long_t) WORK_DATA_INIT(); \
& P6 @, s, [5 c' V: @( v; F lockdep_init_map(&(_work)->lockdep_map, #_work, &__key, 0);\" U* c5 E0 m) c% O
INIT_LIST_HEAD(&(_work)->entry); \
! f# T5 _% T0 n/ a% @8 @/ @ PREPARE_WORK((_work), (_func)); \
/ I2 r2 v" b! q' _# a: h } while (0)
4 @4 z2 `- j5 p M#else4 O/ ~6 q+ ]: q: D q: K9 w+ Q
#define INIT_WORK(_work, _func) \
7 y, ], }( L3 U$ o4 ~) g- K do { \
. P2 S. X/ T) H3 `7 ]! s (_work)->data = (atomic_long_t) WORK_DATA_INIT(); \4 Q( P$ ^( |. e0 g2 U
INIT_LIST_HEAD(&(_work)->entry); \
) y* R' \5 k. T PREPARE_WORK((_work), (_func)); \, p9 E' Y* `1 ?) r' B; E
} while (0)
# M* h/ F# R! b# G% D#endif3 m* i( S6 o* C& P: B
复制代码7 T' Q- ~1 R( T9 s- x
工作队列处理函数的原型:; K; r7 D3 I8 X& p9 p3 E- |4 g
2 m' Z7 \2 l/ B% E
typedef void (*work_func_t)(struct work_struct *work);
- Y; b: C! ?( w3 c! g: @ " @8 r& N6 i& D* c+ W! G
: h0 @( x4 K+ I$ j1 |2 w1 f② 刷新现有的工作,这个步骤不是必须的,可以直接从第①步直接进入第③步
4 E4 _- _4 ?4 z& Q# L/ P2 x2 ?8 x
8 N( u' j" E5 @ i 刷新现有工作的意思就是在追加新的工作之前,保证队列中的已有工作已经执行完了。
; d+ L0 A6 Y1 R7 M; k6 Q: U v8 z# K6 Q7 i i. c
复制代码9 M, i/ D1 Y1 Z6 E# X
/* 刷新系统默认的队列,即 events 队列 */8 B( @$ C, r/ P- V: M: _8 m, `" J
void flush_scheduled_work(void);% }8 u3 }6 _, C
5 {2 K# g# @$ T2 S K/ W) `9 I) {- l/* 刷新用户自定义的队列* G4 b+ y% B, @/ O/ f
* @wq - 用户自定义的队列
6 e, q; r* e/ D+ M: g' h */
0 P/ Z" f! O/ i$ T( fvoid flush_workqueue(struct workqueue_struct *wq);
4 H# d4 z1 }! n6 {复制代码8 }& h5 l. ]. j; Z/ X/ N
$ ^" f) P5 k! b- Z) }
* W5 M: U' c( ~$ d; P. |0 `③ 调度工作 - 调度新定义的工作,使之处于等待处理器执行的状态
& J/ t0 D: c5 n; C X8 A6 d, O# q
2 L( V6 K2 ?' f3 q复制代码- ^/ h/ c6 }9 a3 m! w+ {( e
/* 调度第一步中新定义的工作,在系统默认的工作者线程中执行此工作5 C5 _8 C0 L8 }/ c( k; V
* @work - 第一步中定义的工作 y4 x( u q$ n3 K2 H
*/
& O Q6 k4 c% ~( x- G9 ~schedule_work(struct work_struct *work);2 y: L$ |5 \& N
5 B2 n$ P+ |* I/* 调度第一步中新定义的工作,在系统默认的工作者线程中执行此工作
6 Q* g2 k7 u9 ]9 M% v) d" t* q * @work - 第一步中定义的工作" z5 Q& |8 A3 f4 ?
* @delay - 延迟的时钟节拍% P$ X/ e- [1 I- x+ e; e
*/
9 R/ j$ d7 Q5 M& |& `. }int schedule_delayed_work(struct delayed_work *work, unsigned long delay);
. }2 W5 {2 q$ I( w6 T7 l. ]/ z
" Y9 u! C1 f7 g& O9 g9 _/* 调度第一步中新定义的工作,在用户自定义的工作者线程中执行此工作
1 K; K4 T6 d! \- J& v4 ]$ e * @wq - 用户自定义的工作队列类型
3 Z3 y3 K" k8 w) t1 j * @work - 第一步中定义的工作
! V; s7 w; B" q- y4 h */( s& h' k0 Y: D% Z3 l
int queue_work(struct workqueue_struct *wq, struct work_struct *work);! [1 T' d# \6 [) E8 m$ y
, ]0 C3 {$ I0 `& Q
/* 调度第一步中新定义的工作,在用户自定义的工作者线程中执行此工作
* u. x5 t& r% A( ], B0 P/ E0 S * @wq - 用户自定义的工作队列类型
$ l- g+ s. y, Q1 f9 x; M * @work - 第一步中定义的工作8 D9 ]# W) F! f; h1 s7 s
* @delay - 延迟的时钟节拍1 X5 ~1 w6 e* ?2 P
*/
6 W9 J F9 q5 k/ ?: {5 d$ P4 }' yint queue_delayed_work(struct workqueue_struct *wq,
: h) E1 ^) B1 B: q struct delayed_work *work, unsigned long delay);
& X Z0 a, t! B复制代码3 R2 W! n/ q: y" I9 o4 ]
6 F$ T, W, v: a0 h4 T& c) K, l* o( U4 l W
3. 总结中断下半部的实现9 H! Q8 Y" s, M+ B: U
下面对实现中断下半部工作的3种机制进行总结,便于在实际使用中决定使用哪种机制& j+ m8 H! e# u- n$ q8 o
# ]8 u6 B9 h1 z! S9 f下半部机制 | 上下文 | 复杂度 | 执行性能 | 顺序执行保障 | | 软中断 | 中断 | 高 . N! }3 u4 o7 I3 d/ W! @% q* Z$ X- W& a
(需要自己确保软中断的执行顺序及锁机制) | 好 ) g; u, ^$ R2 x, F8 Z
(全部自己实现,便于调优) | 没有 | | tasklet | 中断 | 中 0 F. c, z) }) W3 J( A
(提供了简单的接口来使用软中断) | 中 | 同类型不能同时执行 | | 工作队列 | 进程 | 低
2 g. j% y2 }* t7 |(在进程上下文中运行,与写用户程序差不多) | 差 | 没有 9 F0 ^' R' r0 X1 I, O5 ~2 M
(和进程上下文一样被调度) | / F0 f, A# X! z b9 m! R
, b5 K1 G' }. R0 n+ G/ A1 M4. 中断实现示例
, d- d3 y/ j2 k# K4.1 软中断的实现3 y5 E8 u& W, |+ v8 c. T
本来想用内核模块的方法来测试一下软中断的流程,但是编译时发现软中断注册函数(open_softirq)和触发函数(raise_softirq)9 f% q9 y! M% b# k* z9 B# k4 a
( p" `) g1 J5 g& q; |并没有用EXPORT_SYMBOL导出,所以自定义的内核模块中无法使用。: P8 F8 `( n' z2 s
. E; h. X- J* Y' K1 X8 j* Y Y测试的代码如下:
" H6 Q; ~* j) \$ q; C7 W7 H
2 u& U9 E+ U& D! {0 `1 _复制代码2 R4 }% \2 s7 v/ E( A9 o
#include <linux/interrupt.h>+ z* ~; C& ^5 w7 `# r k
#include "kn_common.h"/ H. m8 {+ L7 R! q% v1 ]
" w! S0 P# ?; g/ z5 w; J
MODULE_LICENSE("Dual BSD/GPL");
( R4 a C9 P3 h3 k5 P. w
1 M0 @% n, N9 g; k" \static void my_softirq_func(struct softirq_action*);
/ @. { S& h4 H" `0 k- {7 U6 j1 r: H) |* a- i
static int testsoftirq_init(void)' o4 U0 y4 g- E. S3 D/ \
{2 B4 R j- M& R' m: `: }8 |1 X
// 注册softirq,这里注册的是定时器的下半部
. p9 t5 F; n9 [& h8 X" y open_softirq(TIMER_SOFTIRQ, my_softirq_func);* N+ u' t6 W V0 h- r c! x
1 ^: G$ Z) ]3 D
// 触发softirq
) A. I: u1 q) M1 R9 Y" c0 K raise_softirq(TIMER_SOFTIRQ);' v8 W* R1 M6 e# r; K
; J7 f; W- X3 ?, x
return 0;
# e5 l4 g$ N+ }5 k7 M. U4 @) x
. ~' ?* Z6 T4 _" J/ l! V- ^}7 Z8 A% j/ b1 U. w9 ]
) G+ X, v3 s; v' ]6 m7 \" Xstatic void testsoftirq_exit(void)9 X: i8 q* U4 N I7 x
{# n" C- E4 K7 u0 f3 O
printk(KERN_ALERT "*************************\n");* N n- l& L' m
print_current_time(0);
( A% N( ~( ?. s- ?3 ? printk(KERN_ALERT "testrbtree is exited!\n");
3 L6 l' Y# X" q0 n6 h3 X0 W. A printk(KERN_ALERT "*************************\n");
/ w; R. L8 T7 |* ~5 _/ H 2 H/ _8 L, q" k% N9 K
}8 t {- y$ \ _ p* D% u
}( U. f+ C% B" e0 d3 h6 m
static void my_softirq_func(struct softirq_action* act)
5 r- O- n. i8 p' x- w% @{. F0 g( ~4 }# O1 Y% e
printk(KERN_ALERT "=========================\n");
* X# p5 ?: q$ O3 W7 x, ^* D7 a print_current_time(0);: W& `( ^* C+ s1 [. N
printk(KERN_ALERT "my softirq function is been called!....\n");0 ^( ]0 h0 n7 u( ?9 g4 Z
printk(KERN_ALERT "=========================\n");
' R1 O3 w7 I' H}
5 @& _9 k+ m" U4 h3 n. Q, }0 o- i, Y" x7 |4 _) p5 i9 ?' g" C% h
D+ k5 \5 w) p9 q' x6 S
module_init(testsoftirq_init);
9 _- k" S# U- Zmodule_exit(testsoftirq_exit);
; e% E/ m3 \+ m/ P
5 S9 ?+ \' q2 x8 H由于内核没有用EXPORT_SYMBOL导出open_softirq和raise_softirq函数,所以编译时有如下警告:% ]! |' ~: a" A0 ]
4 y5 H) G4 T/ I! m7 r+ u% UWARNING: "open_softirq" [/root/chap08/mysoftirq.ko] undefined!3 L' o8 m1 M: s7 P0 ]; r3 [/ Y( b
WARNING: "raise_softirq" [/root/chap08/mysoftirq.ko] undefined!
9 ]5 [# U c1 U! R2 M8 C4 `- m9 ~注:编译用的系统时centos6.3 (uname -r结果 - 2.6.32-279.el6.x86_64)% D0 z" `7 y1 Y: |* n
) u+ n) j2 o/ @" l: U" g: _! N / e5 j4 @8 r, U) K
. \) I7 S1 {0 ^7 A6 u1 a: s( F b! b没办法,只能尝试修改内核代码(将open_softirq和raise_softirq用EXPORT_SYMBOL导出),再重新编译内核,然后再尝试能否测试软中断。4 p& d' d! X! ?4 `
" I* O) @: u: M. c1 u. [; v
主要修改2个文件,(既然要修改代码,干脆加了一种软中断类型):+ F0 Y6 J4 E, k
4 Z9 k) `8 h/ l, C
复制代码
- L U2 K$ \. T9 C. {( D/* 修改 kernel/softirq.c */
- n. y# [; X- k// ... 略 ...
9 t5 p' h8 }6 y3 Z, {7 B) U8 Lchar *softirq_to_name[NR_SOFTIRQS] = {7 G8 L. y' t) X; M. U9 ]# Y; p( Q
"HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", "BLOCK_IOPOLL",
7 o6 a | ^5 G3 d b "TASKLET", "SCHED", "HRTIMER", "RCU", "WYB"* V+ @/ a8 [ ~ i* Y' o8 Y" O# p0 {. ?
}; /* 追加了一种新的softirq,即 "WYB",我名字的缩写 ^_^ */
, ` B5 N; J8 s" `7 G* N k
$ ^* R7 i- x3 }1 d// ... 略 .... [# H) Z& J3 ?% ~/ J* J
1 s: p/ ?1 |1 Z* [; mvoid raise_softirq(unsigned int nr)
& l* ?6 T/ X! p/ |) y f& ^1 a{& i# q* K' R% E# |! l/ S7 R6 d
unsigned long flags;, a9 y! L7 ?* \0 ?) w+ C: g5 d
/ o6 ]0 a& E# Q5 ]% A" O! }) m
local_irq_save(flags);' S: d6 ?3 m2 M9 c
raise_softirq_irqoff(nr);
' P, v; p9 {( k- V5 R local_irq_restore(flags);
5 s; v3 e$ V. k}
' C5 X/ o# ]: s$ AEXPORT_SYMBOL(raise_softirq); /* 追加的代码 */9 l0 y7 y* \# S6 G
* m$ E% t" q- T( s+ i3 S E
void open_softirq(int nr, void (*action)(struct softirq_action *))
) M; g" H; @$ D# B{5 m0 e; R$ b3 ?: s/ V7 D3 P. W
softirq_vec[nr].action = action;
9 D7 t4 n( o4 }# b5 }* n- k. Q. B}
0 |- w' ~4 _% a' ? v0 j9 BEXPORT_SYMBOL(open_softirq); /* 追加的代码 */ _! w+ J5 s0 m6 \+ D
6 O8 S$ N) v. Y& F
// ... 略 ...! D3 X- e, H$ i$ ~
& _) `3 q- F9 M$ ^: J% [7 t, H f' ?& m8 V3 g5 W
/* 还修改了 include/linux/interrupt.h */& X& N4 A Y3 b& W& h
enum
- I- p8 w# z) P) W& Y5 P{
8 \' M8 K0 P* J HI_SOFTIRQ=0,
, ~" `8 V1 l7 N TIMER_SOFTIRQ,5 u8 L! }1 W% O6 }
NET_TX_SOFTIRQ,7 l/ j# |/ Z! }8 ~, j) U
NET_RX_SOFTIRQ,
1 Q, a4 ^1 T1 f/ {- J, v BLOCK_SOFTIRQ,
& ?& h6 b$ [- d0 q7 a BLOCK_IOPOLL_SOFTIRQ,
7 w' g3 z0 a( j2 c4 i+ z4 ^ TASKLET_SOFTIRQ,/ P: u. [% B+ |% [7 A
SCHED_SOFTIRQ,
" {+ C; p, `& A# W0 m HRTIMER_SOFTIRQ,* e4 p' v( b& L! X+ J- ~2 C
RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */5 y" [& n1 ?- c0 D$ A. @
+ a. i' [, e# b3 d$ l; U; u
WYB_SOFTIRQS, /* 追加的一种中断类型 */' @" o* N3 @! ?" G' ?2 B6 T" r
NR_SOFTIRQS9 |. R$ w* a( H" U+ ?. ?1 ?
};
3 j- J6 S4 P" f% B4 V复制代码
3 t$ z8 c. _" y" K重新编译内核后,在新的内核上再次实验软中断代码:9 C3 p" x+ {: I4 Z
0 t$ y! y; Y, U测试软中断的代码:testsoftirq.c# Q; q% O% N7 f
' U4 O/ M% A9 f' E3 n复制代码. K) n5 I9 f; Z5 W- X1 E
#include <linux/interrupt.h>
z9 D6 o) B8 |( A. g6 ]#include "kn_common.h"& w$ N. F. |- ]( s, e/ Q$ Z
7 a6 T5 N; p3 Y- b% S* ?8 p8 n
MODULE_LICENSE("Dual BSD/GPL");+ [) K8 ^: _% U; u4 F. i3 ?
; l( ^: h+ O" x6 t: N+ Q) b/ ~ lstatic void my_softirq_func(struct softirq_action*);3 Q: v) T1 L: M; Z# F" W, Y2 S
6 Q5 B8 y! G: j% i) O& g
static int testsoftirq_init(void), x" z1 ]& }4 E$ v4 U$ t
{; }+ h& D7 w' G2 D0 ]
printk(KERN_ALERT "interrupt's top half!\n");
7 t+ y0 R/ h z6 K- c! r& p ' d: I. {6 U% g* t3 d7 c
// 注册softirq,这里注册的是自定义的软中断类型
6 K0 r. Q: W; ^+ C open_softirq(WYB_SOFTIRQS, my_softirq_func);
, c9 N" G, O& x8 _5 ]
3 o+ v" E3 z. r4 R4 h // 触发softirq+ f. G$ ]" A$ o) |0 n3 @$ ]: M1 t& I4 o
raise_softirq(WYB_SOFTIRQS);
3 i& k% ~/ `9 M/ z# Z
& Q5 W0 c' i- ~+ c return 0;
/ [; I2 W8 C9 \) z' J' H& c
7 }& v* {3 Q" s' I3 R}
$ P3 c( O; ^) l! x- Z% y( M' w X$ {! m
static void testsoftirq_exit(void)
6 m$ l# r7 T- s! L- d{
" ^# I) k5 x& o/ p, F printk(KERN_ALERT "*************************\n");- E6 e6 C- c1 k2 _+ p) }# ^2 z9 V( d, ]
print_current_time(0); f; p2 D% K8 D% R' ?( g, U
printk(KERN_ALERT "testsoftirq is exited!\n");. O! R! M( P `3 R- P' O
printk(KERN_ALERT "*************************\n");
) \4 e3 C, U' P
) u0 N9 T5 t6 Q* w# I' F. n! W6 S}- w- s/ a9 {; Q% [1 d
; y5 o( C6 K) ]2 M0 f7 kstatic void my_softirq_func(struct softirq_action* act), L* h7 C# a2 a" }7 w6 ?! @
{; O" w4 l, G1 F% Q1 n- I$ R6 N
printk(KERN_ALERT "=========================\n");
1 @* I* ~7 L! n/ t8 _ print_current_time(0);0 e8 V7 Y( V# H2 H
printk(KERN_ALERT "my softirq function is been called!....\n");% H+ f% u* X! }
printk(KERN_ALERT "=========================\n");
4 E, t+ O2 W' b3 ~* Q}* N O, X n; u! f: _
" j, r/ Y! m4 s
, ]; h: t6 w$ b" \7 h2 Q0 C
module_init(testsoftirq_init);( v* A( W; _! {5 X+ ^! l. k
module_exit(testsoftirq_exit);9 o( S! M% i/ N2 A9 P/ \% P
复制代码
$ l1 S3 _( u9 S/ N& w 7 m1 ]# b, e" d y$ L2 m, |
$ `* s. ?, Z: E! |2 V" \! T- rMakefile:0 n. T5 q- @' ?4 X3 d/ F
7 {2 X/ M! l5 X* W1 F! L1 x复制代码
5 w, n; y( X- t2 Pobj-m += mysoftirq.o% }4 b. |# y4 r* W! [
mysoftirq-objs := testsoftirq.o kn_common.o
) b. o2 y, Z4 m Z% u, N4 h9 p" P" e# V5 i1 W4 q
#generate the path' \9 s* t. A' }( [
CURRENT_PATH:=$(shell pwd)
6 c) }* Y9 c3 C& D+ v$ P9 g#the current kernel version number! n: X g Y$ A8 k; ^2 u
LINUX_KERNEL:=$(shell uname -r)
; K1 G" q1 ]& d$ k) y& i' J#the absolute path2 D1 k, ]+ @4 R; e2 s6 q2 @
LINUX_KERNEL_PATH:=/usr/src/kernels/$(LINUX_KERNEL)
& c' F' E* l. h4 l#complie object7 c' U+ r! B5 q
all:
7 K8 v0 H' d* M# |- n7 c- g make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
, U0 a2 ~6 Z0 Y1 j( W rm -RF modules.order Module.symvers .*.cmd *.o *.mod.c .tmp_versions *.unsigned3 E v9 D# M* j! C4 `
#clean
& g. a# X3 x7 F/ |9 qclean:$ Z. r; T& g- ^+ {) ?2 z) N
rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c *.ko .tmp_versions *.unsigned
& u8 |9 r/ A T& ]复制代码
0 D+ s" \3 ]1 t- w1 N6 { 6 d4 q z' H' f1 `! c* p
1 a A& q5 X; u: c7 @3 \- h" l8 F4 q测试软中断的方法如下:* n( O) k& Q+ a( m; g- F i( H
) ?, [3 t. \+ |) }, o复制代码4 o( e' ~' @3 y, J; Q
make
2 B, m7 k. f+ T0 {, i8 Pinsmod mysoftirq.ko5 D( O- O5 @' f2 N
rmmod mysoftirq
7 a- L& ^+ |: I" F1 O, tdmesg | tail -9
0 p) ]: i5 x( q1 S( Y, e+ X. V" `5 Y: S, q: v
# 运行结果' h9 a6 d6 L( X2 q( [# u
interrupt's top half!
1 m H3 W$ l" ^5 ^2 b5 ?=========================
6 V% H( y5 @' a5 I' S* c6 ^* j2013-4-22 14:4:57
5 h* }4 x) n/ {, I+ d& Bmy softirq function is been called!....9 [+ f5 h; `6 ^
=========================0 I/ q6 }- b8 y. `
*************************" j) ]$ [; l& x
2013-4-22 14:5:29 M v5 R) m6 Z& G
testsoftirq is exited!
% [; }5 @; o6 j* E/ A# B*************************
9 H8 L- O+ @: P6 ]( Y L% J复制代码
3 u1 \& J# B b5 U- S5 j, ^: n2 s $ Y# Y3 a1 U: I& l6 ^! |5 F
" b3 E! b, i3 H9 h$ s5 h- {
4.2 tasklet的实现
; x1 n2 X' G2 B8 |7 p" ^tasklet的实验用默认的内核即可,我们切换到centos6.3的默认内核(uname -r: 2.6.32-279.el6.x86_64)( A, C' F) c1 C. S6 S/ U
! d7 m X* w5 W j8 n$ O4 Y从中我们也可以看出,内核之所以没有导出open_softirq和raise_softirq函数,可能还是因为提倡我们尽量用tasklet来实现中断的下半部工作。' B; u9 @7 ~% D/ A% m
1 ~% S4 P+ l" Y: O: {9 P
+ H1 t7 O a( v7 h: S, N- Q( @9 }, ~7 O, L# h) U
tasklet测试代码:testtasklet.c: g; N: K3 O5 l" e1 c- B. b
! u. m% s; E3 u ]. \% B5 I# S
复制代码
8 x' [$ Z1 ~, O, C# T. p8 O6 l# ~#include <linux/interrupt.h>, n6 v& [6 K$ F9 a4 w$ G" O; L
#include "kn_common.h"
# m0 X( V+ {* n& r+ l4 M2 G9 f
) u2 ~* w% [) O7 S& j. w6 zMODULE_LICENSE("Dual BSD/GPL");
" d) ^- n6 s- X% a5 C
, S7 b# L) B$ \4 bstatic void my_tasklet_func(unsigned long);
) s# }4 M. N2 N c& V
( S8 G8 T' ~- q% j0 d1 V& X/* mytasklet 必须定义在testtasklet_init函数的外面,否则会出错 */
1 e( p* Y' s6 f7 b- x5 V% S7 LDECLARE_TASKLET(mytasklet, my_tasklet_func, 1000);/ g/ K8 g/ k5 R9 i0 E
! ?8 L* B, z0 w, q" ~( W) Z" A9 Vstatic int testtasklet_init(void). P7 [$ X6 G+ g" R, y" Z
{; p1 k4 T) A% W: B E C
printk(KERN_ALERT "interrupt's top half!\n");3 J. S; \6 _+ t9 R
* ?. @& d1 w- j' C$ Q* }* f) j& T
// 如果在这里定义的话,那么 mytasklet是函数的局部变量,
+ C2 k E+ U, C) K2 x. w; L // 后面调度的时候会找不到 mytasklet9 n6 ^3 _9 Z; y& P& {
// DECLARE_TASKLET(mytasklet, my_tasklet_func, 1000);9 u* E, B* [6 X4 n4 q3 K: B
K J( j# b3 u7 W2 t // 调度tasklet, 处理器会在适当时候执行这个tasklet/ i9 x8 ]; X& E7 [0 v
tasklet_schedule(&mytasklet);& F$ B; n: z0 M5 ?1 \1 n/ _$ j
, Q/ J+ G. Q- f4 c5 C
return 0;
9 ~# T5 m5 v1 [0 E# p+ L 8 s1 P8 V" E/ A& C" p+ B8 l0 ]
}& G. Q( m: w' K( u" d% L
2 Z- C6 Y# V- C8 D+ I* E9 Q' gstatic void testtasklet_exit(void)7 m8 z3 x) T* u) p+ W$ e0 U/ ?% c
{
& D, J: P: q8 J printk(KERN_ALERT "*************************\n");
8 a, m3 G% n4 L* X9 ]4 ~ print_current_time(0);$ V5 }$ E4 i" t; o6 p r d
printk(KERN_ALERT "testtasklet is exited!\n");
3 z6 y* }5 g O L# y printk(KERN_ALERT "*************************\n");2 n- O ^- {2 @' C9 A. Q, B1 G( h
) F% R) U6 [1 D7 V) K8 U0 z}
/ K8 l5 E( u; l6 I5 a# `
5 q1 t, W4 Z/ N7 Astatic void my_tasklet_func(unsigned long data)8 @, P: z" V& V. T8 t
{& }- S" ?5 Z' O7 s- C
printk(KERN_ALERT "=========================\n"); `: t6 c; a' M4 B
print_current_time(0);$ g/ r J9 Y1 `* Z) e' g; y
printk(KERN_ALERT "my tasklet function is been called!....\n");
) H7 l9 [5 b8 l printk(KERN_ALERT "parameter data is %ld\n", data);
/ b# H% j) G/ i& i/ I printk(KERN_ALERT "=========================\n");! \" d8 x# S6 @5 L
}1 ]5 s2 ~0 b6 [
3 U: b" F2 q: ?+ j: w. D* k" N# k0 v- ]! ]" X
module_init(testtasklet_init);) l% R" s7 Z5 H4 m7 J/ p: j; L
module_exit(testtasklet_exit);
- j9 V* Z* ^, G) ?1 W9 ?& J. l S复制代码: y0 G; g6 V( G
7 y P G& t3 T$ ~
) J' G: ^( O- x+ T+ V2 wMakefile:& f1 e$ Y; a: j6 J, Y+ V
5 V: f$ w1 {% s. \; Q7 u9 x
复制代码
3 w6 T' M6 p( `obj-m += mytasklet.o& o' c. a$ Q( k4 x5 {
mytasklet-objs := testtasklet.o kn_common.o- x( D# v1 M+ w! M5 ^/ k
- s$ C- V$ g( u4 ]2 Q* {, o- g#generate the path
2 H! G/ t- h' n) d- v3 M. Z$ @* qCURRENT_PATH:=$(shell pwd)
8 f0 `! f8 |/ ?#the current kernel version number
) ?8 D1 O8 ?5 r, H1 CLINUX_KERNEL:=$(shell uname -r)
" r* ^: K3 S8 [( n% [2 C#the absolute path7 a! d7 Z2 g4 Y2 h! {
LINUX_KERNEL_PATH:=/usr/src/kernels/$(LINUX_KERNEL)- y) X3 S9 k3 ]- A7 o: W& z, V$ _3 ^4 e
#complie object5 j0 i4 Q8 I# X0 k% q. D% Y& ^! v8 B. ~% F
all:( x5 i( s- M/ y3 K) a
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
2 |! R3 y1 Q' S) W# h8 P% ] rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c .tmp_versions *.unsigned
' g0 M7 {' f% u* @5 R#clean
: m! l. _3 W5 g, Nclean:/ Y$ y& o$ ~* I1 R4 m1 q
rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c *.ko .tmp_versions *.unsigned8 _+ F4 `2 s7 \9 @1 N
复制代码6 `: G' ~( X( q; c
# n/ q! o5 T E5 |! K0 {& s4 A
测试tasklet的方法如下:( `- _& M* S8 G) s3 _
# Z% m1 L/ H, M$ H# S复制代码
0 p# \; X/ r ]' pmake$ q4 Q" w# J1 j) M5 Y6 P5 i
insmod mytasklet.ko
$ L. l5 v( z- prmmod mytasklet
, y3 g" P3 Y* L) q$ S, xdmesg | tail -10
- h. y+ E6 ]/ c/ w, w, p# T# f; a3 \4 F
# 运行结果( v2 G# i6 W( A5 ]
interrupt's top half!5 a) j' `/ p1 v5 I" [. x1 b
=========================: |2 R! R" ^- A( @' W
2013-4-22 14:53:142 t2 d( G3 ^$ f/ A
my tasklet function is been called!....
( m- [8 H, Q) j3 W ^3 l5 C" Mparameter data is 1000 A# a3 a. L5 T
=========================
' o, N/ @) X Y( O: q*************************
. z+ e, P Y6 e2013-4-22 14:53:206 h# P2 g5 z) W2 z' V0 B9 t
testtasklet is exited!( Z. A2 h; [* q; [
*************************6 h0 I: o9 Q3 L* q E1 } m
复制代码9 E9 R+ r4 Y0 M- J f9 B
9 B0 A& S- Q# K* t2 [6 m; G$ K, U2 H3 O
% B7 I' W* N* ]5 m3 X" o4 a U+ s4.3 工作队列的实现
6 o }: E8 c3 qworkqueue的例子的中静态定义了一个工作,动态定义了一个工作。
* I% n4 v" r( c3 l& t' G# _/ ], N
静态定义的工作由系统工作队列(events/n)调度,
6 G2 m4 l9 M' w8 m5 P1 Y. c# |1 x: j; r
动态定义的工作由自定义的工作队列(myworkqueue)调度。
8 t; w. o6 p, L0 y: ?- v6 A% V5 s1 N0 o
: {9 W8 c# [! Z2 T* X2 E6 z, g$ V$ S7 K" Z! r1 @; k
测试工作队列的代码:testworkqueue.c8 k k6 p; p" j. k9 m
: t( z9 B) h* l: S4 Z" E复制代码3 q$ ]% D% i4 [( \7 O$ P7 P
#include <linux/workqueue.h>
" Z! v4 X9 [, `( { s; s* L8 J- `. A#include "kn_common.h"
3 w) [) P+ j; l) q( }2 v3 ~5 Q! ]7 }3 V0 N$ v
MODULE_LICENSE("Dual BSD/GPL");/ D0 F" t1 m1 A: Z9 Z
! x, [+ d5 D! `) |$ c1 d; t
static void my_work_func(struct work_struct *);
5 {4 }$ G: _1 T6 j+ n% h+ a* u2 Mstatic void my_custom_workqueue_func(struct work_struct *);9 }& Y1 U7 a) @2 K& N) s
7 [; \% s/ A, B& d0 z% O
/* 静态创建一个工作,使用系统默认的工作者线程,即 events/n */5 g; @5 \" n% i. a, S \ B% ?
DECLARE_WORK(mywork, my_work_func);
}9 [! `$ x( b8 h7 H# w g; G' @/ l: o' W
static int testworkqueue_init(void)" q5 e' x: g( s7 I* c
{' S, i6 m3 N4 f5 O& E, b* Z
/*自定义的workqueue */$ S- n% c8 b* T3 J
struct workqueue_struct *myworkqueue = create_workqueue("myworkqueue");
( t, a1 k7 L3 o3 ]) m* Y8 O" K1 j! b- {" w) d/ e
/* 动态创建一个工作 */
g/ y b% s! y8 O! e+ K struct work_struct *mywork2;
; e- V, D" D! _& J mywork2 = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
7 ~" _5 D) C+ Q e" Y7 F INIT_WORK(mywork2, my_custom_workqueue_func);9 k0 O* P* V' N( L* s0 H2 E
3 Z* R! H+ y) ]4 @4 I) v" c) b printk(KERN_ALERT "interrupt's top half!\n");9 l4 P0 M" R' N) G! E/ E" r' t% _
/ }" i3 p# h/ I# `
/* 刷新系统默认的队列 */& C9 A& C1 r* ^, j
flush_scheduled_work();/ f. O1 ?1 I5 p6 u8 x
/* 调度工作 */- @/ S% ]4 f/ J+ r( I r
schedule_work(&mywork);% D( W. @- w- Y G, z
- n5 H0 g7 F4 y; J /* 刷新自定义的工作队列 */
+ n I$ @# D, |/ ?% h flush_workqueue(myworkqueue);- V& ]$ d, f( J. K$ Z9 c+ e; ^
/* 调度自定义工作队列上的工作 */
$ s& d* X! i5 I* {2 ^; n queue_work(myworkqueue, mywork2);8 Q# e, T* q- r% P
# n+ Q) T4 \& s return 0;
1 z& _" M T+ | H. I}
) M" S" V2 w+ j' a7 A1 X! Y ?
9 Z [. }8 W, T% [# astatic void testworkqueue_exit(void)
6 v) i3 d; L3 X$ k$ f2 P{
1 V% k* z N3 B; W( z) B printk(KERN_ALERT "*************************\n");
9 y& Y3 f1 i. r; S/ |4 s print_current_time(0);
) L' b8 T$ @, S: V: Z printk(KERN_ALERT "my workqueue test is exited!\n");0 _1 X* ?: M# i5 v! F1 c* x |5 z5 v
printk(KERN_ALERT "*************************\n");
) U; Y% v. U* ?5 l- a0 m % `9 E5 ~5 J* A' L1 n+ ^! y# o
}
' r3 r. C" p$ v8 f7 W( s
+ N" ]6 V- o) L! zstatic void my_work_func(struct work_struct *work)* }1 c% c5 Q6 O& G6 }/ j7 x1 v
{: W2 ]9 G$ F8 |. b/ u- o
printk(KERN_ALERT "=========================\n");# D R" p+ k- D( x6 ^9 K6 [* w# b4 Q
print_current_time(0);- r) u$ U* T& s+ a
printk(KERN_ALERT "my workqueue function is been called!....\n");' d. H3 j" m8 b$ i7 T" f
printk(KERN_ALERT "=========================\n");
0 r. L3 T$ s) P}( d: G, g7 `+ O" Y
% j8 n( E) Y/ F4 C! Q& n: Lstatic void my_custom_workqueue_func(struct work_struct *work)
9 {; I' y ?. t0 O( a1 X9 z2 i{
* z1 d* d% m$ I0 w, F printk(KERN_ALERT "=========================\n");/ m! u: P. }7 l7 c) E# P# h0 z
print_current_time(0); Z1 W* _5 B Y; `4 n
printk(KERN_ALERT "my cutomize workqueue function is been called!....\n");$ A4 i; G/ e g
printk(KERN_ALERT "=========================\n");
+ P% X: X- D4 L kfree(work);3 }1 r5 A7 b2 q
}
2 U/ [4 v4 {& U1 z* L, y7 T5 q# H; m$ P6 ]
module_init(testworkqueue_init);% F- ]" V D# `/ O
module_exit(testworkqueue_exit);( L# I+ Q7 s) r! g3 D6 q3 B
复制代码+ y! E' e/ h3 `, C& k% q
8 D, V& P5 `' T
- _/ ]% Y; Q' Q. p5 ^1 nMakefile:! s1 @ I$ z' L; a* U( m6 x8 Y
& `7 F+ H$ X& r9 _! ]! T复制代码
2 B/ w- W0 `$ mobj-m += myworkqueue.o$ n& u0 r) t+ A" h* w7 N- F) @
myworkqueue-objs := testworkqueue.o kn_common.o
$ S$ u3 \( J X0 \6 B* t
" ?5 S( {$ ~3 f* g; i5 S( @& l#generate the path$ a+ c% K3 d: J$ y
CURRENT_PATH:=$(shell pwd)
9 p8 I$ G+ B* Y! ?! V' h#the current kernel version number
: O$ x9 Z3 z# ELINUX_KERNEL:=$(shell uname -r). R- P: N& f& E& d+ g; U
#the absolute path4 h& z) D6 q/ H: U
LINUX_KERNEL_PATH:=/usr/src/kernels/$(LINUX_KERNEL): j6 K e- x: a+ P
#complie object
7 C3 G' d+ H+ S; n& J8 x9 p9 }all:
- l- P. L; ] O7 E& C9 P make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
- E7 Z b( y3 `- c rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c .tmp_versions *.unsigned
- ^$ C% L: q" G$ C: ?5 A! L1 o( a#clean( T o7 M- D4 I r
clean:
) a4 u/ }$ [- O rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c *.ko .tmp_versions *.unsigned# i) F+ E) J$ ]: `. {9 L2 U9 X
复制代码; D) D. c; B! z6 J, a% y6 P
]- X8 ]! |& ~* ?7 P( H
( j! c7 h I4 K2 z5 O+ B) }2 u测试workqueue的方法如下:7 d5 t, n8 v5 @. h7 K$ W5 Q
+ G# X; k& D7 R复制代码
: z4 h. x, I9 e C: f$ Wmake
; [7 x/ L4 Q5 z: y) Oinsmod myworkqueue.ko; @- ^3 U+ g+ S% d9 W5 _6 Q9 o
rmmod myworkqueue( R0 _% ^/ F1 c: d
dmesg | tail -13: v: O0 _5 O3 h7 B# ? X, C) V% Y
; v+ s( f" k. X
# 运行结果2 I, u) _8 J8 Z4 \" G2 H3 z- s
interrupt's top half!) j+ E3 h2 v* B8 }2 h2 a( }( n
' v. {7 E6 c) o. v) @ |
|