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

你可能感兴趣的:(Linux中断子系统)