没有合适的资源?快使用搜索试试~ 我知道了~
资源详情
资源评论
资源推荐
中断和异常
作者:Matt Pietrek
你可能感觉一切都好。但当你写了一些新代码并运行它时才知道你被感觉欺骗了!又出现了
令人烦恼的访问违规(Access Violation)。你可能也看到了那个令人害怕的号码 0xC0000005,
也就是 STATUS_ACCESS_VIOLATION。0xC0000005 是如何表示“刚刚出错了”的,以及 Win32®是
如何支持不同类型的异常的,这些并不为很多所知。在本月的专栏中,我要挖掘 Win32 的异常以
及它们是如何与硬件异常相关联的。在讨论硬件时主要针对的是 Intel x86 平台。
如果你曾经为 Windows® 3.x 编写过程序或编写过 MS-DOS® extender,你一定遇到过 0xD 这
个异常(一般保护性错误,简称为 GPF)。你也可能看到过其它错误,例如非法指令错误(异常
6)。这些代码并不是人工赋予的。任何 Intel 手册上都告诉过你,这些异常代码是 CPU 用来通
知各种问题或事件用的。在 Win32 中你看不到这些代码,因为 Windows® NT,这个 Win32 操作系
统家族的旗舰产品,被设计运行于多种平台上。它没有简单地让 Alpha 或 MIPS 版本的 Windows NT
使用 Intel CPU 的异常代码。
相反,Win32 使用它自己的一套号码系统来表示各种异常。在任何给定的 Win32 平台上,系
统把相应的 CPU 的异常代码映射到一个或多个通用的 Win32 异常代码上。例如,Intel CPU 上的
异常代码 0xD 可能变成 STATUS_ACCESS_VIOLATION(0xC0000005)。同样,异常代码 0xD 也可能
变成 Win32 的 STATUS_PRIVILEGED_INSTRUCTION(0xC0000096)异常。底层的硬件异常决定了它
应该被映射到哪个 Win32 异常上。
让我们从 CPU 异常和中断出发,开始我们的 Win32 异常之旅。异常(Exception)和中断
(Interrupt)是一种手段, 当正在执行代码时 CPU 通过它切换到一个完全不同的代码路径上以
处理一些外部的刺激或条件。中断通常是由外部的刺激引起的,例如,按下了一个键。而异常则
是代码或数据中的条件导致处理器生成了异常。CPU 试图读取一个没有物理内存映射到的地址时
会产生异常,这是最经典的一个异常的例子。
Intel CPU 保留了 32 个中断/异常号以处理各种情形。图 1 是一些常用的号码。它们中很多
意义很清楚,但是还有很多你没有遇到过(至少是在运行本专栏的样例程序之前)。MS-DOS 上
的老手可能奇怪竟然列出的 INT 5H 不是打印屏幕,INT 8H 也不是计时器中断。这是为什么?图
1 的描述是 Intel 对异常和中断的定义。但不幸的是,在 Intel 迅猛发展之前,MS-DOS 的作者已
经把其中的一些中断号用作其它用途。结果导致当程序员使用 BOUND 指令时竟然意外到得到了屏
幕的输出内容。
图 1:Intel 定义的异常和中断
代码 定义
00 除法错
01 调试异常(单步和硬件调试)
02 不可屏敝中断(NMI)
03 断点中断
04 溢出中断(INTO)
05 越界中断
06 非法指令
07 协处理器不可用
08 异常嵌套
0A 非法任务状态段(TSS)
0B 段不存在
0C 堆栈错误
0D 一般保护性错
0E 页错误
为了简单起见,本专栏以下的部分中我就用异常来代表异常或中断。就像我前面说的,中断
和异常在技术上是不同的。另外,异常可以被进一步分成故障(Fault)、自陷(Trap)和终止
(Abort)。我不想在这里对它们作详细描述,你可以简单地认为它们是一样的。
当异常发生时,CPU 挂起当前的执行路径,把控制权交给异常处理程序。CPU 把标志寄存器
(EFLAGS)、代码段寄存器(CS)、指令指针寄存器(EIP)压入堆栈以保护当前的执行状态。
接着,根据异常代码查找事先设计好的处理这个异常的程序的地址,并把控制权交给它。实际上,
异常代码就是中断描述表(Interrupt Descriptor Talbe,IDT)的索引,而中断描述表指出异
常应该交由谁处理。
IDT 是 Intel CPU 使用的基本数据结构,它由多达 256 个中断描述符组成,每个长为 8 字节。
中断描述符表由操作系统创建和维护,因此被理解为是 CPU 的数据结构,但是它也受到操作系统
的控制。如果操作系统把 IDT 搞错了,那整个系统立马完蛋。
在大多数操作系统,包括基于 Win32 的系统上,IDT 被放在高特权级内存上,低特权级的应
用程序根本不能访问它。这与实模式的 MS-DOS 程序有很大不同,在那里,应用程序通常替换中
断表(IDT 在实模式下的一种版本)。由于多个基于 MS-DOS 的程序、驱动程序、TSR(终止并驻
留程序)缺乏协调,导致 MS-DOS 系统和 16 位的 Windows 系统特别不稳定。在最新的 32 位操作
系统上,CPU 严格限制对 IDT 的访问,相应地增加了稳定性。然而,Win32 设备驱动程序(高特
权级)可以访问 IDT,并且可以修改它在 IDT 中的相应项。
现在回到异常发生时。CPU 把异常号作为索引获取 8 字节的描述符。在描述符中包括各种域。
图 2 显示的是中断描述符的一种简化形式。注意,对于每个异常来说,都有一个相应的异常处理
程序地址(CS:EIP),控制权就是要转到这个地址。图 3 显示了 GPF(异常 0xD)发生时的事件
顺序。
图 2:中断描述符
图 3:异常发生时的事件顺序
要是在平时,到这里我会写一个能显示 IDT 内容的程序。但不幸的是(至少对于我来说),
应用程序不能访问 IDT。这是因为在 Win32 下,应用程序运行在 Ring 3,这是最低的特权级。Win32
操作系统核心运行在 Ring 0(内核或管理模式),这是最高的特权级。同时,关键数据结构,
例如 IDT,只能通过 Ring 0 的代码进行访问。(Ring 1 和 2 在 Win32 中没有使用。从 80286 开
始起它们就存在,但据我所知,还没有人使用这些特权级。)
由于我不能写一个可以读取 IDT 的程序,那就拿一些其它资料吧。图 4 是用 SoftIce/NT 的
IDT 命令得到的前 30 个中断描述符表项。SoftIce 作为 Ring 0 下的驱动程序运行,所以它对 IDT
有读/写权。
图 4:SoftIce 的 IDT 命令输出结果
Int Type Sel:Offset Attributes Symbol/Owner
IDTbase=80036400 Limit=07FF
0000 IntG32 0008:8013C354 DPL=0 P _KiTrap00
0001 IntG32 0008:8013C49C DPL=3 P _KiTrap01
0002 IntG32 0008:0000137E DPL=0 P
0003 IntG32 0008:8013C764 DPL=3 P _KiTrap03
0004 IntG32 0008:8013C8B8 DPL=3 P _KiTrap04
剩余10页未读,继续阅读
GL2Luu
- 粉丝: 2
- 资源: 14
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功
评论0