⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 786.html

📁 里面收集的是发表在www.xfocus.org上的文章
💻 HTML
📖 第 1 页 / 共 2 页
字号:
<br />
IOAPIC<br />
<br />
为了让本文相对简单,我们只讨论有24条 INTINs 的单 IOAPIC 的系统。IOAPIC 也连在系统总线上,但并不是直接相连。它实际上连到了一个桥(也是 Intel 芯片组的一部分)上,这个桥又连到了系统总线上(见 Figure 3)。正像 8259,系统中所有需要中断的设备都分别连在总线上的一条 INTIN 线上。但是不同的是,每一条 INTIN 线并没有隐含的优先级。记住,在 APIC 里,优先级是由中断向量号和 LAPIC 里的 TPR 处理的。<br />
<br />
在 IOAPIC 里有一个叫“I/O Redirection Table”(IOREDTBL)的寄存器。每一个 INTIN 都有一个64位的 IOREDTBL 表项而且每一个值都详细描述了相应中断。IOREDTABLE 表项描述了此中断的亲缘处理器,此中断是 edge 触发的还是 level 触发的,中断的 polarity,中断是否被屏蔽以及与此中断相关联的向量等等。当然,OS 要负责将相应的值填入此表来使 IDT 和连到 INTINs 的设备的特征相匹配。<br />
<br />
关于IOREDTBL 还有一些有意思的问题,比如用来描述每个中断 CPU 亲缘性的位掩码只能是8位宽。唉!我们这里还是不要关心这些复杂的东西们了。我们只限于讨论8个或更少的 CPUs。<br />
<br />
<br />
Aside: PCI to IOAPIC<br />
<br />
在我们讨论中断是如何处理的之前,也许应该先来看一下 PCI 总线是如何连接到 IOAPIC 的。对此问题本身就能写一篇好文章,但是这里只要知道每一条 PCI 中断请求线(PIRQxs)都连到一条 IOAPIC INTIN 线上就足够了。这可以是一个硬布线的连接,也可以是可以通过 BIOS 动态调整的连接。<br />
<br />
<br />
Handling APIC-Based Interrupts In Windows<br />
<br />
到目前为止我所讲到的细节可以用在任意一个使用 PIC 或 APIC 操作系统上。所有的 OSs 都会有一个 IDT,都需要对 PIC 进行编程来反映 IDT,并且都必须有处理中断的 ISRs。因为这是 The NT Insider,本文就不会是大全,除非我们想将所有这些都与 x86 上的 Windows 实际如何处理中断联系起来。由于 8259 即将过时,我们就只讨论 APIC。<br />
<br />
OK,那么当一个设备发出中断时到底发生了什么呢?假设有一个 level-triggered 的设备连在 INTIN16 上,OS 为其分配的向量号为 0x42。当设备向要发出中断时,它就令 INTIN16 有效,然后……<br />
<br />
INTIN16 的有效会将我们带到 IOAPIC 中用于 INTIN16 的 IOREDTBL。若 IOAPIC 看到该中断未被屏蔽,就会通过桥芯片组在系统总线上产生一条消息。<br />
<br />
我们使用简化了的情况,有一个空闲的 CPU 看到了这个中断,然后跳转到 IDT 中索引 0x42 处指向的中断处理程序。记住,这里不用像 8259 那样回到 IOAPIC 察看 ISR 在那个向量上,向量是系统总线上送出的消息的一部分。<br />
<br />
因为 CPU 知道我们要处理一个中断,它就在调用由 IDT[0x42] 指向的 Windows general interrupt dispatcher 之前将 EIP、ESP、SS 和 CS 寄存器压栈。<br />
<br />
general Windows interrupt dispatcher 将 EBP、EAX、EBX、ECX、EDX、EDI、ESI、ES、DS、FS 或 GS 寄存器压栈。 <br />
<br />
为了验证以上步骤的正确性,我们在 general dispatcher 压入所有需要的寄存器后在它要调用的函数中的一个函数里设一个断点。结果,对于有 chained ISRs 的 IDT 表项调用的是 KiChainedDispatch 函数,而对 non-chained 的则调用的是 KiInterruptDispatch(这些函数名是用某个不太聪明的反汇编程序得到的:在 ISR 里设一个断点后就能在堆栈上看到它们)。部分堆栈如下:<br />
<br />
nt!KiInterruptDispatch+0x89 (FPO: [0,2] TrapFrame @ f42f3c30)<br />
<br />
HAL!KfLowerIrql+0x35 (FPO: [0,0,0])<br />
<br />
nt!KeSetPriorityThread+0xc2 (FPO: [Non-Fpo])<br />
<br />
nt!PspExitThread+0x9c (FPO: [Non-Fpo])<br />
<br />
那么到底是怎么会事呢?我们可以看到 generic Windows interrupt handler 调用了 KiInterruptDispatch,但是堆栈上其它的东西是干什么的呢?记住,中断可是在任意时刻都有可能发生的,这个中断就发生在线程退出的时候。我们并没有直接讨论过当中断发生时对于 CPU 上被中断的代码都发生过什么,而现在时机已到。当中断送达时,一个“trap frame”就被创建了,它保存着中断发生前的 CPU 的状态(有读者说到:“Aha!这就是为什么在调用我的 ISR 之前 CPU 会将寄存器压栈!”)。在 WinDBG 里,“.trap”命令会允许我们将我们的上下文设置为所提供的 trap frame 的上下文。我们现在就来用 KiInterruptDispatch 那行的 TrapFrame 旁边的值来做一下……<br />
<br />
1: kd&gt; .trap f42f3c30<br />
<br />
ErrCode = 00000000<br />
<br />
eax=00000000 ebx=00000000 ecx=00000000 edx=ffdff538 esi=81e32da8 edi=00000009<br />
<br />
eip=804e049d esp=f42f3ca4 ebp=f42f3cb4 iopl=0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nv up ei pl zr na po nc<br />
<br />
cs=0008&nbsp;&nbsp;ss=0010&nbsp;&nbsp;ds=01ff&nbsp;&nbsp;es=01ff&nbsp;&nbsp;fs=077a&nbsp;&nbsp;gs=7f30&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; efl=00000246<br />
<br />
HAL!KfLowerIrql+35:<br />
<br />
804e049d 3bc8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmp&nbsp;&nbsp;&nbsp;&nbsp; ecx,eax<br />
<br />
1: kd&gt; kb<br />
<br />
&nbsp;&nbsp;*** Stack trace for last set context - .thread/.cxr resets it<br />
<br />
ChildEBP RetAddr&nbsp;&nbsp;Args to Child&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
<br />
f42f3ca0 80a35ad8 8214c228 81e32da8 00000000 HAL!KfLowerIrql+0x35<br />
<br />
f42f3cb4 80bb295a 00e32da8 00000010 81e32da8 nt!KeSetPriorityThread+0xc2<br />
<br />
f42f3d40 80bb3360 00000000 00000000 f6a79680 nt!PspExitThread+0x9c<br />
<br />
<br />
哦,我们现在的所在就是中断发生前的地方!因此我猜当我们从中断返回时,如果我们将 CPU 的上下文恢复成这个 trap frame 的,那就没人知道发生过中断。<br />
<br />
一旦保存了所需的寄存器,我们就处于某一个 KiXxxRoutines 中了,Windows 取得中断的优先级。用 Windows 里的话讲,这叫中断的“Interrupt Request Level”(IRQL – 读作 ER-QUEL)。HAL 决定何时赋予设备资源以及赋予设备中断什么样的 IRQL。注意这与设备用来产生中断的物理 INTIN 线可没什么关系,而且也不能更改。设备 IRQL(DIRQL)与设备的中断优先级有关(IRQL 高则中断优先级高),也因此就与填入 LAPIC TPR 的值有关。<br />
<br />
Windows 下面要做的就是将当前 CPU 的 IRQL(IRQL 是个 per-CPU 的概念) 提升到 HAL 所赋予的 DIRQL。在此之后,CPU 就只能被更高 IRQL 的中断所中断。<br />
<br />
现在我们就在 DIRQL 上了,我们已经保证了不会被同一 CPU 上的相同中断所打断,因此对于当前的 CPU,ISR 的执行是同步的。Windows 现在还必须得到一把锁来保证 ISR 的执行要与系统中的其它 CPU 同步。因为 INTIN 的共享,与此中断相关联的 ISR 可能有多个,在 ISR 执行前每个 ISR 必须得到自己的锁,执行完 ISR 后还要释放该锁。一旦 Windows 得到了该锁,我们就可以执行 ISR 了。<br />
<br />
对于 Windows 中的中断,每个 IDT 向量还相应有一个 PKINTERRUPT 对象的链表。因为这个结构体是半公开的,就不能访问此结构体的所有的域,但是可以使用调试器的“dt”命令将其 dump 出来:<br />
<br />
1: kd&gt; dt nt!_KINTERRUPT<br />
<br />
&nbsp;&nbsp; +0x000 Type&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : Int2B<br />
<br />
&nbsp;&nbsp; +0x002 Size&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : Int2B<br />
<br />
&nbsp;&nbsp; +0x004 InterruptListEntry : _LIST_ENTRY<br />
<br />
&nbsp;&nbsp; +0x00c ServiceRoutine&nbsp;&nbsp; : Ptr32&nbsp;&nbsp;&nbsp;&nbsp;<br />
<br />
&nbsp;&nbsp; +0x010 ServiceContext&nbsp;&nbsp; : Ptr32 Void<br />
<br />
&nbsp;&nbsp; +0x014 SpinLock&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : Uint4B<br />
<br />
&nbsp;&nbsp; +0x018 TickCount&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;: Uint4B<br />
<br />
&nbsp;&nbsp; +0x01c ActualLock&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : Ptr32 Uint4B<br />
<br />
&nbsp;&nbsp; +0x020 DispatchAddress&nbsp;&nbsp;: Ptr32&nbsp;&nbsp;&nbsp;&nbsp;<br />
<br />
&nbsp;&nbsp; +0x024 Vector&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : Uint4B<br />
<br />
&nbsp;&nbsp; +0x028 Irql&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : UChar<br />
<br />
&nbsp;&nbsp; +0x029 SynchronizeIrql&nbsp;&nbsp;: UChar<br />
<br />
&nbsp;&nbsp; +0x02a FloatingSave&nbsp;&nbsp;&nbsp;&nbsp; : UChar<br />
<br />
&nbsp;&nbsp; +0x02b Connected&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;: UChar<br />
<br />
&nbsp;&nbsp; +0x02c Number&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : Char<br />
<br />
&nbsp;&nbsp; +0x02d ShareVector&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;: UChar<br />
<br />
&nbsp;&nbsp; +0x030 Mode&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : _KINTERRUPT_MODE<br />
<br />
&nbsp;&nbsp; +0x034 ServiceCount&nbsp;&nbsp;&nbsp;&nbsp; : Uint4B<br />
<br />
&nbsp;&nbsp; +0x038 DispatchCount&nbsp;&nbsp;&nbsp;&nbsp;: Uint4B<br />
<br />
&nbsp;&nbsp; +0x03c DispatchCode&nbsp;&nbsp;&nbsp;&nbsp; : [106] Uint4B<br />
<br />
<br />
可以从结构体的定义看出这些实际上都是由 InterruptListEntry 域链接起来的。而且从这里还可以看到 ISR 所分到的向量、相应的 DIRQL 和 ISR 执行前必须得到的自旋锁。因为我们在上一步已经得到了这个自旋锁,Windows 就会从第一个 PKINTERRUPT 开始并调用其 ISR(结构体的 ServiceRoutine 域)。<br />
<br />
ISR 会检查硬件看其是否正在中断,我们假设其正在中断。ISR 会告诉设备停止中断,可能会将一个 DpcForIsr 入队列然后返回 TRUE,指示中断已经处理。记住,我们的中断是 level-triggered 所以处理可以停止,因为我们找到了处理此中断的 ISR。如果 ISR 返回了 FALSE,Windows 就会认为在一个 INTIN 上游多个设备,释放自旋锁并移向链表中的下一个 PKINTERRUPT。然后再获得锁并调用其 ISR,重复此过程直到找到返回 TRUE 的那个。<br />
<br />
在 ISR 返回后,Windows 会作以下处理:<br />
<br />
。释放 ISR 锁。 <br />
。将 CPU 的 IRQL 恢复为中断发生前的 IRQL。<br />
。恢复以前的寄存器并发出一条“Interrupt Return”(IRET)。我们这里使用 IRET 而不是一般的 RET 是因为我们要让 CPU 知道我们是从 ISR 返回的,这样 CPU 会恢复 EIP、ESP、SS 和 CS 寄存器(即恢复我们前面所讨论的的“trap frame”)。<br />
<br />
成了!我们刚才追踪了中断的处理,从设备最初通过 IOAPIC 的 INTINs 发出中断一直到相应驱动程序的 ISR 的返回。<br />
<br />
<br />
Conclusion<br />
<br />
关于本文所讨论的话题还有许多可以说的,但是我猜在我解释更多细节之前我必须先得编写 The NT Insider Bathroom Bible。如果读者的兴趣的火焰此刻要熄灭,我强烈建议读者找一份 Intel architecture manuals 并祭起你最爱的调试器。你永远也不会知道你会发现什么!(译注:让我想起了“生活就像一盒巧克力……”好像把读者当成了阿甘!)。<br />
<br />
<br />
------------------------------------------<br />
<br />
WAIT!&nbsp;&nbsp;Doesn’t IRQ == Interrupt Priority?<br />
<br />
只在 Windows 下是这样,尽管硬件文档可能说法不同。根据配置,要么用的是 programmable interrupt controller(PIC),要么是 advanced programmable interrupt controller(APIC)。如果读过本文关于此问题的讲解就会知道 APIC 的 INTIN 线并没有隐含的优先级,但是 PIC 的 IRQ 线却有隐含的优先级。这可能会使一些人觉得设备的 IRQ(也就是 PIC 优先级)与 IRQL(设备的中断优先级)有着直接的关系。但是,这是错误的。这种误解很常见,现在是忘掉它的时候了!设备连接到 PIC 的地点、方式以及原因与设备在 Windows 中的设备优先级绝对无关。<br />
<br />
例如,假设系统使用的是 PIC。设备 X 连接到 IRQ3,设备 Y 连接到 IRQ7。如果 Windows 使用 PIC 的优先级,则 X 的中断总要优先于 Y 的,因为 X 的 IRQ 优先级更高(硬件中断)。然而,Windows 并没有使用 PIC 的硬件中断优先级来区分中断。所以,IRQ7 的 IRQL 高于 IRQ3 的情况是很正常的。<br />
<br />
在 Windows 上,设备的 IRQ 永远不会说明设备中断的紧急程度。不管用的是 PIC 还是 APIC 都是这样的。能改变么?唔,实在是不能。Windows 就是这么干的。
	</td>
  </tr>
</table>
<div class="footer">
  Copyright &copy; 1998-2003 XFOCUS Team. All Rights Reserved
</div>
</body>
</html>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -