Linux异常处理体系结构

韦东山书读后感

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关闭表项

你可能感兴趣的:(操作系统)