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