Windows中断那些事儿

搞内核研究的经常对中断这个概念肯定不陌生,经常我们会接触很多与中断相关的术语,按照软件和硬件进行分类:

硬件CPU相关:

      IRQ、IDT、cli&sti

软件操作系统相关:

      APC、DPC、IRQL

一直以来对中断这一部分内容弄的一知半解,操作系统和CPU之间如何协同工作也是很模糊。最近花了点时间认真把这块知识进行了梳理,不当之处,还请高手指出,先行谢过了!

      本文旨在解答下面这些问题:

  1.IRQ和IRQL之间是什么关系?

  2.Windows是如何在软件层面上虚拟出IRQL这套中断机制的

  3.APC和DPC都是软件中断,既然是中断那么对应的IDT表项中的处理例程在哪里呢?

 

0x00 Intel 80386处理器的中断

  首先,让我们忘记Windows,从最开始的80386处理器开始,看看Intel设计它的时候是如何处理中断这个东西的。

先来看看这个诞生于1985年的CPU长什么样子:

 

  看看那些伸出来的引脚,下面是它的引脚标注图:

  注意用红圈标注的两个引脚,这两个就是80386处理器为中断留出的两个引脚。其中INTR是可屏蔽中断输入口,NMI是不可屏蔽中断输入口。

那么中断是如何输入给处理器的呢?那么多外部设备,而这只有一个引脚(暂时只考虑可屏蔽中断),这里就需要为CPU配备一个管理中断的秘书——可编程中断控制器PIC。这个秘书需要干哪些活呢?外部设备的中断都从它来进入中央处理器,所以它负责从外设接收中断信号,并根据优先级向CPU发起中断请求。最开始的这个PIC角色是一个代号为8259A的芯片在进行扮演,这货长这样:

 

  下面是它的引脚图:

 

  其中IR0-IR7共8个引脚负责连接外部设备, 8259A PIC的每个IR口都连接着一条IRQ线,用于接收外设的中断信号。INT负责连接CPU的INTR引脚,用于向CPU发起中断请求。通常情况下,使用两片8259A芯片进行级联,一片连接CPU,称为主片,另一片连接到主PIC的IR2引脚,称为从片,这样总共就可以连接8+7=15个外设了。如下图所示:

  在8259A中,默认情况下的优先级是主片IR0的中断请求优先级最高,主片IR7最低,从片IR0-7所有中断请求优先级都相当于IR2。所以IRQ线的优先级由高到低次序为IRQ0,IRQ1,IRQ8-15,IRQ3-7。这是默认情况,可以通过编程改变。

 

  在8259a芯片内部有几个重要的寄存器:

  中断请求寄存器: IRR,8bit,对应IR0-IR7,当对应引脚产生中断信号时,该bit位置1。

  中断服务寄存器: ISR,8bit,对应IR0-IR7,当对应引脚的中断正在被CPU处理时,该bit位置1。

  中断屏蔽寄存器: IMR,8bit,对应IR0-IR7,当对应位为1时,表示屏蔽该引脚产生的中断信号。

  还有一个中断优先级判决器: PR,当中断引脚有信号时,结合这次产生中断的IRQ号和ISR中记录的当前正在处理的中断信息,根据优先级来决定是否把这个新的中断信号报告给CPU,以此来产生中断嵌套。

      下面是这15条IRQ线分别连接的外设:

  现在我们来看看这个秘书是如何和CPU之间进行协调工作的。

 

  现在假设我们敲击了一个键盘按键,键盘有中断事件产生,这一事件通过IRQ1这根线告知了主PIC,主PIC经过内部一些判断处理后通过INT发送电信号到CPU侧的INTR。CPU在执行完当前的指令后,检查到INTR有信号,说明有中断请求来了,再检查eflags中的IF不为零,表示当前允许中断,则发送信号给PIC的-INTA,告诉它把本次中断的向量号发送过来。主PIC收到-INTA管脚上的信号后,通过D0-D7引脚,输出此次中断的中断向量号到数据总线(这里简化了交互过程,实际上有两次INTA信号的发送)。CPU拿到这个号后,就可以从IDT中寻找中断服务例程(ISR)进行处理了,后面的事大家都知道了。

 

  那PIC中的中断向量号是怎么来的呢?各个IRQ是如何对应到IDT中的各个项呢?这里就利用了中断控制器的可编程性来决定的了。

 

  PIC全称为可编程中断控制器,那么它的可编程体现在哪些方面呢?参考资料2《i8259A中断控制器分析一一文有比较详细的描述,大体包括编程指定主从片的IRQ线对应的中断在IDT表中的中断向量号、8259a中断控制器的中断方式、优先级方式、中断嵌套方式,中断屏蔽方式、中断结束方式等等,这些都可以由操作系统编程指定。具体的编程格式在参考资料3《i8259A中断控制器分析二》一文中有图文介绍。

  回到上一个问题,IRQ线上的中断如何和IDT中的条目对应起来,操作系统在初始化的时候,会通过对8259a芯片编程(读写I/O端口),将指定PIC芯片的起始向量号,并要求低三位为0,起始向量号按照8对齐,这样规定的原因是,当中断发生时,低三位将自动填充对应的IRQ号,这样就可以和起始向量号相加直接送给数据总线从而被CPU拿到。具体到Windows中,系统初始化的时候对PIC的编程为:指定主片的起始中断向量号为0x30,指定从片的起始中断向量号为0x38。这样,通过中断控制器连接的15个外设将被平坦的映射到IDT中0x30-0x40这一范围中。Windows内核启动初始化过程中使用了hal!HalpInitializePICs对8259a芯片进行编程,ReactOS中代码如下:

