基于s5pv210cpu在linux2.6.35.6中的异常体系介绍:
start_kernel:
setup_arch:
early_trap_init:构建异常向量表(一些固有的跳转程序)
异常向量表如下所示
异常向量表:
__vectors_start:
ARM( swi SYS_ERROR0 )
THUMB( svc #0 )
THUMB( nop )
W(b) vector_und + stubs_offset
W(ldr) pc, .LCvswi + stubs_offset
W(b) vector_pabt + stubs_offset
W(b) vector_dabt + stubs_offset
W(b) vector_addrexcptn + stubs_offset
W(b) vector_irq + stubs_offset
W(b) vector_fiq + stubs_offset
.globl __vectors_end
__vectors_end:
vector_irq使用#+name宏来定义(在linux内核中使用广泛):
.macro vector_stub, name, mode, correction=0
.align 5
vector_\name:
.if \correction
sub lr, lr, #\correction
.endif
mrs r0, cpsr
eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
msr spsr_cxsf, r0
and lr, lr, #0x0f
THUMB( adr r0, 1f )
THUMB( ldr lr, [r0, lr, lsl #2] )
mov r0, sp
ARM( ldr lr, [pc, lr, lsl #2] )
movs pc, lr @ branch to handler in SVC mode
ENDPROC(vector_\name)
.align 2
@ handler addresses follow this label
1:
.endm
下面以中断这个异常向量为例进行中断处理的分析:
W(b) vector_irq + stubs_offset 执行vector_irq处的代码,stubs_offset用来重新定位跳转的位置
该段代码主要将中断返回地址保存在栈中,进入管理SVC模式
__irq_usr:
usr_entry 宏,保存现场
irq_handler 进行中断处理的宏
asm_do_IRQ 进行中断处理的c函数
generic_handle_irq
generic_handle_irq_desc
desc->handle_irq(irq, desc); 每一个中断号对应一个中断描述符,通过中段描述符中的处理函数进行中断处理
或者__do_IRQ(irq) 可以配置使用中断线程,是否使用中断线程由action->handler函数的返回值决定
在 Linux 中,中断具有最高的优先级。不论在任何时刻,只要产生中断事件,内核将立即执行相应的中断处理程序,等到所有挂起的中断和软中断处理完毕后才能执行正常的任务,因此有可能造成实时任务得不到及时的处理。中断线程化之后,中断将作为内核线程运行而且被赋予不同的实时优先级,实时任务可以有比中断线程更高的优先级。这样,具有最高优先级的实时任务就能得到优先处理,即使在严重负载下仍有实时性保证。但是,并不是所有的中断都可以被线程化,比如时钟中断,主要用来维护系统时间以及定时器等,其中定时器是操作系统的脉搏,一旦被线程化,就有可能被挂起,这样后果将不堪设想,所以不应当被线程化。
for (irq = IRQ_EINT(0); irq <= IRQ_EINT(15); irq++)
set_irq_chip(irq, &s5p_irq_vic_eint);
for (irq = IRQ_EINT(16); irq <= IRQ_EINT(31); irq++) {
set_irq_chip(irq, &s5p_irq_eint);
set_irq_handler(irq, handle_level_irq);
set_irq_flags(irq, IRQF_VALID);
}
可见IRQ_EINT(0)到IRQ_EINT(15)没有设置set_irq_handler(irq, handle_level_irq)。由下面代码:
if (likely(desc->handle_irq))
desc->handle_irq(irq, desc);
else
__do_IRQ(irq);
可知IRQ_EINT(0)到IRQ_EINT(15)可以进行配置中断线程
上面追述到了使用中断描述符中的中断处理函数进行中断处理,在什么地方将这个描述符的成员进行填充呢?
上面那个问题的入口从init_IRQ函数开始
init_IRQ:
init_arch_irq() 函数指针 真正调用的是s5pv210_init_irq,在开发板machine_desc结构体中定义
s5p_init_irq
vic_init 填充四组中断的irq_dsc
s3c_init_vic_timer_irq 填充定时器中断的irq_desc
s3c_init_uart_irqs 填充串口中断的irq_desc
上面主要初始化的是内部中断
arch_initcall(s5p_init_irq_eint)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
上面这段代码在段.initcall3.init中定义了函数s5p_init_irq_eint
思考:定义这个函数在什么地方进行调用呢?
start_kernel
rest_init
kernel_init 进程1
do_basic_setup
do_initcalls 该函数中调用了所有在initcall段中的函数
思考:s5p_init_irq_eint该函数做了什么呢?
这个函数主要初始化了外部中断:
s5p_init_irq_eint
set_irq_chip 设置chip进行底层的硬件相关处理
set_irq_handler 设置irq_desc中的中断处理函数
irq_handle处理中断函数handle_level_irq,最终调用的是action->handler
下面就是action这个结构是什么时候构造的呢?
request_threaded_irq 构造action
__setup_irq 链入action 综合考虑中断的触发方式、是否共享等
irq_chip_set_defaults 使用默认填充irq_desc中的chip成员
__irq_set_trigger
chip->set_type 设置外部中断触发类型
desc->chip->startup 开启中断使能中断
构造的action怎样释放呢?
free_irq(unsigned int irq, void *dev_id)
kfree(__free_irq(irq, dev_id))
__free_irq(unsigned int irq, void *dev_id)
上面代码主要是从链表中释放action对象,如果释放后链表中再也没有action对象则关闭中断