与中断有关的数据结构
来源:互联网 发布:网络原创古风歌曲 编辑:程序博客网 时间:2024/04/29 19:05
1.概述
通过前文,我们已经知道了中断通常由上下两部分组成。在上部分,也就是中断处理程序,完成中断请求的响应以及完成那些对时间要求紧迫的工作;而在下部分,通常完成那些被推后的工作,因为这部分工作对时间的要求相对宽松一些。通过了解上下两部分的工作情况,可以更好的理解中断这个概念。从下半部分执行机制来看——不管是tasklet还是工作队列——这些推后的工作总是在上半部分被调用,然后交给内核在适当的时间来完成。那么,中断上部分具体是如何工作的?内核对中断是如何处理的?
在开始分析之前,需要说明的是接下来分析的属于内核对外设产生的中断的处理情况,异常的处理过程在本文的最后会有简单解释。
所有的中断的处理程序在init_IRQ函数中都被初始化为interrupt[i]。interrupt数组中每一项均指向一个代码片段:
1
pushl $n-256
2
/*省略部分代码*/
3
jmp common_interrupt
该代码片段除了将中断向量号压入堆栈,还会跳到一个公共处理程序common_interrup:
1
//在linux/arch/x86/kernel/entry_32.S
2
863 common_interrupt:
3
864 addl $-0x80,(%esp) /* Adjust vector into the [-256,-1] range * /
4
865 SAVE_ALL
5
866 TRACE_IRQS_OFF
6
867 movl %esp,%eax
7
868 call do_IRQ
8
869 jmp ret_from_intr
这段公共处理程序会将中断发生前的所有寄存器的值压入堆栈,也就是保存被中断任务的现场。然后调用do_IRQ函数,在do_IRQ函数中会调用(并非直接调用那么简单)到handle_IRQ_event函数,在此函数中会执行实际的中断服务例程。当中断服务例程执行完毕后,会返回到上面的那段汇编程序中,转入ret_from_intr代码段从中断返回。
在上述文字描述的基础上,可以通过下图进一步加深中断的处理过程:
从上面的概述中,我们可以很快定位我们未来要分析的函数:do_IRQ()和hand_IRQ_event();在分析上述函数之前,我们很有必要先分析一下关于中断的三个重要的数据结构。
2.struct irq_desc
在一开始我们分析中断的时候,谈及到了中断向量。在内核中,每个中断向量都有相应的有一个irq_desc结构体(稍早内核版本中为irq_desc_t)来描述一个中断向量(也就是中断源),具体代码如下:
01
31struct irq_desc;
02
32typedef
void
(*irq_flow_handler_t)(unsigned
int
irq,
03
33
struct
irq_desc *desc);
04
05
175struct irq_desc {
06
176 unsigned
int
irq;
07
177
struct
timer_rand_state *timer_rand_state;
08
178 unsigned
int
*kstat_irqs;
09
179#ifdef CONFIG_INTR_REMAP
10
180
struct
irq_2_iommu *irq_2_iommu;
11
181#endif
12
182 irq_flow_handler_t handle_irq;
13
183
struct
irq_chip *chip;
14
184
struct
msi_desc *msi_desc;
15
185
void
*handler_data;
16
186
void
*chip_data;
17
187
struct
irqaction *action;
18
188 unsigned
int
status;
19
189
20
190 unsigned
int
depth;
21
191 unsigned
int
wake_depth;
22
192 unsigned
int
irq_count;
23
193 unsigned
long
last_unhandled;
24
194 unsigned
int
irqs_unhandled;
25
195 raw_spinlock_t lock;
26
196#ifdef CONFIG_SMP
27
197 cpumask_var_t affinity;
28
198
const
struct
cpumask *affinity_hint;
29
199 unsigned
int
node;
30
200#ifdef CONFIG_GENERIC_PENDING_IRQ
31
201 cpumask_var_t pending_mask;
32
202#endif
33
203#endif
34
204 atomic_t threads_active;
35
205 wait_queue_head_t wait_for_threads;
36
206#ifdef CONFIG_PROC_FS
37
207
struct
proc_dir_entry *dir;
38
208#endif
39
209
const
char
*name;
40
210}
41
42
216#ifndef CONFIG_SPARSE_IRQ
43
217extern
struct
irq_desc irq_desc[NR_IRQS];
44
218#endif
所有这样的描述符组织在一起形成irq_desc[NR_IRQS]数组。下面我们对上述结构体的部分字符进行解释;
irq:通过数据类型可知这便是这个描述符所对应的中断号;
handle_irq:指向该IRQ线的公共服务程序;
chip:它是一个struct irq_chip类型的指针,是中断控制器的描述符,与平台有关,下文有详细描述;
handler_data:用于handler_irq的参数;
chip_data:用于chip的参数;
action:一个struct irqaction类型的指针(下文有该结构的详细描述);它指向一个单链表,该单链表是由该中断线上所有中断服务程序(对应struct irqaction)所连接起来的;
status:描述中断线当前的状态;
depth:中断线被激活时,值为0;其值为正数时,表示被禁止的次数;
irq_count:记录该中断线发生中断的次数;
irqs_unhandled:该IRQ线上未处理中断发生的次数;
name: /proc/interrupts 中显示的中断名称;
3.struct irqaction
当多个设备共享一条IRQ线时,因为每个设备都要有各自的ISR。为了能够正确处理此条IRQ线上的中断处理程序(也就是区分每个设备),就需要我们使用irqaction结构体。在这个结构体中,会有专门的handler字段指向该设备的真正的ISR。共享同一条IRQ线上的多个这样的结构体会连接成了一个单链表,即所谓的中断请求队列。中断产生时,该IRQ线的中断请求队列上所有的ISR都会被依次调用,因此每个设备的ISR必须判断当前的中断是否是自己所属的设备产生的。irqaction结构具体的定义如下:
01
98typedef irqreturn_t (*irq_handler_t)(
int
,
void
*);
02
03
113struct irqaction {
04
114 irq_handler_t handler;
05
115 unsigned
long
flags;
06
116
const
char
*name;
07
117
void
*dev_id;
08
118
struct
irqaction *next;
09
119
int
irq;
10
120
struct
proc_dir_entry *dir;
11
121 irq_handler_t thread_fn;
12
122
struct
task_struct *
thread
;
13
123 unsigned
long
thread_flags;
14
124};
handler:指向一个具体的硬件设备的中断服务例程,可以从此指针的类型发现与前文我们所定义的中断处理函数声明相同;
flags:对应request_irq函数中所传递的第三个参数,可取IRQF_DISABLED、IRQF_SAMPLE_RANDOM和IRQF_SHARED其中之一;
name:对应于request_irq函数中所传递的第四个参数,可通过/proc/interrupts文件查看到;
next:指向下一个irqaction结构体;
dev_id:对应于request_irq函数中所传递的第五个参数,可取任意值,但必须唯一能够代表发出中断请求的设备,通常取描述该设备的结构体;
irq:中断号
如果一个IRQ线上有中断请求,那么内核将依次次调用在该中断线上注册的每一个中断服务程序,但是并不是所有中断服务程序都被执行。一般硬件设备都会提供一个状态寄存器,以便中断服务程序进行检查是否应该为这个硬件服务。也就是说在整个中断请求队列中,最多会有一个ISR被执行,也就是该ISR对应的那个设备产生了中断请求时;不过当该IRQ线上某个设备未找到匹配的ISR时,那这个中断就不会被处理。此时irq_desc结构中的irqs_unhandled字段就会加1。
4.struct irq_chip
struct irq_chip是一个中断控制器的描述符。通常不同的体系结构就有一套自己的中断处理方式。内核为了统一的处理中断,提供了底层的中断处理抽象接口,对于每个平台都需要实现底层的接口函数。这样对于上层的中断通用处理程序就无需任何改动。这样的结构体就好比一个插板,我们可以使用各种插头。至于插板内部是如何实现的,使用者并不需要过于关心。
比如经典的中断控制器是2片级联的8259A,那么得15个irq_desc描述符,每一个描述符的irq_chip都指向描述8259A的i8259A_irq_type变量(arch/alpha/kernel/irq_i8259.c):
1
86struct irq_chip i8259a_irq_type = {
2
87 .name =
"XT-PIC"
,
3
88 .startup = i8259a_startup_irq,
4
89 .shutdown = i8259a_disable_irq,
5
90 .enable = i8259a_enable_irq,
6
91 .disable = i8259a_disable_irq,
7
92 .ack = i8259a_mask_and_ack_irq,
8
93 .end = i8259a_end_irq,
9
94};
这一点类似于VFS中所采用的原理:通过struct file_operations提供统一的接口,每种文件系统都必须具体实现这个结构体中所提供的接口。struct irq_chip具体代码如下:
01
111struct irq_chip {
02
112
const
char
*name;
03
113 unsigned
int
(*startup)(unsigned
int
irq);
04
114
void
(*shutdown)(unsigned
int
irq);
05
115
void
(*enable)(unsigned
int
irq);
06
116
void
(*disable)(unsigned
int
irq);
07
117
08
118
void
(*ack)(unsigned
int
irq);
09
119
void
(*mask)(unsigned
int
irq);
10
120
void
(*mask_ack)(unsigned
int
irq);
11
121
void
(*unmask)(unsigned
int
irq);
12
122
void
(*eoi)(unsigned
int
irq);
13
123
14
124
void
(*end)(unsigned
int
irq);
15
125
int
(*set_affinity)(unsigned
int
irq,
16
126
const
struct
cpumask *dest);
17
127
int
(*retrigger)(unsigned
int
irq);
18
128
int
(*set_type)(unsigned
int
irq, unsigned
int
flow_type);
19
129
int
(*set_wake)(unsigned
int
irq, unsigned
int
on);
20
130
21
131
void
(*bus_lock)(unsigned
int
irq);
22
132
void
(*bus_sync_unlock)(unsigned
int
irq);
23
133
24
134
/* Currently used only by UML, might disappear one day.*/
25
135#ifdef CONFIG_IRQ_RELEASE_METHOD
26
136
void
(*release)(unsigned
int
irq,
void
*dev_id);
27
137#endif
28
138
/*
29
139 * For compatibility, ->typename is copied into ->name.
30
140 * Will disappear.
31
141 */
32
142
const
char
*
typename
;
33
143};
name:中断控制器的名字;
Startup:启动中断线;
Shutdown:关闭中断线;
Enable:允许中断;
Disable:禁止中断;
5.数据结构之间的关系
首先我们通过下图来了解上述三个数据结构的关系:
通过上述分析,我们可以大致的知道中断处理程序的调用过程:在do_IRQ函数中,通过对irq_desc结构体中handler_irq字段的引用,调用handler_irq所指向的公共服务程序;在这个公共服务程序中会调用hand_IRQ_event函数;在hand_IRQ_event函数中,通过对irqaction结构体中handler字段的引用最终调用我们所写的中断处理程序。
另外,通过上述分析,我们知道struct irq_chip描述了中断最底层的部分;而struct irqacton则描述最上层具体的中断处理函数;而与中断向量所对应的struct desc则类似一个中间层,将中断中的硬件相关的部分和软件相关的部分连接起来。
6.内核对异常的处理
与中断不同,各种异常都有固定的中断向量(0~31)以及固定的异常处理程序。因此,当异常发生时,将直接跳转到相应的服务程序中执行。
- 与中断有关的数据结构
- 与中断有关的数据结构
- 有关中断的使用
- WINCE5.0中与中断有关的几个源文件
- 数据结构的有关概念
- 准备写一些与数据结构,算法有关的东西
- 中断有关
- 有关中断
- 有关中断
- 中断与中断器的
- 有关字符设备的数据结构
- 消息队列有关的数据结构
- Linux内核--网络协议栈深入分析(一)--与sk_buff有关的几个重要的数据结构
- Linux内核--网络协议栈深入分析(一)--与sk_buff有关的几个重要的数据结构
- 网络协议栈深入分析(一)--与sk_buff有关的几个重要的数据结构
- Linux内核--网络协议栈深入分析(一)--与sk_buff有关的几个重要的数据结构
- Linux内核--网络协议栈深入分析(一)--与sk_buff有关的几个重要的数据结构
- linux中断系列之中断重要的数据结构(二)
- SIT
- 相机标定的原理与意义及OpenCV、Matlab实现差异小结
- Openfire开发配置,Openfire源代码配置,OpenFire二次开发配置
- 汇编中addr和offset的异同点
- 浏览器程序设计
- 与中断有关的数据结构
- MyEclipse8.5+版本安装SVN插件方法(经本人亲测,完全有效)
- 初识Halcon
- socket http 理解
- 那些过气的IT领袖们
- 实习总结(二)---HTML5探索
- 华为内部LINUX学习资料 笔记心得及linux学习口诀秘诀
- iptables 解决双网卡linux服务器SSH&putty远程访问问题
- [最小生成树]Prim算法和Kruskal算法