中断子系统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