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