韋東山學習筆記

韋東山書讀後感

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關閉表項

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章