EDA365电子论坛网

标题: Linux内核设计与实现之中断处理 [打印本页]

作者: pulbieup    时间: 2020-11-12 15:20
标题: Linux内核设计与实现之中断处理
本帖最后由 pulbieup 于 2020-11-12 15:45 编辑 0 X. z! R2 b1 Z! E

! a$ v# w0 d8 Z* y中断处理一般不是纯软件来实现的,需要硬件的支持。通过对中断的学习有助于更深入的了解系统的一些底层原理,特别是驱动程序的开发。/ R; P9 u6 I0 s* ?$ a

0 f$ P4 d  \0 i0 f# R1 y8 v主要内容:+ ~) n6 |+ m* O9 p2 }; d5 H
6 h  n% p5 n+ _; w+ L) I
什么是中断
, G/ R* b7 x: O- P) Y中断类型
8 e  v1 `2 `$ N( p  f0 d( n" B中断相关函数
) H+ n' ]$ L# x9 J& k中断处理机制7 k, e' Y/ a% W: ~* \
中断控制方法4 o" \( s: f$ I; P
总结4 {4 a% \! ~; I' O8 Z3 j2 i# }

* b! a  f- g6 F" w# L4 `: h3 E; z- V7 n2 E. T! I/ N9 x0 R
1. 什么是中断0 f% l. k  _) l
为了提高CPU和外围硬件(硬盘,键盘,鼠标等等)之间协同工作的性能,引入了中断的机制。
1 Y" j! p2 m* F$ a( f; h5 `' D; m. B$ c7 _2 |/ _* s" ~- E8 M! H
没有中断的话,CPU和外围设备之间协同工作可能只有轮询这个方法:CPU定期检查硬件状态,需要处理时就处理,否则就跳过。
) ~3 g$ o- k) s
" s& l, g' Z2 d$ ]% M当硬件忙碌的时候,CPU很可能会做许多无用功(每次轮询都是跳过不处理)。
" e: g1 ~9 c& q& K8 K. E
/ z% ?- S- w/ O# v7 c8 I" h8 z
2 M& K5 f( J* C& _/ h. I& |% x5 q. m5 W! ?$ ?3 ^
中断机制是硬件在需要的时候向CPU发出信号,CPU暂时停止正在进行的工作,来处理硬件请求的一种机制。7 u' U7 _, T1 _: M+ L  p( H/ t! H
' Z  Y8 `: W# F9 k1 K
& v/ ]+ z" c9 i4 Z) p- s! H- @

