中断基础知识
中断与中断函数
在linux中,中断处理程序就是普普通通的c函数,只不过这些函数必须按照特定的类型声明,以便内核能够以标准的方式传递处理程序的信息,在其他方面它们与一般的函数没有什么不同,中断处理程序与其他内核函数的真正区别在于中断处理程序是被内核调用来响应中断的。中断机制是硬件在需要的时候向cpu发出信号,cpu暂时停止正在运行的工作,来处理硬件请求的一种机制。
/proc/interrupts这个文件包含有哪些中断正在使用和每个处理器各被中断多少次的信息
举例16号
16: 0 198540 0 0 IO-APIC 16-fasteoi vmwgfx, snd_ens1371
中断号16的中断口,在cpu1上响应了198540个中断,其他三个cpu上响应了0次,链接在这个端口的中断链表上设备接口是IO-APIC-fasteoi
这个中断号是设备名为 vmwgfx, snd_ens1371的几个设备共享的。
IRQ与中断信号
每个能够发出中断请求的硬件设备控制器都有一条名为IRQ的输出线,所有现有的IRQ线都与一个名为可编程中断控制器的硬件电路的输入引脚相连,可编程中断控制器执行下列动作:
- 监视IRQ线,检查产生的信号,如果有两条或两条以上的IRQ线上产生了信号,就选择引脚编号比较小的IRQ线。
- 如果一个引发信号出现在IRQ线上:
- a. 把接收到的引发信号转换成对应的向量。
- b. 把这个向量存放在中断控制器的一个I/O端口,从而允许CPU通过数据总线读此向量。
- c. 把引发向量发送到处理器的INTER引脚,即产生一个中断。
- d. 等待,直到CPU通过把这个中断信号写进可编程中断控制器的一个I/O端口来确认它;当这种情况发生时,清INTER线。
- 返回到第一步。
中断向量描述符
中断向量是指中断发生以后,程序会按照中断类型执行不同的中断服务程序,这个中断向量就是这些中断处理函数的入口地址的变量。
具体为什么叫向量,我认为,中断是有优先级的,程序会按照优先级从高到低依次查询,突出一个方向性,所以叫向量。
CPU是根据中断号获取中断向量值,即对应中断服务程序的入口地址值。因此为了让CPU由中断号查找到对应的中断向量,就需要在内存中建立一张查询表,即中断向量表(在32位保护模式下该表称为中断描述符表)。80x86微机支持256个中断,对应每个中断需要安排一个中断服务程序。在80x86实模式运行方式下,每个中断向量由4字节组成。这4字节指明了一个中断服务程序的段值和段内偏移值。
中断描述表(IDT)是一个系统表,它与每一个中断或异常向量相联系,每一个向量在表中有相应的中断或异常处理程序的入口地址。IDT表可以驻留在线性地址空间的任何地方,处理器使用IDTR寄存器来定位IDT表的位置,这个寄存器中含有IDT表32位的基地址和16位的长度(限长)值,IDT表基地址应该对其在8字节边界上以提高处理器的访问效率,限长值是以字节为单位的IDT表的长度。其中每一个表项叫做门描述符,“门”的含义是当中断发生时必须先通过这些门,然后才能进入相应的处理程序。
中断号查询简要步骤
计算机内存的前1024个字节(偏移量00000H到003FFH)保存着256个中断向量,每个中断向量占4个字节,前两个字节保存着中断服务程序的入口地址偏移量,后两个字节保存着中断程序的入口段地址,使用时,只要将它们分别调入寄存器IP及CS中,就可以转入中断服务程序实现中断调用。每当中断发生时,CPU将中断号乘以4,在中断向量表中得到该中断向量地址,进而获得IP及CS值,从而转到中断服务程序的入口地址,调用中断。这就是中断服务程序通过中断号调用的基本过程。在计算机启动的时候,BIOS将基本的中断填入中断向量表,当DOS得到系统控制权后,它又要将一些中断向量填入表中,还要修改一部分BIOS的中断向量。有一部分中断向量是系统为用户保留的,如60H到67H号中断,用户可以将自己的中断服务程序写入这些中断向量中。不仅如此,用户还可以自己更改和完善系统已有的中断向量。
中断类型
中断一般分为异步中断(通常由硬件引起)和同步中断(一般由处理器本身引起)。
**异步中断:**CPU处理中断的时间过长,所以先将硬件复位,使硬件可以继续自己的工作,然后再使得当时候处理中断请求中耗时的部分。
网卡的工作原理(异步中断)
- 网卡收到数据包后,向CPU发出中断信号,请求处理接收的的数据包。
- CPU将收到的数据包拷贝到内存后,即通知网卡继续工作。
- 至于数据包拷贝到内存后的处理会在适当的时候进行。
**同步中断:**CPU处理完中断请求的所有工作后才反馈硬件
系统异常处理(比如运算中的除0操作)
- 应用程序出现异常后,需要内核来处理
- 内核调用相应的异常处理函数来处理异常
- 处理完后终止应用程序或给出message
中断相关函数
实现一个中断,主要需要知道3个函数:
- 注册中断的函数
- 释放中断的函数
- 中断处理程序的声明
注册中断的函数,在内核<linux/interrupt.h>
/*
irg -表示要分配的中断号
handler -实际的中断处理程序
flags -标志位,表示此中断的具有特性
name -中断设备名称的ASCII表示,会被/proc/irg和/proc/interruputs文件使用
dev -用于共享中断线,多个中断程序共享一个中断线时(共用一个中断信号),依靠dev来区别各个中断程序
*/
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev);
中断处理机制,主要涉及3个函数
do_IRQ与体系结构有关,对所接收的中断进行应答
handle_IRQ_event调用中断线上所有中断处理
ret_from_intr恢复寄存器,将内核恢复到中断前的状态
处理流程如下:
在内核中,中断开始于预定于入口点,这类似于系统调用通过预定于的异常句柄进入内核,对于每条中断线,处理器都会跳到对应的一个唯一的位置,这样内核就可知道所接收到的IRQ号了,初始入口点只是在栈中保存这个号,并存放当前寄存器的值,然后内核调用函数do_IRQ()。
do_IRQ()声明如下:
unsigned int do_IRQ(struct pt_regs regs)
其中pt_regs结构包含原始寄存器的值,中断的值也得以保存,所以do_IRQ()可以将它提取出来。
接下来,do_IRQ()需要确保在这条中断线上有一个有效的处理程序,而且这个程序已经启动,但是当前并没有执行,如果存在该已启动且尚未执行的处理程序,do_IRQ()就调用handle_IRQ_event()来运行这条中断线上所安装的中断处理程序。kernel/irq/handle.c
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
irqreturn_t ret, retval = IRQ_NONE;
unsigned int status = 0;
if (!(action->flags & IRQF_DISABLED))
local_irq_enable_in_hardirq();
do {
trace_irq_handler_entry(irq, action);
ret = action->handler(irq, action->dev_id);
trace_irq_handler_exit(irq, action, ret);
switch (ret) {
case IRQ_WAKE_THREAD:
/*
* 把返回值设置为已处理,以便可疑的检查不再触发
*/
ret = IRQ_HANDLED;
/*
* 捕获返回值为WAKE_THREAD的驱动程序,但是并不创建一个线程函数
*/
if (unlikely(!action->thread_fn)) {
warn_no_thread(irq, action);
break;
}
/*
* 为这次中断唤醒处理线程,万一线程崩溃且杀死,我们仅仅假装已经处理了该中断,上述的硬件中断处理程序已经禁止设备中断,因此杜绝irq的产生。
* storm is lurking.
*/
if (likely(!test_bit(IRQTF_DIED,
&action->thread_flags))) {
set_bit(IRQTF_RUNTHREAD, &action->thread_flags);
wake_up_process(action->thread);
}
/* Fall through to add to randomness */
case IRQ_HANDLED:
status |= action->flags;
break;
default:
break;
}
retval |= ret;
action = action->next;
} while (action);
if (status & IRQF_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
local_irq_disable();
return retval;
}
#ifndef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ
#ifdef CONFIG_ENABLE_WARN_DEPRECATED
# warning __do_IRQ is deprecated. Please convert to proper flow handlers
#endif
中断上下文
中断上文:硬件通过中断触发信号,导致内核调用中断处理程序,进入内核空间中,硬件的一些变量和参数也要传递给内核,内核通过这些参数进行中断处理,中断上文可以看着就是硬件传递过来的这些参数和内核需要保存的一些其他环境。
中断下文:执行在内核空间的中断服务程序。
中断上下文具有较为严格的时间限制,因为它打断了其他代码。中断上下文中的代码应当迅速、简洁,尽量不使用循环去处理繁重的工作。尽量把工作从中断处理程序中分离出来,放在下半部来执行,因为下半部可以在更合适的时间运行。