其中0x20,0x21是主片的IO端口,0xa0,0xa1是从片的IO端口:

  PRIMARY_VECTOR_BASE定义为:

      具体8259a的编程方法就是读写IO端口,设置对应的控制命令,不用深入研究。我们来看Windows编程8259a的时候指定了哪些东西。

  1、指定了主片的工作方式为级联、中断方式为电信号边沿触发

  2、指定了主片IRQ的中断向量映射基址:0x30

  3、指定了主片的级联方式为使用了自己的IRQ2这个管脚

  4、指定了主片的工作模式为80x86模式,中断结束方式为普通结束模式

  5、指定了从片的工作方式为级联、中断方式为电信号边沿触发

  6、指定了从片IRQ的中断向量映射基址:0x38

  7、指定了从片的工作方式级联方式为主片的IRQ2这个管脚

  8、指定了从片的工作模式为80x86模式,中断结束方式为普通结束模式

 

  至此我们可以知道,在使用8259A中断控制器的计算机上,通过IRQ线连接的那15个外设可屏蔽中断是被操作系统线性的映射到了IDT中的一个范围段。在Windows中是0x30-0x40PS:Linux中是0x20-0x2F),同时指定了中断控制器的中断方式为边沿触发,结束模式为普通结束模式(也就是需要CPU侧告知中断处理有没有结束并设置对应bit位,不能自动设置)。

 

0x02 8259a上的Windows IRQL

       下面来看看IRQL。

      从前面我们看到,硬件层面已经对中断的处理提供了很好的支持,需要操作系统做的也就两点:首先,初始化的时候对PIC进行编程设置其工作方式并对IRQ进行映射,让这些中断对应到IDT中的各个项,其次,实现这些IDT中的中断服务例程。似乎这样就够了,那Windows弄出来的一套IRQL又是什么东西呢?

      看看《Windows Internals》一书对IRQL的定义:

  

  写驱动的时候经常会接触到IRQL这个概念,它实现了Windows里的中断优先级制度,高优先级的中断总是可以优先被处理,而低优先级的中断则不得不等待高优先级中断被处理完后才得到处理。软件虚拟出来的这一套机制怎么能管到硬件的优先级呢?这是如何实现的呢?

 

  先来解决两个问题:

  1、IRQ和IRQL的关系是什么?

  2、使用KeRaiseIrql提升当前IRQL后,为什么就能保证不被低优先级的中断打扰?

 

  对于第一个问题,在使用8259a中断控制器的计算机中,IRQL=27-IRQ,其就是一个线性关系。

  关于第二个问题,《Windows Internals》一书是这样解答的:

 

  下面我们具体来看Windows的实现:

  IRQL是一个完全虚拟出来的概念,Windows为了实现这一个虚拟的机制,完全虚拟了一个中断控制器,它在KPCR中:

  +0x024 Irql             : UChar         //IRQL

  +0x028 IRR             : Uint4B       //虚拟中断请求寄存器

  +0x02c IrrActive       : Uint4B        //虚拟中断在服务寄存器

  +0x030 IDR             : Uint4B       //虚拟中断屏蔽寄存器

      在前面第一部分提到过,通过两片8259a芯片连接的15个中断源被映射到处理器IDT中的一段范围,具体Windows而言,是在0x30-0x40这个范围。这15个IDT中的中断描述符所描述的中断处理例程(ISR)不同于int 3所对应的KiTrap03和int 0e所对应的KiTrap0E,他们的ISR指向的代码位于各自的中断对象KINTERRUPT的DispatchCode。下面是这个结构的定义:

