什么是中断
中断就是被打断的意思。生活中充满了中断,甚至可以说生活中离不开中断。
比如我们正在写代码时,被领导叫出去谈话,打断了当前的工作,这就是中断。而且这个中断有点“意外”,因为没人提前告知我们。
当然也有很多可以预期的中断,比如水喝多之后要去厕所,就必须打断当前工作,这就是可以预期的,还有中午要出去吃饭,也要打断当前工作,自然也算中断。
宏观上看计算机是在运行着一些软件,其实质是CPU在执行着这些软件的程序代码。CPU的执行过程也是可以被打断的,这就是和计算机相关的中断。
因此,不管采用什么形式或基于什么原因,只要是打断了CPU的当前执行,都算中断。
为什么会有中断
生活中的中断存在的很自然,似乎不需要什么理由。同样,计算机中的中断也不难理解。
CPU无疑是一个计算机的核心部件,许多工作都需要它参与,比如鼠标、键盘、网卡、磁盘等等这些外部设备的正常运转都离不开它。
同样,CPU又是非常宝贵的计算资源,为了使资源利用率最大化,我们必须制定出一个较好的和CPU打交道的方式,来尽量不浪费CPU的时间。这种方式就是中断。
就拿网卡来讲,CPU并不知道什么时候数据包会来到,但又不可能一直等着接收数据包,要是这样的话效率太低了。所以合理的办法是,数据包来到之后,进行一个通知,然后CPU再参与处理,怎么通知呢?
答案当然是中断!键盘、鼠标亦是如此!因此中断传达的就是,当把所有的准备工作都做好后,比如数据已到位,再去中断CPU,让它参与处理。这样才能保证不浪费它的时间。
现在的计算机都可以同时运行多个任务,但是CPU通常都是一个,我们知道一个CPU在同一时刻只能运行一个程序,所以底层都是通过时间片来轮换执行的。
计算机里都有定时机制,当一个时间片用完时,定时器会给CPU一个中断,告诉它当前正在执行的程序时间用完了,于是CPU就把它换出来,转而执行下一个程序。
这种时间片的轮换机制就是通过中断实现的。此外,计算机里的一些其它功能,也是通过中断实现的。
中断的本质
中断本质上是一种特殊的电信号,由硬件设备生成,并直接送入中断控制器的输入引脚上,再由中断控制器向CPU发送INT信号申请CPU来执行刚才的硬件操作,并且将中断类型号也发给CPU,CPU一经检测到此信号,便中断自己当前工作转而处理中断。
CPU根据中断类型号找到它的中断向量(即中断程序在内存中的地址),然后去执行这段程序(这段程序已经写好,在内存中),执行完后再向中断控制器发送一个INTA信号表示其已经处理完刚才的申请。此时CPU就可以继续做它刚才被打断的工作了。
不同的设备对应的中断不同,而每个中断都通过一个唯一的数字标识。这些中断值通常被称为中断请求(IRQ)线。比如,IRQ0是时钟中断,而IRQ1是键盘中断。并不是所有的中断号都这样严格定义,像PCI总线上的设备,中断就是动态分配的。
中断是申请并获得CPU处理的一种机制,是外部设备与CPU之间通信的方式,如硬盘读写服务请求中断。当然中断也可以和硬件设备无关,如服务请求,任务完成提醒等都是软件中断。
中断是异步的,因为从逻辑上来说,中断的产生与当前正在执行的进程无关,即中断可以比较自由的产生,所以中断也是CPU实现并发执行的关键,也是操作系统工作的根本。
当发生了中断,就意味着需要操作系统的介入,开展管理工作。由于操作系统的管理工作(如进程切换、分配IO设备)需要使用特权指令,因此CPU要从用户态转换为核心态。
中断就可以使CPU从用户态转换为核心态,使操作系统获得计算机的控制权,并且中断是唯一途径。
中断的分类
根据中断源的不同,可把中断分为硬件中断和软中断两大类。
硬件中断:
硬件中断是由与系统相连的外设(比如网卡 硬盘 键盘等)自动产生的,每个设备或设备集都有他自己的IRQ(中断请求线), 基于IRQ, CPU可以将相应的请求分发到相应的硬件驱动上(注: 硬件驱动通常是内核中的一个子程序, 而不是一个独立的进程)。比如当网卡收到一个数据包的时候, 就会发出一个中断。
处理中断的驱动是需要运行在CPU上的, 因此, 当中断产生时, CPU会暂时停止当前执行的程序转而执行中断请求。一个中断只能中断一颗CPU(也有一种特殊情况, 就是在大型主机上是有硬件通道的, 它可以在没有主CPU的支持下, 同时处理多个中断)。
硬件中断可以直接中断CPU,它会引起内核中相关代码被触发,对于那些需要花费时间去处理的进程,中断代码本身也可以被其他的硬件中断中断。
对于时钟中断,内核调度代码会将当前正在运行的代码挂起,从而让其它代码来运行。它的存在是为了让调度代码(或称为调度器)可以调度多任务。
软中断:
软中断的处理类似于硬中断,但只有当前正在运行的代码(或进程)才会产生软中断。
通常软中断是对一些I/O的请求。
软中断不会直接中断CPU,而是一种需要内核为正在运行的进程去做一些事情(通常为I/O)的请求。
有一个特殊的软中断是Yield调用,它的作用是请求内核调度器去查看是否有一些其他的进程可以运行。
硬件中断和软中断的区别:
硬件中断是由外设引发的,软中断是执行中断指令产生的。
硬件中断的中断号是由中断控制器提供的,软中断的中断号由指令直接指出,无需使用中断控制器。
硬件中断是可屏蔽的,软中断不可屏蔽。
硬件中断处理程序要确保它能快速地完成任务,这样程序执行时才不会等待较长时间,称为上半部。
软中断处理硬中断未完成的工作,是一种推后执行的机制,属于下半部。
同样根据来源的不同,又把中断分为内部中断和外部中断。
内部中断的信号来自于CPU内部,与当前执行的指令有关。而内部中断按事件是否正常来划分可分为软中断和异常两种。
其中软中断是由软件主动发起的中断,常被用于系统调用(system call),而异常则是指令执行期间CPU内部产生的错误引起的。异常也和不可屏蔽中断一样不受eflags寄存器的IF位影响,区别在于不可屏蔽中断发生的事件会导致处理器无法运行(如断电、电源故障等),而异常则是影响系统正常运行的中断(如除0、越界访问等)。
外部中断的中断事件来源于CPU外部,与当前执行的指令无关。必然是某个硬件产生的,所以外部中断又被称为硬件中断(hardware interrupt)。计算机的外部设备,如网卡、声卡、显卡等都能产生中断。如用户强制结束一个进程、IO设备的操作便会发生中断信号。
按照是否可以被屏蔽,外部中断还可分为可屏蔽中断(maskable interrupt)和不可屏蔽中断(non-maskable interrupt)两种。
不可屏蔽中断源一旦提出请求,CPU必须无条件响应,而对于可屏蔽中断源的请求,CPU可以响应,也可以不响应。
在x86架构的处理器中,CPU一般设置两根中断请求输入线:可屏蔽中断请求INTR(Interrupt Require)和不可屏蔽中断请求NMI(Nonmaskable Interrupt)。
对于可屏蔽中断,除了受本身的屏蔽位的控制外,还都要受一个总的控制,即CPU标志寄存器中的中断允许标志位IF(Interrupt Flag)的控制,IF位为1,可以得到CPU的响应,否则,得不到响应。IF位可以由用户控制,指令STI或Turbo c的Enable()函数,将IF位置1(开中断),指令CLI或Turbo_c 的Disable()函数,将IF位清0(关中断)。
典型的非屏蔽中断源的例子是电源掉电、内存校验错误,一旦出现,必须立即无条件地响应,否则进行其他任何工作都是没有意义的。
典型的可屏蔽中断源的例子是外设或一些接口功能,如打印机中断,CPU对打印机中断请求的响应可以快一些,也可以慢一些,因为让打印机等待会儿是完全可以的。
中断处理过程
在微机系统中,对于外部中断,中断请求信号是由外部设备产生,并施加到CPU的NMI或INTR引脚上,CPU通过不断地检测NMI和INTR引脚信号来识别是否有中断请求发生。
对于内部中断,中断请求方式不需要外部施加信号激发,而是通过内部中断控制逻辑去调用。无论是外部中断还是内部中断,中断处理过程都要经历以下步骤:
请求中断→响应中断→关闭中断→保留断点→中断源识别→保护现场→中断服务子程序→恢复现场→中断返回。
请求中断
当某一中断源需要CPU为其进行中断服务时,就输出中断请求信号,使中断控制系统的中断请求触发器置位,向CPU请求中断。每一个中断都通过一个唯一的数字标识。这些数字表示的中断值被称为中断请求(IRQ, Interrupt Request)线,也可称为IRL(Interrupt Request Lines)。系统要求中断请求信号一直保持到CPU对其进行中断响应为止。
中断响应
CPU对系统内部中断源提出的中断请求必须响应,而且自动取得中断服务子程序的入口地址,执行中断服务子程序。对于外部中断,CPU在执行当前指令的最后一个时钟周期去查询INTR引脚,若查询到中断请求信号有效,同时在系统开中断(即IF=1)的情况下,CPU向发出中断请求的外设回送一个低电平有效的中断应答信号,作为对中断请求INTR的应答,系统自动进入中断响应周期。
关闭中断
CPU响应中断后,输出中断响应信号,自动将状态标志寄存器FR或EFR的内容压入堆栈保护起来,然后将FR或EFR中的中断标志位IF与陷阱标志位TF清零,从而自动关闭外部硬件中断。因为CPU刚进入中断时要保护现场,主要涉及堆栈操作,此时不能再响应中断,否则将造成系统混乱。
保护断点
保护断点就是将CS和IP/EIP的当前内容压入堆栈保存,以便中断处理完毕后能返回被中断的原程序继续执行,这一过程也是由CPU自动完成。
中断源识别
当系统中有多个中断源时,一旦有中断请求,CPU必须确定是哪一个中断源提出的中断请求,并由中断控制器给出中断服务子程序的入口地址,装入CS与IP/EIP两个寄存器。CPU转入相应的中断服务子程序开始执行。
保护现场
主程序和中断服务子程序都要使用CPU内部寄存器等资源,为使中断处理程序不破坏主程序中寄存器的内容,应先将断点处各寄存器的内容压入堆栈保护起来,再进入的中断处理。现场保护是由用户使用PUSH指令来实现的。
中断服务
中断服务是执行中断的主体部分,不同的中断请求,有各自不同的中断服务内容,需要根据中断源所要完成的功能,事先编写相应的中断服务子程序存入内存,等待中断请求响应后调用执行。所以产生中断的每个设备都有一个相应的中断处理程序。一个设备的中断处理程序是它设备驱动程序的一部分。
恢复现场
当中断处理完毕后,用户通过POP指令将保存在堆栈中的各个寄存器的内容弹出,即恢复主程序断点处寄存器的原值。
中断返回
在中断服务子程序的最后要安排一条中断返回指令IRET,执行该指令,系统自动将堆栈内保存的IP/EIP和CS值弹出,从而恢复主程序断点处的地址值,同时还自动恢复标志寄存器FR或EFR的内容,使CPU转到被中断的程序中继续执行。
异常与中断
异常与中断不同,它在产生时必须考虑与处理器时钟同步。实际上,异常也称为同步中断。比如,在处理器执行到由于编程失误而导致的错误指令的时候,或者在执行期间出现特殊情况(缺页),必须靠内核来处理的,处理器就产生一个异常。
异常是由当前正在执行的进程产生。异常包括很多方面,有出错(fault),有陷入(trap),也有可编程异常(programmable exception)。
出错(fault)和陷入(trap)最重要的一点区别是他们发生时所保存的EIP值的不同。出错(fault)保存的EIP指向触发异常的那条指令,而陷入(trap)保存的EIP指向触发异常的那条指令的下一条指令。
因此,当从异常返回时,出错(fault)会重新执行那条指令,而陷入(trap)就不会重新执行。这一点实际上也是相当重要的,比如我们熟悉的缺页异常(page fault),由于是fault,所以当缺页异常处理完成之后,还会去尝试重新执行那条触发异常的指令(那时多半情况是不再缺页)。
陷入的最主要的应用是在调试中,被调试的进程遇到你设置的断点,会停下来等待你的处理,等到你让其重新执行了,它当然不会再去执行已经执行过的断点指令。
可编程中断:这类中断可由编程者用int指令来触发。在Linux中,使用了一个,也是唯一的一个可编程中断,就是int 0x80系统调用。硬件对可编程中断的处理与对trap的处理类似,即从这类异常返回时也是返回到触发异常的下一条指令。
(END)
作者现任架构师,工作11年,Java技术栈,计算机基础,用心写文章,喜欢研究技术,崇尚简单快乐。追求以通俗易懂的语言解说技术,希望所有的读者都能看懂并记住。
>>> 热门文章集锦 <<<
毕业10年,我有话说
我是一个协程
我是一个跳表
线程池开门营业招聘开发人员的一天
递归 —— 你值得拥有
迄今为止最好理解的ZooKeeper入门文章
基于角色的访问控制(RBAC)
11年码农的肺腑之言,如何成为一个优秀的程序员,送给渴望优秀的人
非著名架构师告诉你,代码该如何写,才能自己写的容易别人看的也不痛苦
迄今为止最硬核的「Java8时间系统」设计原理与使用方法
任何人都需要知道的「世界时间系统」构成原理,尤其开发人员
彻彻底底给你讲明白啥是SpringMvc异步处理
【面试】我是如何面试别人List相关知识的,深度有点长文
我是如何在毕业不久只用1年就升为开发组长的
爸爸又给Spring MVC生了个弟弟叫Spring WebFlux
【面试】我是如何在面试别人Spring事务时“套路”对方的
【面试】Spring事务面试考点吐血整理(建议珍藏)
【面试】吃透了这些Redis知识点,面试官一定觉得你很NB(干货 | 建议珍藏)
【面试】如果你这样回答“什么是线程安全”,面试官都会对你刮目相看(建议珍藏)
【面试】迄今为止把同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲的这么清楚的好文章(快快珍藏)
【面试】一篇文章帮你彻底搞清楚“I/O多路复用”和“异步I/O”的前世今生(深度好文,建议珍藏)
【面试】如果把线程当作一个人来对待,所有问题都瞬间明白了
Java多线程通关———基础知识挑战
品Spring:帝国的基石