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