韋東山書讀後感
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關閉表項