中断的响应和服务

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Rebirth__Man/article/details/78690235

CPU从中断控制器取得中断向量,然后根据具体的中断向量从中断向量表IDT中找到相应的表项(中断门),CPU根据中断门的设置而到达了该通道的总服务程序入口,如果中断是当CPU在用户空间中运行时发生的,当前的运行级别CPL为3;而中断服务程序属于内核,其运行级别为DPL为0,二者不同。所以,CPU要从寄存器TR所指的当前TSS中取出用于内核(0级)的堆栈指针,并把堆栈切换到内核堆栈,即当前进程的系统空间堆栈。另外,由于穿过的是中断门(而不是陷阱门),所以中断已被关闭,在重新开启之前再没有其它中断发生。

中断发生会进入公用中断的总入口,进入之前会将中断号减去256使其变成负值(区别中断号和系统调用号),可以明确中断的来源;

common_interrupt(公用中断)主要的是宏操作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;



这里要指出,第一是标志位寄存器EFLAGS的内容并不是在SAVE_ALL中保存的,这是因为CPU在进入中断服务时已经把他的内容连同返回地址一起压入堆栈了。第二是段寄存器DS和ES原来的内容在堆栈中,然后就被改成指向内核的_ _KERNAL_DS(运行级别改变了)。至于原来的堆栈寄存器SS和堆栈指针ESP的内容,则或者已经压入堆栈(如果更换堆栈),或者继续使用而无需保存(如果不更换堆栈)。


进入中断服务程序时系统堆栈示意图

当SAVE_ALL结束以后,又将一个程序标号(入口)ret_from_intr压入堆栈,

然后进入do_IRQ()函数。当执行完do_IRQ()就返回ret_from_intr继续执行。

common_interrupt->do_IRQ( )

unsigned int do_IRQ(struct pt_regs regs)  

pt_regs是一个数据结构,而不是指针;在返回地址以上位置应该是一个数据结构(pt_regs)的映像。

在do_IRQ()中找到中断源。(0xffff03,把高位屏蔽掉变成0x03

找到中断请求号,再从数组irq_desc[ ]中找到的中断请求队列。

然后查看标志位,当IRQ_INPROGRESS标志为1有两种情况,1、在多处理器SMP系统结构中,一个CPU正在中断服务,而另一个CPU又进入了do_IRQ();2、在单处理器系统中CPU已经在中断服务程序中,但是因某种原因有将中断开启了,而且在同一个中断通道又产生了一次中断。

IRQ_PENDING用来避免同一中断源或同一中断通道嵌套。

common_inteerupt -> do_IRQ()> handle_IRQ_event( )

这个函数依次执行队列中的各个中断服务程序,让他们辨别本次中断请求是否来自各自的服务对象。即中断源,如果是就进而提供相应的服务。

当运行完返回ret_from_intr时,获得当前进程的task_struct数据结构置于寄存器EBX,然后检验中断前夕CPU是否运行在VM86模式?中断前夕CPU运行于用户空间还是系统空间。

如果中断发生在系统空间,控制直接转移到restore_all,而如果发生在用户空间,则先转移到ret_with_reschedule检验是否需要调度然后转到restore_all。

最后处理完毕到达restore_all,与save_all对应。















展开阅读全文

没有更多推荐了,返回首页