每個(gè)中斷處理的函數(shù)存放在entry.S中的interrupt數(shù)組中,該數(shù)組有NR_IRQS個(gè)元素.
每個(gè)元素做的工作有:
ENTRY(irq_entries_start)
.rept NR_IRQS
ALIGN
1: pushl $vector-256
jmp common_interrupt
.data
.long 1b
.text
vector=vector+1
.endr
ALIGN
common_interrupt:
SAVE_ALL
movl %esp,%eax
call do_IRQ
jmp ret_from_intr
首先將中斷向量- 256保存在棧中
其中的SAVE_ALL做的工作包括:
#define SAVE_ALL \
cld; \
pushl %es; \
pushl %ds; \
pushl %eax; \
pushl %ebp; \
pushl %edi; \
pushl %esi; \
pushl %edx; \
pushl %ecx; \
pushl %ebx; \
movl $(__USER_DS), %edx; \
movl %edx, %ds; \
movl %edx, %es;
也就是保存一些寄存器, 然后調(diào)用do_IRQ函數(shù):
do_IRQ函數(shù)首先調(diào)用irq_enter()函數(shù):
#define irq_enter() \
do { \
account_system_vtime(current); \
add_preempt_count(HARDIRQ_OFFSET); \
} while (0)
其中要注意的是函數(shù)add_preempt_count, 它改變的是當(dāng)前進(jìn)程中thread_info中的成員preempt_count,它是一個(gè)32位的字段,分為幾個(gè)部分,:
0-7位: 搶占計(jì)數(shù)器, 最大值255
8-15位: 軟中斷計(jì)數(shù)器, 最大值255
16-27位: 硬中斷計(jì)數(shù)器, 最大值4096
28位: PREEMPT_ACTIVE標(biāo)志
因此,在hardirq.h中定義了幾個(gè)宏:
#define PREEMPT_BITS 8
#define SOFTIRQ_BITS 8
#define HARDIRQ_BITS 12
#define PREEMPT_SHIFT 0
#define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS)
#define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS)
#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT)
#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT)
#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT)
因此, 函數(shù)調(diào)用add_preempt_count(HARDIRQ_OFFSET)是增加其中硬中斷的計(jì)數(shù).
回到do_IRQ函數(shù)調(diào)用中,接下來(lái):
#ifdef CONFIG_4KSTACKS
curctx = (union irq_ctx *) current_thread_info();
irqctx = hardirq_ctx[smp_processor_id()];
/*
* this is where we switch to the IRQ stack. However, if we are
* already using the IRQ stack (because we interrupted a hardirq
* handler) we can't do that and just have to keep using the
* current stack (which is the irq stack already after all)
*/
if (curctx != irqctx) {
int arg1, arg2, ebx;
/* build the stack frame on the IRQ stack */
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"
" call __do_IRQ \n"
" movl %%ebx,%%esp \n"
: "=a" (arg1), "=d" (arg2), "=b" (ebx)
: "0" (irq), "1" (regs), "2" (isp)
: "memory", "cc", "ecx"
);
} else
#endif
這段代碼僅在線程棧大小是4K的情況下被調(diào)用, 有一個(gè)名為hardirq_ctx的數(shù)組保存硬中斷的請(qǐng)求棧,它的定義是:
union irq_ctx {
struct thread_info tinfo;
u32 stack[THREAD_SIZE/sizeof(u32)];
};
static union irq_ctx *hardirq_ctx[NR_CPUS];
static union irq_ctx *softirq_ctx[NR_CPUS];
也就是說(shuō), 這兩個(gè)數(shù)組的元素?cái)?shù)量由CPU數(shù)量來(lái)決定.
在系統(tǒng)初始化的時(shí)候, 調(diào)用函數(shù)irq_ctx_init, 分別把這兩個(gè)數(shù)組中的元素(irq_ctx *類(lèi)型指針)指向hardirq_stack和softirq_stack:
static char softirq_stack[NR_CPUS * THREAD_SIZE]
__attribute__((__aligned__(THREAD_SIZE)));
static char hardirq_stack[NR_CPUS * THREAD_SIZE]
__attribute__((__aligned__(THREAD_SIZE)));
因此, 上面的那段do_IRQ函數(shù)中的代碼做的工作比較當(dāng)前thread_info描述符地址(通過(guò)調(diào)用current_thread_info()函數(shù))與hardirq_ctx
的內(nèi)容, 如果相同, 說(shuō)明內(nèi)核已經(jīng)在使用硬件中斷請(qǐng)求棧了, 否則如果不相等那么就要切換內(nèi)核棧,需要保存當(dāng)前進(jìn)程描述符指針和esp寄存器.
接著, do_IRQ函數(shù)調(diào)用__do_IRQ函數(shù),這個(gè)函數(shù)的主要工作有:
// 加鎖
spin_lock(&(irq_desc[irq].lock));
// 響應(yīng)
irq_desc[irq].handler->ack(irq);
// 當(dāng)前狀態(tài)既不是IRQ_REPLAY:The IRQ line has been disabled but the previous IRQ occurrence has not yet been acknowledged to the PIC
// 也不是IRQ_WAITING:The kernel is using the IRQ line while performing a hardware device probe; moreover, the corresponding interrupt has not been raised
irq_desc[irq].status &= ~(IRQ_REPLAY | IRQ_WAITING);
// 當(dāng)前狀態(tài)為IRQ_PENDING:An IRQ has occurred on the line; its occurrence has been acknowledged to the PIC, but it has not yet been serviced by the kernel
irq_desc[irq].status |= IRQ_PENDING;
if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)) // 如果當(dāng)前狀態(tài)不是IRQ_DISABLED 或者 IRQ_INPROGRESS
&& irq_desc[irq].action) { // action指針有效
irq_desc[irq].status |= IRQ_INPROGRESS; // 設(shè)置當(dāng)前當(dāng)前狀態(tài)為IRQ_INPROGRESS: A handler for the IRQ is being executed
do {
irq_desc[irq].status &= ~IRQ_PENDING; // 設(shè)置當(dāng)前當(dāng)前狀態(tài)不是IRQ_PENDING,因?yàn)橄旅嬉_(kāi)始處理了
spin_unlock(&(irq_desc[irq].lock));
handle_IRQ_event(irq, regs, irq_desc[irq].action); // 處理事件
spin_lock(&(irq_desc[irq].lock));
} while (irq_desc[irq].status & IRQ_PENDING); // 如果當(dāng)前狀態(tài)還是IRQ_PENDING循環(huán)繼續(xù)
irq_desc[irq].status &= ~IRQ_INPROGRESS; // 設(shè)置當(dāng)前狀態(tài)不是IRQ_INPROGRESS
}
irq_desc[irq].handler->end(irq);
spin_unlock(&(irq_desc[irq].lock));
在循環(huán)處理IRQ請(qǐng)求的時(shí)候, 最開(kāi)始要設(shè)置狀態(tài)為 IRQ_INPROGRESS同時(shí)不是IRQ_PENDING, 這個(gè)循環(huán)處理IRQ請(qǐng)求的過(guò)程在當(dāng)前狀態(tài)是IRQ_PENDING則一直進(jìn)行下去,
當(dāng)該循環(huán)處理完畢之后, 再將狀態(tài)設(shè)置為IRQ_INPROGRESS.
在從__do_IRQ函數(shù)返回后, 調(diào)用irq_exit函數(shù):
void irq_exit(void)
{
account_system_vtime(current);
sub_preempt_count(IRQ_EXIT_OFFSET);
if (!in_interrupt() && local_softirq_pending())
invoke_softirq();
preempt_enable_no_resched();
}
該函數(shù)首先調(diào)用sub_preempt_count減少搶占計(jì)數(shù), 然后如果當(dāng)前不在中斷狀態(tài)以及當(dāng)前有未處理的軟中斷(softirq)則調(diào)用invoke_softirq函數(shù)(其實(shí)就是do_softirq函數(shù))
處理軟中斷,最后調(diào)用preempt_enable_no_resched允許內(nèi)核搶占.