您的位置:首页 > 运维架构 > Linux

Linux异常处理体系结构

2012-04-30 18:19 423 查看
韦东山书读后感

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)填充结构体[b]struct irq_desc 中的成员chip,handle_irq[/b]
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关闭表项
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: