wdk tips (3): IRQL

今天我们来聊聊IRQL,这是驱动新手的梦魇,想想看多少BSOD是因为IRQL不对引起的。这也是*NIX类内核开发人员最喜欢的吐槽点之一,你看linux里就没有这个概念,我们还不是活的好好的?我偶尔有时候能得着一些空,也会问一样的问题:为毛?为毛要有这东西存在!后来我想通了。

我们先聊passive level和interrupt level。passive level是普通级别,同时也是优先级最低的,所有的用户态线程和大部分的内核态线程都会在这个级别上运行。interrupt level则是中断服务例程的运行级别。这两者有差很好理解,几乎所有os教程里都有告诫我们中断服务例程要尽可能快的完成,并且不能被其他线程抢占,也不能主动交出运行权(也就是sleep)。nt内核里的中断没有属于自己的栈,它会抢占当前运行线程的栈使用,抢到谁就算谁倒霉,linux内核似乎看配置,栈大小配为4k的时候也是抢别人的用。倘若中断服务例程可以被抢占(也就是换出),那么这些上下文就存在了别人的栈顶上。考虑这么一种情况:中断服务例程抢了线程T1的栈使用,运行到一半被线程T2抢占了,T2这个倒霉催的刚运行一点点时间也被换出,新上位的正好是T1,这下问题就来了。T1并不是因为线程切换才被抢占的,它是被中断抢了,中断换出前T1的上下文并没有被保留,所以恢复也成了不可能完成的任务。为了防止此类情况的发生,中断服务例程必须不能被其他线程抢占,它在T1的栈上做事情,做完后清理栈然后把还给T1继续运行,在T1看来就跟什么都没发生过一样。要做到这一点其实很容易,只要在运行中断服务例程前关闭所有其他中断即可,事实上linux也真是这么做的(如果我错了请千万告诉我),但是nt内核的设计者想的就有点多,他们觉得关闭所有中断似乎太暴力,即使名字都叫中断,那也得有个轻重缓急之分嘛,断电的中断就比硬盘中断急的多不是。所以他们决定给这类抢占别人的能力分个等级,(硬件)中断在interrupt level,在它之上还有N个等级,即使中断服务例程正在运行,这N个高等级的任务还是可以抢人家鸡蛋。完整的权限等级请自行google。

再来聊apc level和dpc level。这俩等级和体系结构没什么关系,纯粹是跟nt的实现机制有关的。apc提供了一个方法,能让一个线程侵入到别的线程,委托别的线程做一些自己想干的事情,比如进程要退出时,发起退出动作的那个线程会给进程里所有的线程都插一个apc,而该apc做的事情就是结束本线程。再比如上两回我们一直说的CompleteRoutine,也是用apc机制完成的。你可以把apc看成是类似*NIX信号的东东。dpc做的事情和apc有些相似,都是把某些工作委托给别人做,不过apc依附在线程上,而dpc是独立的,每个cpu都有一个dpc队列,线程调度前会先检查dpc队列,如果非空,会先做dpc再挑线程。这个概念和linux中的下半部很相似,大致想法就是中断服务例程越简短越好,其他事情弄个什么方式延后处理。nt内核中有一个时钟每隔一段时间interrupt一次并发出一个dpc,该dpc做的事情就是检测当前运行的线程是否已经用完了自己的时间片,如果用完了,则让它进入wait状态并挑个别的线程执行。所以我们看到,nt内核里的线程调度是运行在dpc级别的,倘若在dpc级别运行的东西要做sleep的动作,死锁就要发生了:我要睡觉了,我主动交出运行权-->调度器试图运行,发现它的级别也是dpc level-->抢占不了,同等级的人是不能相互抢占的。城管可以抢小商贩,但去抢拆迁队那还了得。-->you got a deadlock!。

总体来讲,passive level和interrupt level是显而易见的,也很好理解,apc level和dpc level则是nt实现机制引入的,其实看着烦人,但都很有用。再反过头来看,linux中真的没有IRQL的概念吗?未必,passive level和interrupt level是必然存在的,下半部可以看成是dpc,信号可以看成是apc,四个等级里前三者和nt内核一致,只有apc和信号有差别。所以我们说内核这个东西,发展思路已经日趋一致,正所谓大江大河汇入海,都一样。

posted @ 2011-01-20 14:39  gussing  阅读(2007)  评论(0编辑  收藏  举报