1.中断的概念:
中断是指CPU在正常运行程序时,由于程序的预先安排或内外部事件,引起CPU中断正在运行的程序,而转到发生中断事件程序中。引起中断的事件称为中断源。中断源向CPU提出处理的请求称为中断请求。发生中断时被打断程序的暂停点称为断点。CPU暂停现行程序而转为响应中断请求的过程称为中断响应。处理中断源的程序称为中断处理程序。CPU执行有关的中断处理程序称为中断处理。而返回断点的过程称为中断返回。中断的实现实行软件和硬件综合完成,硬件部分叫做硬件装置,软件部分称为软件处理程序。
从物理学的角度看,中断是一种电信号,由硬件设备产生,并直接送入中断控制器(如8259A)的输入引脚上,然后再由中断控制器向处理器发送相应的信号。处理器一经检测到该信号,便中断自己当前正在处理的工作,转而去处理中断。此后,处理器会通知OS已经产生中断。这样,OS就可以对这个中断进行适当的处理。不同的设备对应的中断不同,而每个中断都通过一个唯一的数字标识,这些值通常被称为中断请求线。
2. ARM中断:
不同的处理器的体系结构是不同的,中断系统也是不相同的,下面以arm为例讲述。
以上是arm的异常模式和类型,以IRQ为例:
在IRQ中断发生时arm处理器的硬件会自动执行以下工作
(1)将被中断任务模式的PC值保存到IRQ模式中的LR寄存器中;
(2)将被中断任务模式的CPSR值保存到IRQ模式中的SPSR寄存器中;
(3)将模式自动切换到IRQ模式并将CPSR中的bit7位置禁止后继IRQ中断的发生;
(4)PC被赋予0x00000018或0xFFFF0018,将从该地址开始执行,得到中断源,然后执行相应中断服务程序;
(5)清除中断标志,返回
上述过程用伪代码可以描述如下:
(1) R14_irq = address of next instruction to be executed + 4
(2) SPSR_irq = CPSR
(3) CPSR[4:0] = 0b10010
CPSR[5] = 0 //Execute in ARM state
CPSR[7] = 1
(4) if VE == 0 then
if high vectors configured then
PC = 0xFFFF0018
else
PC = 0x00000018
else
PC = IMPLEMENTATION
(5) SUBS PC,R14, #4
3. linux arm异常向量表处理:
在start_kernel中,调用trap_init处理异常向量表;
arch/arm/kernel/traps.c void __init trap_init(void) { unsigned long vectors = CONFIG_VECTORS_BASE; ...... memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start); memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start); memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz); ...... }
在arch/arm/kernel/entry-armv.S中,定义了__vectors_start跟__vectors_end,这个范围就是异常向量表,另CONFIG_VECTORS_BASE在autoconf.h定义,中断向量表的位置可以有两个位置:一个是0,另一个是0xffff0000。可以通过CP15协处理器c1寄存器中V位(bit[13])控制。
arch/arm/kernel/entry-armv.S .globl __vectors_start __vectors_start: swi SYS_ERROR0 b vector_und + stubs_offset ldr pc, .LCvswi + stubs_offset b vector_pabt + stubs_offset b vector_dabt + stubs_offset b vector_addrexcptn + stubs_offset b vector_irq + stubs_offset b vector_fiq + stubs_offset .globl __vectors_end __vectors_end:
4. 中断响应:
当有外部中断产生时,跳到中断向量表执行b vector_irq + stubs_offset,最后到asm_do_IRQ执行。
asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs) { struct irqdesc *desc = irq_desc + irq; /* * Some hardware gives randomly wrong interrupts. Rather * than crashing, do something sensible. */ if (irq >= NR_IRQS) desc = &bad_irq_desc; irq_enter(); desc_handle_irq(irq, desc, regs); /* AT91 specific workaround */ irq_finish(irq); irq_exit(); }