typedef struct _KINTERRUPT {CSHORT Type;CSHORT Size;LIST_ENTRY InterruptListEntry;PKSERVICE_ROUTINE ServiceRoutine;PVOID ServiceContext;KSPIN_LOCK SpinLock;ULONG TickCount;PKSPIN_LOCK ActualLock;PVOID DispatchAddress;ULONG Vector;KIRQL Irql;KIRQL SynchronizeIrql;BOOLEAN FloatingSave;BOOLEAN Connected;CHAR Number;UCHAR ShareVector;KINTERRUPT_MODE Mode;ULONG ServiceCount;ULONG DispatchCount;ULONG DispatchCode[106];} KINTERRUPT, *PKINTERRUPT;

    DispatchCode里面的代码是根据一个模板来的,这些ISR处理开始和KiTrap03这些一样,首先会建立陷阱帧,然后会获取自己所在KINTERRUPT对象地址,得到这两个参数之后,便开始使用KiInterruptDispatch或KiChainedDispatch(如果对该中断注册了多个KINTERRUPT结构构成了链表使用此函数)进行中断派遣。而在这两个具体的派遣中都会先调用HalBeginSystemInterrupt,然后才会执行对应中断的实际处理工作,最后会执行HalEndSystemInterrupt完成此次中断处理。下面我们重点来看看这两个函数。

BOOLEANHalBeginSystemInterrupt(IN KIRQL IrqlIN CCHAR Vector,OUT PKIRQL OldIrql);

    输入参数Irql表示本次发生的中断对应的的IRQL,Vector表示中断向量号,如前所述,这两个参数都是DispatchCode从自己所在KINTERRUPT对象中取出来的。

HalBeginSystemInterrupt内部使用IRQL参数在一个表格中进行了分发,这个表中除了个别函数不同外(其实也只是多了一层判断),其他表项都是一致的,在ReactOS中名为HalpDismissIrqGeneric,该函数直接转而调用其下划线版本_HalpDismissIrqGeneric。这里就是IRQL优先级实现的核心所在了。该函数不长,下面是ReactOS中的代码(在Windows2000代码中是汇编形式不如ReactOS使用的C语言形式直观,所以采用了ReactOS的代码进行说明):

 

      首先,判断本次发生的中断对应的IRQL与当前处理器(KPCR)中的IRQL进行比较,如果大于了当前处理器的IRQL,则表示来了一个优先级更高的中断,这时设置KPCR中的IRQL为这个新的更高的数值,后面返回了TRUE,表示需要处理这次中断请求。如果不大于当前处理器的IRQL的话,首先把本次中断记录记录到KPCR中的虚拟中断控制器的IRR值,然后就直接通过KiI8259MaskTable表中选取当前处理器IRQL对应的屏蔽码写入PIC,用以屏蔽那些IRQL比自己低的中断源,后面返回FALSE,表示不处理这次中断请求。为什么不在设置处理器新IRQL的时候就进行设置屏蔽码呢?《Windows Internals》是这样解释的:

    HalpDismissIrqGeneric的返回值将直接作为HalBeginSystemInterrupt的返回值。以中断派遣函数KiInterruptDispatch为例看看它是如何使用这个返回值的:

 

      可以看出,如果HalBeginSystemInterrupt返回了FALSE,则直接导致本次中断处理提前结束。只有当HalBeginSystemInterrupt返回了TRUE时,才继续执行真正的中断处理例程。最后, 情况下都会调用KiExitInterrupt结束中断处理过程,看一下这个函数。结合KiInterruptDispatch的代码,可以看出,只有当HalBeginSystemInterrupt返回的是TRUE时,下面的if条件才会成立,从而进入HalEndSystemInterrupt。

 

      最后看一下HalEndSystemInterrupt,前面提到如果发生的中断对应的IRQL低于处理器的IRQL,则不会执行其ISR,但会在KPCR中的虚拟中断控制器的IRR中记录起来,等到处理器执行完了高IRQL的任务时,到了HalEndSystemInterrupt的时候,就会降低处理器的IRQL并重新设置PIC的中断屏蔽码,另外很重要的就是去检查IRR中的记录,如果记录中有比降低后的IRQL高的记录,则派遣该中断。

 

      最后总结一下使用8259a中断控制器的计算机中Windows的IRQL。

  首先,系统启动时对8259a芯片编程,设置其工作方式,并将15个中断源(IRQ)映射到IDT中的0x30-0x40这一段。

  第二,Windows自己定义了一个称为中断请求级的IRQL概念用来描述中断的优先级别,IRQL是一个DWORD,共计32个级别,Windows使用一个简单的线性关系来映射IRQ和IRQL:IRQL=27-IRQ。

  第三,被映射中断请求的0x30-0x40这一段的中断描述符的每个ISR都指向了一个KINTERRUPT结构中的DispatchCode,这段DispatchCode使用中断派遣函数KiInterruptDispatch或KiChainedDispatch进行中断派遣。

  第四,派遣过程为:先使用HalBeginSystemInterrupt对本次中断的IRQL进行判断来决定是否需要处理本次中断,若不需要,则设置中断控制器的屏蔽码,防止再被打扰,同时将本次中断登记在KPCR中的虚拟中断控制器IRR中。若需要则提升IRQL,进而执行该中断的实际处理例程,执行完毕后使用HalEndSystemInterrupt降低IRQL,然后检查IRR有没有记录没被处理的中断以便在这个时候进行处理。

 

