韦东山书读后感
1. 对异常概念的理解
异常就是可以打断CPU正常运行的事件,比如,外部中断、未定义的指令、软中断等。当这些异常发生时,就打断CPU的正常运行,跳到相应的异常处理程序去处理这些异常要求的一些操作。
2. Linux内核异常处理框架
基于Linux-2.6.32,内核启动时early_trap_init(void)将异常向量表拷贝到0xffff0000的虚拟地址中去:
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中定义:
.globl __vectors_start
__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
每一条均跳转执行对应的代码:
(1)以vector_und 为例
vector_stub und, UND_MODE
.long __und_usr @ 0 (USR_26 / USR_32) ==》__und_usr==》__und_usr_unknown==》asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
.long __und_invalid @ 1 (FIQ_26 / FIQ_32)
.long __und_invalid @ 2 (IRQ_26 / IRQ_32)
.long __und_svc @ 3 (SVC_26 / SVC_32)
.long __und_invalid @ 4
.long __und_invalid @ 5
.long __und_invalid @ 6
.long __und_invalid @ 7
.long __und_invalid @ 8
.long __und_invalid @ 9
.long __und_invalid @ a
.long __und_invalid @ b
.long __und_invalid @ c
.long __und_invalid @ d
.long __und_invalid @ e
.long __und_invalid @ f
.align 5
vector_stub und, UND_MODE宏展开:
.macro vector_stub, name, mode, correction=0
.align 5
vector_\name: ==》vector_und
stmia sp, {r0, lr} @ save r0, lr
mrs lr, spsr
str lr, [sp, #8] @ save spsr
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 ENDPROC(vector_und)
.align 2
1:
.endm
这里,计算处理完异常后的返回地址,保存一些寄存器,然后进入管理模式,最后根据被中断的工作模式调用上面的某个跳转分支,不同的跳转分支只是在入口处稍有差别,后续的处理大体相同,都是调用相应的c函数,
(2)看看 W(b) vector_irq + stubs_offset
vector_stub irq, IRQ_MODE, 4
.long __irq_usr @ 0 (USR_26 / USR_32) ==》 usr_entry==》irq_handler==》 asm_do_IRQ
.long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
.long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
.long __irq_svc @ 3 (SVC_26 / SVC_32)
.long __irq_invalid @ 4
.long __irq_invalid @ 5
.long __irq_invalid @ 6
.long __irq_invalid @ 7
.long __irq_invalid @ 8
.long __irq_invalid @ 9
.long __irq_invalid @ a
.long __irq_invalid @ b
.long __irq_invalid @ c
.long __irq_invalid @ d
.long __irq_invalid @ e
.long __irq_invalid @ f
vector_stub irq, IRQ_MODE, 4宏展开:
.macro vector_stub, name, mode, correction=0
.align 5
vector_irq:
sub lr, lr, #4 //计算返回地址
stmia sp, {r0, lr} @ save r0, lr
mrs lr, spsr
str lr, [sp, #8] @ save spsr
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_irq)
.align 2
1:
.endm
3. Linux内核中断处理
上一步中,我们知道了irq中最终会调用了asm_do_IRQ,接下来我们也来看一下asm_do_IRQ处理过程:
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
irq_enter(); //rcu模块记录内部计数,表示当前退出了NOHZ状态,将当前任务的抢占计数中的硬中断计数加1.该计数表示当前中断嵌套层次。
if (unlikely(irq >= NR_IRQS)) { //判断是否为错误中断号,对于错误中断号就进行计数而不进行处理
if (printk_ratelimit())
printk(KERN_WARNING "Bad IRQ%u\n", irq);
ack_bad_irq(irq);
} else {
generic_handle_irq(irq); //真正的中断处理
}
irq_finish(irq);
irq_exit();
set_irq_regs(old_regs);
}
static inline void generic_handle_irq(unsigned int irq) 调用generic_handle_irq_desc(irq, irq_to_desc(irq));
struct irq_desc *irq_to_desc(unsigned int irq) //根据中断号取出中断描述符
{
return (irq < NR_IRQS) ? irq_desc + irq: NULL;
}
static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
#ifdef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ //在配置文件中定义
desc->handle_irq(irq, desc);
#else
if (likely(desc->handle_irq))
desc->handle_irq(irq, desc);
else
__do_IRQ(irq);
#endif
}
s3c24xx_init_irq(void)填充结构体struct irq_desc 中的成员chip,handle_irq
set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
{
__set_irq_handler(irq, handle, 0, NULL);
}
for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
irqdbf("registering irq %d (extended s3c irq)\n", irqno);
set_irq_chip(irqno, &s3c_irqext_chip);
set_irq_handler(irqno, handle_edge_irq);
set_irq_flags(irqno, IRQF_VALID);
}
handle_edge_irq函数中:
desc->chip->ack(irq);//清中断
handle_IRQ_event(irq, action);//取出action链表中的成员,执行action->handle.
4. request_irq中断注册
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
==》request_threaded_irq(irq, handler, NULL, flags, name, dev);
int request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_tthread_fn, unsigned longirqflags,const char *devname, void *dev_id)
{
struct irqaction *action;
struct irq_desc *desc;
int retval;
if ((irqflags & (IRQF_SHARED|IRQF_DISABLED)) ==
(IRQF_SHARED|IRQF_DISABLED)) {
pr_warning(
"IRQ %d/%s: IRQF_DISABLED is not guaranteed on shared IRQs\n",
irq, devname);
}
#ifdef CONFIG_LOCKDEP
/*
* Lockdep wants atomic interrupt handlers:
*/
irqflags |= IRQF_DISABLED;
#endif
if ((irqflags & IRQF_SHARED) && !dev_id)
return -EINVAL;
desc = irq_to_desc(irq);
if (!desc)
return -EINVAL;
if (desc->status & IRQ_NOREQUEST)
return -EINVAL;
if (!handler) {
if (!thread_fn)
return -EINVAL;
handler = irq_default_primary_handler;
}
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); //申请并填充结构体
if (!action)
return -ENOMEM;
action->handler = handler;
action->thread_fn = thread_fn;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;
chip_bus_lock(irq, desc);
retval = __setup_irq(irq, desc, action);==》此函数中将已有的desc[irq]的action中新加入了形参的action;set_type;starup/enable
chip_bus_sync_unlock(irq, desc);
if (retval)
kfree(action);
#ifdef CONFIG_DEBUG_SHIRQ
if (irqflags & IRQF_SHARED) {
unsigned long flags;
disable_irq(irq);
local_irq_save(flags);
handler(irq, dev_id);
local_irq_restore(flags);
enable_irq(irq);
}
#endif
return retval;
}
5. free_irq中断卸载
(1)根据中断号irq、dev_id从action链表中找出表项,将他移除
(2)如果它是唯一的表项,还要调用irq_desc[irq].chip->shutdown或者irq_desc[irq].chip->disable关闭表项