到底什么是软中断,在提这个问题的时候你理解什么是中断了吗?我不准备在这里解释这些概念。同样的软中断,不同的操作系统实现是不一样的,为什么不一样,
这是操作系统的总体设计理念决定的,比如:中断线程化实际上并不是什么大部了的,当你知道了那个内核对进程和线程有偏爱时,你就知道设计者实际想把什么都 往线程靠拢,于是把中断也线程化就不足为奇了,执行绪中断化也是同样的道理,因为设计者想把什么都想成中断,这样更易于实现完全异步操作系统,但是为何不
把线程也中断化呢?因为:1.
进程/
线程是一个古老的概念,人都是有惰性的,难以改变已经成型的东西,更何况消灭它呢;2.
我们讨论的系统内核,内核中断 化就已经完成了设计,而进程/
线程都是用户空间的概念,内核仅仅是个管理的东西,我们要管理的是进程/
线程,而不是让它们来参与管理系统(unix /linux
中,它确实参与了管理);3.
这个问题很较真,没有意义。以下是一些论调:
/*
开始
对比一下两本书的同一段落
【4.4BSD
操作系统设计与实现】:
实 现低优先级处理的机制被称作软件中断。一般来说,都是由一个高优先级的中断创建一个将在低优先级中完成的工作队列。在把工作请求放到队列中之后,高优先级
的中断会把这些请求安排在某个低优先级中运行。当机器的优先级低于那个低优先级之后,就会产生一个中断来调用所需的函数。如果在处理请求期间又来了一个高 优先级的中断,处理过程就会像任何其它的低优先级任务一样被抢占。在某些架构上,前述中断就是由软件指令产生的真实的硬件陷阱。而其它的一些架构则通过在
恰当的时候监视由中断处理程序设置的标志并直接调用请求处理函数的方式来实现同样的功能。
【FreeBSD
操作系统设计与实现】:
实现低 优先级处理的机制被称作软件中断。一般来说,都是由一个高优先级的中断创建一个将在低优先级中完成的工作队列。和FreeBSD5.2
中的硬件设备一样, 每个软件中断都有一个与之相关联的进程上下文。软件中断进程的调度优先级一般比设备驱动程序进程低,但却比用户进程的优先级高。只要有硬件中断到达,和设
备驱动程序关联的进程就会获得最高优先级,并被调度运行。如果没有设备驱动程序进程处于可运行状态,优先级最高的软件中断进程就会被调度运行。如果没有软 件中断进程处于可运行状态,就会运行优先级最高的用户进程。如果在一个软件中断进程或者用户进程正在运行的时候来了一个中断并使其设备驱动程序进程处于可
运行状态,调度程序将会抢占软件中断或用户进程来运行设备驱动程序进程。
在非中断线程化的 OS
中,如果把响应中断的所有工作都在
ISR
中完成,系统是无法忍受的,我们要做的是在 ISR
中尽量的减少代码,只做一些必要性的工作,如 in / out
操作,把一些其他不必要在 ISR
中工作放到其他地方,比如数据的处理,这也就是软中断目的所在。其实即使在中断线程化的 OS
当中(如:SOLARIS
)也一样有软中断,但这里的软中断含义和那种推迟调度不是一个概念了。以下仅针对此帖题目举例说明硬件中断如何触发软中断,关于
如何注册软中断和注册步骤就不说了。这里所说的软中断是与题目相关的。
在 NT
KERNEL
中把软件运行环境又分为三个软件中断 IRQL
等级,DISPATCH_LEVEL
,APC_LEVEL
,PASSVIE_LEVEL
。系统提供的 DPC
是为了处理硬件中断后续工作提供的一种机制,DPC
与 Linux Kernel
提供的基于 tasklet
实现的 BottomHalf
作用上有些相似,但又不一样。在使用 DPC
处理硬件中断后续的工作前,需显示调用相关的 kernel api
注册 DPC
。关于硬件中断如何触发 DPC
调用,可以看 HalEndSystemInterrupt()
这个函数,在这个函数中,首先确定没有其他嵌套硬件中断后(没有高于 DPC_LEVEL
的 IRQL
),把
IRQL
降低,并判断是否有 DPC
调用(之前使用
IoRequestDpc()
向系统注册的 DPC
回调函数,当把 DPC
回调函数插入DPC LIST
后,系统调用 HalRequestSoftwareInterrupt()
设置当前 IRQL
为 DPC_LEVEL
,如果当前有硬件级的 IRQL
高于 DPC_LEVEL
则 pending DPC
),如果有则设置 IRQL
为 DPC_LEVEL
并调用 KiDispatchInterrupt()
处理之前注册的 DPC
回调函数。KiDispatchInterrupt()
函数本身是在 IRQL ==
DPC_LEVEL
上运行的,它同样是处理线程调度的主函数。也就是说 DPC
调用点不光是在所有硬件ISR
处理完成后调用 DPC
。还有是处理任务调度时也会判断是否需要执行 DPC
。这里注意,DPC
与
DPC_LEVEL
不是一回事,DPC
回调函数运行级别在
DPC_LEVEL
,但并不是说所有 DPC_LEVEL
级别都是要运行 DPC
回调函数的。在非 SMP
环境下spinlock
的实现就是简单的提升当前 IRQL
为 DPC_LEVEL
,因为在这个级别的
IRQL
下所有其他软件级别运行的代码都将被阻塞,所以说线程调度主函数运行在这个级别是不会被任何线程抢占的,另DPC_LEVEL
虽然不是中断上下文,但在这个级别下,任何引起睡眠的机制都将导致系统崩溃。扯的有点远了。再简单说下 APC(
Asynchronous Procedure Call)
,故名思意,既然是“
异步过程调用”
那么当前执行环境很可能是任意线程上下文。其实到了 APC
这里就跟硬件中断扯不上边了,虽然是这样系统还是把 APC
实现也定位为一种软中断,APC
运行在 APC_LEVEL
级别(同样系统调用
HalRequestSoftwareInterrupt()
函数设置当前IRQL
为APC_LEVEL
)。关于如何触发 APC
可以去看一些读/
写函数,当一个读/
写函数设置了异步标志时,有几种情况(因为是异步的),首先说一下触发条件,条件是:“
系统处理的当前线程是调用者线 程且 IRQL
小于 APC_LEVEL
时从当前线程 APC
队列里取出 APC
回调函数并把 IRQL
提升到
APC_LEVEL
进行调用”
。继续看这几种情况,如果系统当前处理线程不是调用者者线程,则清除 APC_LEVEL
标志,并将此 APC
(回调函数)插入调用者线程的 APC
列然后返回。如果系统当前处理线程正是调用者线程,那么判断当前 IRQL
是否高于 APC_LEVEL
,如果高于则插入调用者线程 APC
队列,此次 APC
中断被记录并返回。如果系统当前处理线程是调用者线程且 IRQL
小于 APC_LEVEL
则满足调用条件,APC
回调函数立刻被调用。处理 APC
的调用点是在 syscall
返回时检查。
在 Linux
Kernel
里引入 softirq
机制后,使用
tasklet
来实现 Bottom Half
。tasklet
也是 softirq
的一种应用,在使用 Bootom
Half
(tasklet
)处理硬件中断后续工作前,需显示调用相关的 kernel api
注册 Bootom Half
(tasklet
)。关于硬件中断如何触发 softirq
,可以看 do_IRQ()
这个函数,这个函数在处理完设备驱动程序调用 request_irq()
注册的 ISR
后,会接着调用 irq_exit()
,在irq_exit()
中会检测是否注册了 softirq
回调函数,且在确定所有硬件中断都返回后调用 do_softirq()
,在这个函数当中会遍历软中断向量表,并调用相应的回调函数,被调用的函数就是 tasklet
注册的回调函数,也就是你注册的 Bottom Half
。上面只是针对硬件中断而言,其实处理软中断的调用点,不光是在硬件中断后触发,还有一些地方也调用了 do_softirq()
。如在任务调度中 schedule()
函数里也会判断是否需要执行软中断。还有在 syscall
调用时也有判断是否需要执行软中断的地方。软中断的运行环境同样是中断上下文,在中断上下文中任何引起睡眠的机制都将导致系统崩溃。
在
SOLARIS Kernel
中,有很大一部分硬件中断被线程化,如磁盘和网络中断,包括时钟中断。实现中断上下文可阻塞。被线程化的中断,与普通线程共用一个 dispatcher
。中断线程使用区别于 TS/IA/RT
调度等级的优先级,即全局优先级最高的等级。我个人认为在这种响应硬件中断时可阻塞的中断线程化的处理机制中无需提供像 DPC
,Bottom Half
这种机制,这也是跟以上两种非中断线程化 OS
主要区别。以上两种 OS
的中断处理机制,是为了不让在 ISR
中处理过多耗时操作,而提供了一个系统回调函数接口,把那些相对耗时的处理操作放在回调函数中延迟调用。而现在中断线程化并可阻塞了,我完全可以创建一个
中断线程,所有处理工作由系统负责,这样就无需再提供这种回调函数的接口了。既然不需要 DPC / tasklet
的Bootom Half
的这种推迟机制那么在 SOLAIRS
内核中软中断的实现自然也就不一样,它本身也是作为一种中断线程形式存在。关于软中断(softint
)的调用可以查看 interrupt.s
的代码。在中断处理的入口中首先判断了是否为软中断调用 T_SOFTINT
。如果是,则跳转到 dosoftint()
去执行,首先会判断如果有比自身更高级的 pending
软中断,则直接返回。如果没有则判断当前处理线程优先级比自身高的话马上返回。如果可以处理当前软中断则先 lock BUS
防止其他CPU
重入这个软中断,设置当前 IPL
为软中断级。然后比较当前要中断的线程是否是一个中断线程,如果不是则再继续判断是否要设置时间片。如果需要则进行设置。如果当前是中断线程,则没有以上
的步骤。然后就是中断当前线程,并使用被中断线程的 LWP
。接下来保存被中断的线程,切换到新软中断线程运行,继续使用av_dispatch_softvect()
函数来执行被注册到 struct
autovec
结构里的软中断
回调函数。等到调用完成后,退出软中断线程返回。以上步骤是在没有更高级的硬件中断线程在运行。如果当前有更高级 别的硬件中断正在执行的话,那么会在处理完硬件中断后无条件的跳转到软中断处理中执行。注意,这里没有判断。从以上流程可以看出软中断是有优先级的,即可
以进行抢占。软中断的调用是无条件调用的,即我可以直接显示的调用。这样我们可以看出系统在处理软中断时,从处理机制来讲是与硬中断平级的,当然有优先级 之分。这也意味着与 DPC
和 Bootom Half
这种被动调用机制有这明显的不同。之所以设计成可以直接显示调用,应该是为了某种性能调优而考虑的。在 SOLAIRS
的解释中,软中断是为一些伪设备所提供的。
结束*/
从上可以看出,linux
是多么...
啊,它既有nt
内核软中断的影子,也有unix
中断线程化的影子(在软中断执行时间超过一个阀值的时候,他就唤醒软中断线程,将之线程化,以线程上下文执行),这样,linux
包含了一种哲学,一种让人捉摸不透的哲学。