0x03 进入奔腾时代——APIC

  下回再聊。

转载于:https://www.cnblogs.com/xuanyuan/p/5506508.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/430520.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(1-1)文件结构的升级(Area和Filter知识总结) - ASP.NET从MVC5升级到MVC6

ASP.NET从MVC5升级到MVC6 总目录 MVC5项目结构 带有Areas和Filter的项目结构 一般来说,小的MVC项目是不考虑领域的,但是,如果是稍微复杂一点的项目,往往是需要领域这个概念的。 一个领域就是一个小型的MVC项目,所以领域…

重启模块与及关开邮件存储设置功能页面-PHP-shell-py

邮件系统几百台,每台负责 grep -P "^ip\d.\d." /home/mymail/newconf/hosts.conf -c465 每台机器负责启动的模块又是不一样的如: A机器: ProgramsList"1svr,2svr,3svr,4svr," b机器: ProgramsList"asvr,…

用IIS配置反向代理

https://natapp.cn/ http://blog.csdn.net/g2321514568/article/details/12406755 目标服务器:targetServer 配置反向代理的服务器:reveseProxServer 1、确定最终访问的网址:比如www.baidu.com 、www.csdn.net等等。 当然你也可以自己在targ…

oracle存储过程使用ftp,ASM存储FTP上传文件

引用SQL>execute dbms_xdb.sethttpport(8080);SQL>execute dbms_xdb.setftpport(2100);SQL>commit;检查端口是否开启引用SQL> select dbms_xdb.GETHTTPPORT() from dual;DBMS_XDB.GETHTTPPORT()----------------------8080SQL> select dbms_xdb.GETFTPPORT() fr…

Python学习笔记——基础篇【第六周】——hashlib模块

常用模块之hashlib模块  用于加密相关的操作,3.x里代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法 import md5 hash md5.new() hash.update(admin) print hash.hexdigest() MD5-废弃import shahash sha…

虚拟存储

为解决日益增长的内存需要,有以下几种解决办法: 1.覆盖: 将程序划分成几个模块,将没有调用关系的模块(即不会同时运行的模块)分成一组,其中每组所占的内存大小为组内所需内存最大的模块的内存&a…

作为前端应当了解的Web缓存知识

缓存优点 通常所说的Web缓存指的是可以自动保存常见http请求副本的http设备。对于前端开发者来说,浏览器充当了重要角色。除此外常见的还有各种各样的代理服务器也可以做缓存。当Web请求到达缓存时,缓存从本地副本中提取这个副本内容而不需要经过服务器。…

linux 提取日志字段,记一次Linux下提取MySQL日志关键字段

8种机械键盘轴体对比本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?环境说明操作系统:centos7sed版本:4.2.2egrep版本:2.20paste版本:8.22提取要求一次同事说,需要提取MySQ…

