疑问
- CPU获得硬中断后,执行中断处理程序,这个“中断处理程序”就是“驱动程序”,对于一个设备发来的中断,不应该由这个设备的“中断处理程序”(驱动)处理吗?“中断处理程序”其实是在内核中的一段代码
中断的概念
中断,在系统的不同设计者眼中完全不是一个东西,这是为什么大家对它有那么多误解,因为不少人把用于其他名称空间的概念直接用于你自己的名称空间了。
我们先看一张全景(示意)图:
我的理解:中断系统
当一个设备发送一个中断给CPU的时候,可能需要先经过一个中介“中断控制器”,也有一个东西叫“可编程中断控制器”,“中断控制器”将中断信号 会通过管角进入CPU,这个时候CPU也许在执行一个进程,CPU依然会保存现场,迎接中断,根据中断类型决定去执行哪个“中断处理程序”,当CPU获得一个中断后,会执行“关中断”操作,即CPU要将这个“中断处理程序”执行完再说,CPU 告诉 “中断控制器”,你不要发中断信号给我,当然,实际上,“中断处理程序”都是相当短的一段代码,CPU执行的很快,当CPU执行完这“中断处理程序”,进行正常的“进程调度”,恢复原来的上下文,根据调度算法去执行某个进程。
设备眼中的中断,中断控制器眼中的中断,还有CPU眼中的中断,进程眼中的中断
一个系统的的中断系统通常是类似这样的组成,我们应该注意到,设备眼中的中断,中断控制器眼中的中断,还有CPU眼中的中断,都不是同一个概念。所以,当我们说“关中断”一类的说法的时候,对它们三者也是不一样的。
设备的中断,是设备要产生一个事件通知CPU,事件的产生的方法有很多,最简单的是在一条信号线上产生特定的电平(电平中断,比如平时都是高电平,拉低了就表示有中断了),或者产生一次电平变化(边缘触发中断),复杂的可以很复杂,比如在总线上发送特定消息或者消息序列。对设备“关中断”,指的仅仅是让这个设备不要再提供中断信号了,但如果中断控制器已经获得这个中断信号,这个中断信号还是会报到CPU上的。
中断控制器
中断控制器,是对多个设备的中断进行采样,排队,分发的机制。
对中断控制器说:关中断,是让中断控制器不要给CPU(或者上级)发送中断信号了,设备报不报信号上来,这些信号是否被排队,那是另一个问题。
最后,是对我们软件程序员最熟悉的CPU了,CPU的中断,是CPU核上有一条中断线,当这条线加上合适的电平或者信号,CPU核就会从当前的执行上下文中,直接跳转到中断处理程序中执行。在CPU的角度上关中断,就是跟CPU说:就算现在你的中断线上有中断,也不要执行“跳转到中断处理程序”这个动作。
CPU能认识的就仅仅是中断线和中断处理程序这些概念。所谓线程,进程,软中断等概念,是软件发明出来的,CPU是不认识的。所谓线程,本质上是保存CPU运行状态的一种形式,CPU的运行状态,就是CPU的所有寄存器的内容的集合(包括用来控制中断的寄存器),线程的作用就是可以把这些寄存器都保存下来(其实还有软件本身的堆栈等其他信息,但我们这里不关心软件,先忽略),然后用另一个保存的状态刷新CPU的状态,让CPU感觉自己在运行到另一个上下文上。OS对CPU不断进行状态的切换,保存上一个状态,加载下一个线程的状态,就实现线程切换了。至于进程,本质上可以认为是线程切换的同时也会切换地址空间。这里我们混用这两个概念。
所以,进程也有关中断的概念,关进程的中断,会导致本进程运行的过程中,CPU不再接受中断,但如果这个进程切换到另一个进程上,那就按新进程的上下文来说了。
在大部分的通用CPU上(请注意,这里说的是CPU这个视角),中断是可嵌套的,就算你在处理中断,只要CPU中断线上又来信号的,它就有可能再次进入中断处理程序。为了避免这种情况,我们通常需要在中断控制器上玩游戏:当中断控制器给一个CPU发了一个中断,它就不会再发下一个中断(这也有很多变种,我们只说一种常见的实现),要等CPU主动对中断控制器发起EOI操作,才会允许下一个中断信号去激发CPU。Linux系统的中断处理的名称空间中,把进入中断处理,到发出EOI这一段,称为硬中断,把EOI之后,再回到原来被中断之前的程序之前的这一段,称为软中断。但这些是软件的概念,中断对于CPU来说,只有开始,没有结束一说,每次中断的发生,都只是一次强行跳转的过程。
现在可以回到问题本身了,从设备的角度,给CPU发中断,CPU可能正在运行任何进程,无论是哪个进程,效果都是陷入到内核中(通常所有进程的内核是共享的),是内核的(其实是驱动的)的中断处理程序在处理这个中断,并在返回到用户空间前,保存中断上收到的信息,然后进入调度程序,调度当前或者下一个进程运行。剩下的事情,就是进程和驱动之间的恩怨了。
至于多长时间报一个中断,这个问题完全取决于你如何分布设备和CPU之间的压力,如果你发下去一个字节,要等1分钟IP才能处理完,让CPU不断给你polling设备准备好没有,(这种情况,)那你当然最好是为此设置一个中断了。如果你的IP完成这个处理的时间和内存操作差不多,你完全可以一直写下去,每隔一段时间yield一下就好了。更常见的情况是,你在Device上设置了一个queue,填满以后就扔给Device处理,等它处理完了,CPU回去该干嘛干嘛,直到中断来了,再填充下一轮。
硬中断:
-
硬中断是由硬件产生的,比如,像磁盘,网卡,键盘,时钟等。每个设备或设备集都有它自己的IRQ(中断请求)。基于IRQ,CPU可以将相应的请求分发到对应的硬件驱动上(注:硬件驱动通常是内核中的一个子程序,而不是一个独立的进程)。
-
处理中断的驱动是需要运行在CPU上的,因此,当中断产生的时候,CPU会中断当前正在运行的任务,来处理中断。在有多核心的系统上,一个中断通常只能中断一颗CPU(也有一种特殊的情况,就是在大型主机上是有硬件通道的,它可以在没有主CPU的支持下,可以同时处理多个中断。)。
-
硬中断可以直接中断CPU。它会引起内核中相关的代码被触发。对于那些需要花费一些时间去处理的进程,中断代码本身也可以被其他的硬中断中断。
-
对于时钟中断,内核调度代码会将当前正在运行的进程挂起,从而让其他的进程来运行。它的存在是为了让调度代码(或称为调度器)可以调度多任务。
软中断:
-
软中断的处理非常像硬中断。然而,它们仅仅是由当前正在运行的进程所产生的。
-
通常,软中断是一些对I/O的请求。这些请求会调用内核中可以调度I/O发生的程序。比如:一个进程需要从磁盘读数据!对于某些设备,I/O请求需要被立即处理,而磁盘I/O请求通常可以排队并且可以稍后处理。根据I/O模型的不同,进程或许会被挂起直到I/O完成,此时内核调度器就会选择另一个进程去运行。I/O可以在进程之间产生并且调度过程通常和磁盘I/O的方式是相同。
-
软中断仅与内核相联系。而内核主要负责对需要运行的任何其他的进程进行调度。一些内核允许设备驱动的一些部分存在于用户空间,并且当需要的时候内核也会调度这个进程去运行。
-
软中断并不会直接中断CPU。也只有当前正在运行的代码(或进程)才会产生软中断。这种中断是一种需要内核为正在运行的进程去做一些事情(通常为I/O)的请求。有一个特殊的软中断是Yield调用,它的作用是请求内核调度器去查看是否有一些其他的进程可以运行。
问题解答:
-
问:对于软中断,I/O操作是否是由内核中的I/O设备驱动程序完成?
答:对于I/O请求,内核会将这项工作分派给合适的内核驱动程序,这个程序会对I/O进行队列化,以可以稍后处理(通常是磁盘I/O),或如果可能可以立即执行它。通常,当对硬中断进行回应的时候,这个队列会被驱动所处理。当一个I/O请求完成的时候,下一个在队列中的I/O请求就会发送到这个设备上。
-
问:软中断所经过的操作流程是比硬中断的少吗?换句话说,对于软中断就是:进程 ->内核中的设备驱动程序;对于硬中断:硬件->CPU->内核中的设备驱动程序?
答:是的,软中断比硬中断少了一个硬件发送信号的步骤。产生软中断的进程一定是当前正在运行的进程,因此它们不会中断CPU。但是它们会中断调用代码的流程。
如果硬件需要CPU去做一些事情,那么这个硬件会使CPU中断当前正在运行的代码。而后CPU会将当前正在运行进程的当前状态放到堆栈(stack)中,以至于之后可以返回继续运行。这种中断可以停止一个正在运行的进程;可以停止正处理另一个中断的内核代码;或者可以停止空闲进程。