% u" a4 S7 t4 I) O  F! _" ^1 x2. 中断类型
: p4 H4 z$ n( j( \* H) `' }: G中断一般分为异步中断(一般由硬件引起)和同步中断(一般由处理器本身引起)。
8 S0 f$ g& ^* O: Y
: P( }0 e; N5 `3 s- P3 w异步中断:CPU处理中断的时间过长,所以先将硬件复位,使硬件可以继续自己的工作,然后在适当时候处理中断请求中耗时的部分。! T9 y/ j9 H; Y& n' ^
6 y8 ~( r7 Q* V8 I3 O! T3 L# }
举个例子:网卡的工作原理
1 |5 @; U# u. y* t# V$ a2 M/ ]5 R$ A1 d# y, T3 v* N% q/ C
    网卡收到数据包后,向CPU发出中断信号,请求处理接收到的数据包- ?; f: E2 o) H. E# r# @3 F* g
    CPU将收到的数据包拷贝到内存后,即通知网卡继续工作
" u' R! K  C+ i# m" Q    至于数据包拷贝至内存后的处理会在适当的时候进行: |( J, A9 {& V; e4 n
# O% D3 C0 b3 p, l- K+ z

8 Q8 \% c( z" t# C9 X3 M4 b2 R这样做避免了处理数据包时间过长导致网卡接收数据包速度变慢。3 {6 S! B0 l9 z& t4 S& u

, J& c1 a: i6 d $ u9 X+ m! D6 K, h5 }; v
5 H/ Z1 `% \2 r& @- Z# J
同步中断:CPU处理完中断请求的所有工作后才反馈硬件/ @; G/ J* g0 J& }

' d6 k: |4 j2 Y  e0 j7 F( p4 D举个例子:系统异常处理(比如运算中的除0操作)
$ t; s- C" w* _! [) u1 [
' `& C; f0 i* m3 f8 T: D/ X    应用程序出现异常后,需要内核来处理
  T; Q. ^. F. C) ^    内核调用相应的异常处理函数来处理异常% u, c& ]' u$ k; p! S' f
    处理完后终了应用程序或者给出message% u# L  u2 |8 M3 @" x; y" I
/ j, ~1 q6 c+ B$ O* q2 Y# U
! T7 k' y6 p& J' i2 T
同步中断应该处理能很快完成的一种中断。3 B1 O% Q9 A& h. O* L7 h4 [( t* X' f
  M% x( |* _% F+ X8 [+ b& Z* n

5 Q0 R# O7 I3 `. J1 ?
+ ~, \1 N1 _; D0 Y! L3. 中断相关函数
  [, \. k0 m7 T实现一个中断,主要需要知道3个函数:
" t4 a$ t' P' f- X3 N, `0 I
0 @1 n$ a2 d" Q2 q1 _7 U1 n注册中断的函数% h2 Q7 J  V5 O
释放中断的函数5 i: y" l# u4 l' ?" ~
中断处理程序的声明
8 [% K, @2 G" h+ L 6 a5 z* Y+ d3 B3 Q; _
( m. w) v8 z. E. P, N+ s* m$ a
3.1 注册中断的函数
, g  B. n+ [! D4 ^9 N8 v$ ?    位置:<linux/interrupt.h>  include/linux/interrupt.h. F  ]/ I/ ]9 @! }# t% e% k, d  W$ V
9 |: R9 h1 Y5 P+ D/ {

, i, e3 |5 C3 p3 U' I/ t1 H) m8 {8 F8 B9 Y& X/ O' K8 Y
定义如下:
% [1 W: R. H  |% O; I4 ?: i' G, C$ i* r/ S3 |* U4 @/ a  j# n! o
复制代码
1 S, N9 H. ^' s# e, ~7 D$ K" n8 K/*. }0 `+ w# C% [5 a9 o, J0 o0 g" a
* irg     - 表示要分配的中断号# k* o( c1 ^; g# H: W! ?" T: [
* handler - 实际的中断处理程序
, p- [  b( G; Y$ S( G& U * flags   - 标志位,表示此中断的具有特性
* B7 B4 Y) G5 d! Z9 `! h3 y8 W * name    - 中断设备名称的ASCII 表示,这些会被/proc/irq和/proc/interrupts文件使用
% f/ I; [8 S, E$ E4 { * dev     - 用于共享中断线,多个中断程序共享一个中断线时(共用一个中断号),依靠dev来区别各个中断程序
! E8 P) u6 F8 W9 p/ k5 N * 返回值:
7 M4 Y  K. B' X * 执行成功:04 O& C# j- V* Q1 f, f8 p
* 执行失败:非0
3 I+ |& _& C  \+ @  U6 e: j9 O */4 q, C4 P& O9 s- R4 ~( l
int request_irq(unsigned int irq,
2 J) N! x. U. x. X                irq_handler_t handler,
3 O3 b$ ?# w1 f                unsigned long flags,
+ L( H* K1 y' y. \- \0 k                const char* name,
. a* _' y9 S! P5 B: G                void *dev)' P- \( f/ d# ]
复制代码
+ u/ @6 h  Y7 e' Z) ?+ F + s' x: c/ R- J. A8 W/ g; l

9 {: G# o$ j) j$ E3.2 释放中断的函数  h5 E" l/ r6 s. f1 j8 f) o
定义比较简单:& X6 k* g& G' \) c! U; P+ i

' ^6 T& c" |: Dvoid free_irq(unsigned int irq, void *dev)
+ k- V" |* H2 J0 T如果不是共享中断线,则直接删除irq对应的中断线。
3 L0 l6 x1 u0 A$ i
' o& B  `' w- y如果是共享中断线,则判断此中断处理程序是否中断线上的最后一个中断处理程序,% o3 u" d. m0 H) q6 |9 ]9 e% c

2 M4 a( `( Z9 x+ [- @' R6 [    是最后一个中断处理程序 -> 删除中断线和中断处理程序
8 a; Z9 l1 c$ r# t* n* d* L3 ^4 w( ~9 t- Y7 I' ]
    不是最后一个中断处理程序 -> 删除中断处理程序
0 K/ G2 I7 n7 P  C; H1 s6 c7 G9 X* K' A

: k8 t1 q- G, f1 ^. d7 j! e8 y( l
" j; {/ y9 @- b3.3 中断处理程序的声明" y, [% B* u' s' `0 Z
声明格式如下:
0 b% \) B" |$ b5 I* Q
9 j; q8 Z* m# S& {$ h& o7 X5 O' p; n1 l复制代码
$ l' ]9 _0 G/ u2 Y6 F+ h/*
3 y! c3 {  {2 q' H- E+ w7 t * 中断处理程序的声明; Q+ T% K& M# P
* @irp  - 中断处理程序(即request_irq()中handler)关联的中断号+ `2 L- l# J6 A/ B
* @dev  - 与 request_irq()中的dev一样,表示一个设备的结构体
! F  h1 Z3 h! U * 返回值:
/ ?% f# S( {" ~4 t! V5 G! \1 ? * irqreturn_t -  执行成功:IRQ_HANDLED  执行失败:IRQ_NONE; u. z* a* R' R5 e7 R) j' O7 i5 V8 M
*// G. s3 h0 i  B+ J% A; [
static irqreturn_t intr_handler(int, irq, void *dev)
- h% ^1 ^2 E6 B" G复制代码! _3 e7 P7 F* f. S" Y. u/ V

! X' n" V/ J+ n4 r! F. W
5 q2 q; b' h8 t1 u& v4. 中断处理机制% M0 f8 u: g5 ^4 \" X
中断处理的过程主要涉及3函数:2 y9 q2 v5 q1 k) D; C4 }
5 |+ p5 g, `5 z: }6 ^- Q0 G
do_IRQ 与体系结构有关,对所接收的中断进行应答
$ K- x- Z+ ?3 g5 M9 ihandle_IRQ_event 调用中断线上所有中断处理: a7 r3 F& {" x7 ]$ F6 ?
ret_from_intr 恢复寄存器,将内核恢复到中断前的状态9 E: ]0 B) ]# ]1 X4 ?2 }& g  `

+ A1 O% R" l2 {% G' ^
, O$ D5 y& s3 x9 Z! k: s处理流程可以参见书中的图,如下:/ r3 L. h: h+ t& l+ B  w" |/ n. U
: R5 M% ]* f8 M" L$ M
$ ?% v# \( Q! i# C4 E! l  o! `

& y/ a  K% Z" l- C* V$ R7 F
# ~  h0 o7 V3 B) O% c
4 ~  P1 b8 K  L) W: K  {; Z* C5. 中断控制方法* s2 R) M3 M! e; X: B
常用的中断控制方法见下表:
, ^- Z+ A: J- k* j- K: Q" m2 v
函数
说明
local_irq_disable()禁止本地中断传递
local_irq_enable()激活本地中断传递
local_irq_save()保存本地中断传递的当前状态,然后禁止本地中断传递
local_irq_restore()恢复本地中断传递到给定的状态
disable_irq()禁止给定中断线,并确保该函数返回之前在该中断线上没有处理程序在运行
disable_irq_nosync()禁止给定中断线
enable_irq()激活给定中断线
irqs_disabled()如果本地中断传递被禁止,则返回非0;否则返回0
in_interrupt()如果在中断上下文中,则返回非0;如果在进程上下文中,则返回0
in_irq()如果当前正在执行中断处理程序,则返回非0;否则返回0
* Y4 J/ j1 t4 j3 d
总结
3 G$ i% d. R8 a3 m  s3 X8 g3 I# Q; L; p6 K& d
1 V  w  I- e5 L4 _* W* r9 m! v
中断处理对处理时间的要求很高,如果一个中断要花费较长时间,那么中断处理一般分为2部分。/ d2 D6 o7 k9 a6 Z; d
$ D' r( {: V* t7 R4 x2 U
上半部只做一些必要的工作后,立即通知硬件继续自己的工作。
! B; P9 c! X! `* ^3 q( q) f
/ j' i, ?& A+ e. J  [' P" I' L! C中断处理中耗时的部分,也就是下半部的工作,CPU会在适当的时候去完成。+ [1 e1 g- t0 n8 N: Z6 T" D

作者: youOK    时间: 2020-11-12 15:58
Linux内核设计与实现之中断处理




欢迎光临 EDA365电子论坛网 (https://bbs.eda365.com/) Powered by Discuz! X3.2