中斷子係統3_中斷入口處理
// 中斷入口
// 注:gnu 每個符號分屬global(被輸出)和local(不被輸出)兩類中的一種。
1.1 #define ENTRY(name) \
.globl name; \
ALIGN; \//之後的代碼對齊到32字節,使用NOP(0x90)補齊
name:
// 代碼對齊
// .align(n) power-of-2對齊
// 4 對齊到16字節, 5 對齊到32字節
// 0x90 NOP 指令的機器碼,用於填充到指定的對齊字節
1.2 #define ALIGN .align 4,0x90
// 可屏蔽中斷入口
// 1.IRQn中斷處理程序所在的地址開始是保存在interrupt[n]之中的,之後才複製到IDT相應的表項中斷門中
// 2..text段連續存儲在一起,.data段連續存儲在一起
// 3.最終在內存中:
// 3.1 所有可屏蔽中斷的入口地址依次連續存儲在.data段,interrupt保存數組起始地址
// interrupt ->
// interrupt[0]
// interrupt[1]
// interrupt[2]
// .
// .
// interrupt[255]
// 3.2 所有可屏蔽中斷處理函數依次連續存儲在.text段,irq_entries_start保存數組起始地址
// irq_entries_start ->
// pushl -256
// jmp common_interrupt
// pushl -255
// jmp common_interrupt
// pushl -254
// jmp common_interrupt
// .
// .
// pushl -1
// jmp common_interrupt
1.3
.data //數據段
ENTRY(interrupt)
.text //代碼段
// IRQ0~IRQ255
vector=0
ENTRY(irq_entries_start)
.rept NR_IRQS //.rept,.endr之間的代碼展開255次
ALIGN
1: pushl $vector-256 //IRQ號取負
jmp common_interrupt
.data //數據段,會與35行合並,所有數據段連續存儲起來
.long 1b // 標簽1的地址
.text
vector=vector+1
.endr //32行重複結束
ALIGN
common_interrupt: //所有可屏蔽中斷函數的公共部分
SAVE_ALL //寄存器入棧
movl %esp,%eax // eax保存棧頂指針
call do_IRQ //中斷處理函數
jmp ret_from_intr
1.4 #define SAVE_ALL \
cld; \ //清除方向標誌
pushl %es; \
pushl %ds; \
pushl %eax; \ //eax保存中斷號
pushl %ebp; \
pushl %edi; \
pushl %esi; \
pushl %edx; \
pushl %ecx; \
pushl %ebx; \
movl $(__USER_DS), %edx; \ //es,ds指向用戶數據段
movl %edx, %ds; \
movl %edx, %es;
// 中斷入口函數
// 參數:regs,通過eax傳遞被中斷進程或被中斷中斷的上下文
// 函數主要任務:
// 1.遞增中斷嵌套計數器
// 2.切換內核棧
// 2.1 4k核心棧,並且當前在進程上下文
// 3.__do_IRQ統一入口
// 3.1 向芯片屏蔽並確認中斷
// 3.2 執行中斷處理函數
// 3.3 解除屏蔽
// 4.退出中斷
// 4.1 遞減中斷嵌套計數器
// 4.2 執行軟中斷,或調度進程執行
1.5 fastcall unsigned int do_IRQ(struct pt_regs *regs)
{
int irq = regs->orig_eax & 0xff; //eax低8位保存中斷號
#ifdef CONFIG_4KSTACKS //每個線程使用4k的內核堆棧,中斷使用獨立的棧
union irq_ctx *curctx, *irqctx;
u32 *isp;
#endif
//遞增current->preempt_count中的中斷嵌套計數
irq_enter();
#ifdef CONFIG_4KSTACKS
//irq_ctx與thread_union結構相同,因此可以相互轉換
curctx = (union irq_ctx *) current_thread_info();
//本cpu的中斷棧
irqctx = hardirq_ctx[smp_processor_id()];
//當前在進程上下文,則需要切換到中斷棧
//當前在中斷上下文,則無需切換
if (curctx != irqctx) { //進程被中斷
int arg1, arg2, ebx;
//中斷棧棧頂的位置
isp = (u32*) ((char*)irqctx + sizeof(*irqctx));
//保存被中斷的進程描述符
irqctx->tinfo.task = curctx->tinfo.task;
//保存被中斷的進程的棧指針
irqctx->tinfo.previous_esp = current_stack_pointer;
asm volatile(
" xchgl %%ebx,%%esp \n" //交換esp,ebx,交換前ebx的值為被中斷進程的用戶代碼段,esp為核心棧
" call __do_IRQ \n"
" movl %%ebx,%%esp \n"
: "=a" (arg1), "=d" (arg2), "=b" (ebx) //返回值:arg1=eax, arg2=edx, ebx=ebx
: "0" (irq), "1" (regs), "2" (isp) //形參結合:$0=irq, $1=regs, $2=isp
: "memory", "cc", "ecx"
);
} else
#endif
__do_IRQ(irq, regs);
//退出中斷處理
irq_exit();
return 1;
}
// 中斷通用處理函數
// 函數主要任務:
// 1.如果為cpu本地中斷
// 1.1 向中斷控製器ack
// 1.2 調用共享該中斷號的所有中斷處理程序
// 1.3 向中斷控製器end
// 2.否則
// 2.1 獲取中斷描述符鎖
// 2.2 向中斷控製器顯示確認中斷
// 2.3 設置IRQ_PENDING表示中斷還沒有被處理
// 2.4 如果當前中斷沒有被禁用,並且中斷處理程序沒有在運行中
// 2.4.1 清除IRQ_PENDING,設置IRQ_INPROGESS, 表示中斷處理開始
// 2.4.2 釋放irq描述符鎖,執行中斷處理函數
// 2.4.3 檢查在中斷處理函數執行過程中,此中斷是否再次發生
// 2.4.3.1 如果pending設置,表示在中斷函數處理過程中,此中斷再次發生,再次執行處理函數
// 2.4.3.2 否則退出
// 2.5 向中斷控製器end
// 注:
// 1.互斥使用中斷控製器進行ack,mask,end。
// 2.中斷處理函數irq_desc_t->action在cpu間可以並行
// 3.通過irq_desc_t->status =
// IRQ_INPROGRESS 保證中斷處理函數同時隻有一個在執行
// IRQ_PENDING 保證在中斷處理函數在執行時,可以記錄第二次發生的中斷
//
1.6 fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs)
{
//irq號對應的中斷描述符
irq_desc_t *desc = irq_desc + irq;
struct irqaction * action;
unsigned int status;
//統計信息
kstat_this_cpu.irqs[irq]++;
//cpu本地中斷
if (desc->status & IRQ_PER_CPU) {
irqreturn_t action_ret;
//向中斷控製器顯式確認
desc->handler->ack(irq);
//遍曆中斷描述符下邊所有的共享中斷號的中斷處理程序
action_ret = handle_IRQ_event(irq, regs, desc->action);
desc->handler->end(irq);
return 1;
}
//互斥操作中斷控製器
spin_lock(&desc->lock);
//顯式確認
desc->handler->ack(irq);
//IRQ_REPLAY IRQ已經被禁用
//IRQ_WAITING IRQ的自動檢測
status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);
//IRQ_PENDING cpu注意到中斷,但是尚未處理它
status |= IRQ_PENDING;
//當前中斷沒有被禁用,當前中斷沒有在執行
action = NULL;
if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) {
action = desc->action;
//清除IRQ_PENDING,設置IRQ_INPROGRESS表示馬上執行處理函數
status &= ~IRQ_PENDING;
status |= IRQ_INPROGRESS;
}
desc->status = status;
if (action == NULL)
goto out;
//在處理中斷的過程中,在此發生的同一irq,通過pending標識
for (;;) {
irqreturn_t action_ret;
//中斷處理函數可以在cpu間並行執行
spin_unlock(&desc->lock);
action_ret = handle_IRQ_event(irq, regs, action);
spin_lock(&desc->lock);
//沒有新到來的中斷,退出
if (likely(!(desc->status & IRQ_PENDING)))
break;
desc->status &= ~IRQ_PENDING;
}
desc->status &= ~IRQ_INPROGRESS;
out:
//通知中斷控製器已經處理完中斷
desc->handler->end(irq);
spin_unlock(&desc->lock);
return 1;
}
最後更新:2017-04-03 14:54:06