中断子系统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;
}
时间: 2024-08-23 04:13:00

中断子系统3_中断入口处理的相关文章

中断子系统7_中断出口处理

// 中断公共入口 1.1 common_interrupt: //所有可屏蔽中断函数的公共入口 SAVE_ALL //寄存器入栈 movl %esp,%eax // eax保存栈顶指针 call do_IRQ //中断处理函数 jmp ret_from_intr //从中断返回 // 从中断返回 // 函数主要任务: // 1.确定中断发生前的运行模式 // 1.1 恢复内核执行路径 // 1.1.1 检查是否内核抢占,执行内核抢占 // 1.1.2 恢复硬件上下文 // 1.1.3.iret

中断子系统1_中断子系统初始化

// 控制单元对中断信号的处理: // 当cpu执行一条指令后,cs和eip包含下一条要执行的指令的逻辑地址,在处理那条指令之前, // 控制单元会检查在运行前一条指令时是否发生了一个中断或异常,如果发生了一个中断或异常, // 控制单元执行下列操作: // 1.确定与中断或异常关联的向量i // 2.读入由idtr寄存器指向的IDT表中的第i项 // 3.从gdtr寄存器获得GDT的基地址,并在GDT中查找,以读取IDT表项中的选择符所标识的段描述符. // 这个描述符指定中断或异常处理程序的

中断子系统4_i8259a中断控制器

// 8259A 中的寄存器: // ICW: Initialization Command Word,初始化命令寄存器 // OCW: Operation Command Word,操作命令字,用于控制 8259A // IRR: Interrupt Request Register,中断请求寄存器,共 8bit,对应 IR0~IR7 八个中断管脚.当某个管脚的中断请求到来后, // 若该管脚没有被屏蔽,IRR 中对应的 bit 被置1.表示 PIC 已经收到设备的中断请求,但还未提交给 CP

Linux的IRQ中断子系统分析

本文以Linux中断子系统架构为视角,旨在提供一个对Linux中断系统的全局认识,不涉及具体实现细节. 一.Linux中断子系统架构 在Linux中断子系统(generic irq)出现之前,内核使用__do_IRQ处理所有的中断,这意味着__do_IRQ中要处理各种类型的中断,这会导致软件的复杂性增加,层次不分明,而且代码的可重用性也不好.通用中断子系统的原型最初出现于ARM体系中,一开始内核的开发者们把3种中断类型区分出来(电平中断.边缘中断.简易中断),后来又针对某些需要回应eoi(end

中断子系统5_电流层处理

// 电流类型: // 1.电平型:handle_level_irq /* * Level type interrupts are active as long as the hardware line has * the active level. This may require to mask the interrupt and unmask * it after the associated handler has acknowledged the device, so the * in

linux kernel的中断子系统之(三):IRQ number和中断描述符【转】

转自:http://www.wowotech.net/linux_kenrel/interrupt_descriptor.html 一.前言 本文主要围绕IRQ number和中断描述符(interrupt descriptor)这两个概念描述通用中断处理过程.第二章主要描述基本概念,包括什么是IRQ number,什么是中断描述符等.第三章描述中断描述符数据结构的各个成员.第四章描述了初始化中断描述符相关的接口API.第五章描述中断描述符相关的接口API. 二.基本概念 1.通用中断的代码处理

msp430 中断 结构-msp430F5529A中断程序运行卡顿

问题描述 msp430F5529A中断程序运行卡顿 菜鸟写了一段程序如下,希望实现电子秤的功能.中断的话是两个按键触发的,首次按下S1,清零:第二次按下S1进行称重:按下S2则关机. 真正运行起来卡的不行甚至动不起来,请问是不是我的主函数设计有问题? 还有求多个触发中断的例程,感觉自己写的结构确实很不合理. 谢谢啦 void main(void) { P1DIR = 0X00;//调零,按下S1+开始称重,按下S1:设置P1.7为输入状态 P1IE = 0X80;//设置P1.7开中断 P1IE

linux驱动开发--中断:按键中断

1.中断定义 中断是指cpu在执行过程中,出现了某些突发事件时cpu必须暂停执行当前的程序,转去处理突发事件,处理完毕后cpu又返回原程序被中断的位置并继续执行. 2.中断分类 3.Linux中断处理程序结构 a.在Linux系统中,中断处理程序分解为两个半部:顶半部(TopHalf)和底半部(BottomHalt). b.顶半部:完成尽可能少的比较紧急的功能,往往只是简单的读取寄存器中的中断状态并清除中断标志后就进行"登记中断"的工作,也就是将底半部处理程序挂到该设备的底半部执行队列

win7中断-关于win7中的中断号的中断问题

问题描述 关于win7中的中断号的中断问题 我看书上说x86不是有24个中断吗?为什么我用win7 32位查看的时候,中断有190了呢? 解决方案 你的教材说的是实模式,这个在386(80386是1986年问世的,比你岁数都大)以后都淘汰了,保护模式的中断机制完全不同.你学的是计算机科学还是计算机考古学?