每个中断处理的函数存放在entry.S中的interrupt数组中,该数组有NR_IRQS个元素.
每个元素做的工作有:
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;
也就是保存一些寄存器, 然后调用do_IRQ函数:
do_IRQ函数首先调用irq_enter()函数:
#define
irq_enter() /
do
{ /
account_system_vtime(current); /
add_preempt_count(HARDIRQ_OFFSET); /
}
while
(
0
)
其中要注意的是函数add_preempt_count, 它改变的是当前进程中thread_info中的成员preempt_count,它是一个32位的字段,分为几个部分,:
0-7位: 抢占计数器, 最大值255
8-15位: 软中断计数器, 最大值255
16-27位: 硬中断计数器, 最大值4096
28位: PREEMPT_ACTIVE标志
因此,在hardirq.h中定义了几个宏:
#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)
因此, 函数调用add_preempt_count(HARDIRQ_OFFSET)是增加其中硬中断的计数.
回到do_IRQ函数调用中,接下来:
#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的情况下被调用, 有一个名为hardirq_ctx的数组保存硬中断的请求栈,它的定义是:
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];
也就是说, 这两个数组的元素数量由CPU数量来决定.
在系统初始化的时候, 调用函数irq_ctx_init, 分别把这两个数组中的元素(irq_ctx *类型指针)指向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函数中的代码做的工作比较当前thread_info描述符地址(通过调用current_thread_info()函数)与hardirq_ctx
的内容, 如果相同, 说明内核已经在使用硬件中断请求栈了, 否则如果不相等那么就要切换内核栈,需要保存当前进程描述符指针和esp寄存器.
接着, do_IRQ函数调用__do_IRQ函数,这个函数的主要工作有:
//
加锁
spin_lock(
&
(irq_desc[irq].
lock
));
//
响应
irq_desc[irq].handler
->
ack(irq);
//
当前状态既不是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);
//
当 前状态为 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))
//
如果当前状态不是IRQ_DISABLED 或者 IRQ_INPROGRESS
&&
irq_desc[irq].action) {
//
action指针有效
irq_desc[irq].status
|=
IRQ_INPROGRESS;
//
设置当前当前状态为IRQ_INPROGRESS: A handler for the IRQ is being executed
do
{
irq_desc[irq].status
&=
~
IRQ_PENDING;
//
设置当前当前状态不是IRQ_PENDING,因为下面要开始处理了
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);
//
如果当前状态还是IRQ_PENDING循环继续
irq_desc[irq].status
&=
~
IRQ_INPROGRESS;
//
设置当前状态不是IRQ_INPROGRESS
}
irq_desc[irq].handler
->
end(irq);
spin_unlock(
&
(irq_desc[irq].
lock
));
在循环处理IRQ请求的时候, 最开始要设置状态为 IRQ_INPROGRESS同时不是IRQ_PENDING, 这个循环处理IRQ请求的过程在当前状态是IRQ_PENDING则一直进行下去,
当该循环处理完毕之后, 再将状态设置为IRQ_INPROGRESS.
在从__do_IRQ函数返回后, 调用irq_exit函数:
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();
}
该函数首先调用sub_preempt_count减少抢占计数, 然后如果当前不在中断状态以及当前有未处理的软中断(softirq)则调用invoke_softirq函数(其实就是do_softirq函数)
处理软中断,最后调用preempt_enable_no_resched允许内核抢占.
转载自:http://www.cppblog.com/converse/archive/2009/05/03/81773.html