1x1 11b g n linux,基于RN1810下的2.4 GHz IEEE 802.11b/g/n无线模块

特性• 符合IEEE 802.11b/g/n的收发器• 2.4 GHz IEEE 802.11n单流1x1• 与主机控制器的UART接口(4线,包括RTS/CTS)• 易于集成到最终产品中——最大程度地减少产品开发工作量,缩短上市时间• 使用简单的ASCII命令进行配置• 带稳压电路、晶振、RF匹配电…

!!“理都懂”为什么“然并卵”?

“理都懂”为什么“然并卵”? 脑子有话讲 收藏(176)| 阅读(17980)以前看见过别人问过这么一个问题:「为什么我们懂得很多道理,却依然过不好这一生?」 知乎上有很多这个问题的不同版本,但其实都说的是同一个事情&#…

linux终端模拟器app下载,3C终端模拟器app下载-3C终端模拟器v0.9最新版下载 - 91手游网...

应用介绍3C终端模拟器是一个终端模拟的app,风格多变,轻松好用,还有功能各异的语句等你来试验,可以在其中运行属于你自己的脚本内容,并且这个软件是不限定使用的,这也就意味着你是否有ROOT并不影响这个软件的…

【VS开发】static、extern分析总结

引用请注明出处:http://blog.csdn.net/int64ago/article/details/7396325 对于写了很多小程序的人,可能static和extern都用的很少,因为static和extern通常在工程量很大时候才能体现优势很必要性,这就不奇怪linux内核代码中“泛滥”…

android 画布心形,Android CustomShapeImageView对图片进行各种样式裁剪:圆形、星形、心形、花瓣形等...

Android CustomShapeImageView对图片进行各种样式裁剪:圆形、星形、心形、花瓣形等Android CustomShapeImageView是github上一个第三方开源的对图片进行各种样式裁剪的库,其要实现的功能如图所示:Android CustomShap…

iOS开发UI篇-在UItableview中实现加载更多功能

iOS开发UI篇-在UItableview中实现加载更多功能 一、实现效果 点击加载更多按钮,出现一个加载图示,三秒钟后添加两条新的数据。 二、实现代码和说明 当在页面(视图部分)点击加载更多按钮的时候,主页面&#…

ublox Android 定位超时,[RK3288] [Android 7.1] u-blox GPS调试

我这里GPS使用的是TTL串口GPS芯片,用的是uart01.确认原理图对应的uart节点,将其打开&uart0 {status "okay";dma-names "!tx", "!rx";pinctrl-0 ;};2.在hal层编译出 gps.default.so 目录在hardware/rockchip/gps/有的目录下自带…

1.4Activity保存现场状态

概念: 保存Activity的状态是非常重要的,例如我们在玩一个游戏的时候,突然来了一个电话,这个时候在接听完电话之后我们返回到游戏中,这个时候我们希望游戏还是之前那个进度,或者说发生突发事件,游戏这个应用…

鸿蒙系统什么时候超过苹果,为何任正非说鸿蒙系统想超过苹果系统需要的时间,不会超过300年...

还记得任正非的一段话:华为操作系统要想超安卓苹果,需要很长时间,但不会超过 300 年。我相信这段话并不是说华为系统要超过苹果手机,需要300年的时间。任正非只是解释了华为系统目前和安卓系统以及苹果系统还有一定的差距&#xf…

android开发设计平台,10款开发和设计应该安装的android应用

过去几年里有很多新开发的小工具出现,人们的生活越来越离不开智能机,当然包括android手机,它已经成为了人们的最大需求量之一,市场上出现的android手机也越来越多,人们也比较喜欢用andorid手机。因为相对而言&#xff…

html的div显示到最左侧,HTML/CSS:如何淡化div的左右边缘?

嗨,我想淡出div和它的内容从左和右边缘使用纯CSS。 目前,我能够根据一个关于堆栈溢出的问题的答案来实现这一点。null.container {height: 234px;width: 234px;overflow: scroll;mask-image: linear-gradient(transparent,black 20%,black 80%,transpare…

android 微积分计算器,高数计算器1.0(高数计算工具app)

高数计算器1.0是手机上的一款免费好用的高数计算工具app,利用它,用户就可以进行快速进准的高等数学计算操作,范围包涵函数偏导、泰勒展开、一重积分、二重积分以及Latex编辑等等。详细内容请感兴趣的朋友前来西西下载体验!应用简介…