14.2 蓝屏
不管系统崩溃的原因是什么,真正执行崩溃的函数是KeBugCheckEx(Windows DDK中有文档)。该函数接受一个停止代码(stop code,有时候也称为错误检查码[bug check code]),以及四个根据停止代码来解释的参数。KeBugCheckEx屏蔽了该系统的所有处理器上的所有中断以后,将显示器切换到低分辨率的VGA图形模式(Windows支持的所有视频卡都实现了这种模式)下,绘制一个蓝色背景,然后显示此停止代码,后面紧跟一些文本来建议用户应该怎么办。最后,KeBugCheckEx调用任何已注册的设备驱动程序错误检查回调函数(通过调用KeRegisterBugCheckCallback函数来注册的回调函数),从而让驱动程序有机会停止它们的设备(有可能系统数据结构已经被破坏得太严重,以至于蓝屏都显示不出来)。图14.2显示了Windows XP蓝屏的一个例子。
注 Windows XP Service Pack 1和更高的版本,以及Windows Server 2003引入了KeRegisterBugCheckReasonCallback函数,以允许设备驱动程序在一个崩溃转储的后面附加一些数据,或者将崩溃转储的信息写到其他的设备中。

图14.2 蓝屏的例子
KeBugCheckEx在蓝屏顶部的附近显示了停止代码的文本表示,在Windows 2000蓝屏的顶部,也显示了停止代码数值以及四个参数,但是,在Windows XP和Windows 2003蓝屏上,这些数值被显示在蓝屏底部。
第一行列出了传递给KeBugCheckEx的停止代码和四个参数。屏幕顶部附近的文本提供了该停止代码数值标识符的文本表示。根据图14.2中的例子,停止代码0x000000D1是一个DRIVER_IRQL_NOT_LESS_OR_EQUAL崩溃。当其中一个参数包含了操作系统或设备驱动程序代码的地址时(如同图14.2中显示的那样),Windows显示出该地址所处的模块的基地址、日期戳,以及该设备驱动程序的文件名。仅仅这些信息就有可能帮助你查明错误的组件。
虽然总共有一百多个不同的停止代码,但是,绝大多数代码很少在产品系统上发生(尽管也有)。相反地,少数常见的停止代码表达了大多数的Windows系统崩溃实例。 而且,四个附加参数的含义也取决于停止代码(并不是所有的停止代码都有扩展的参数信息)。然而,查看停止代码和参数(如果适用的话)的含义至少有可能帮助你诊断出当前正在经受失败的组件(或者引发系统崩溃的硬件设备)。
你可以在Windows调试工具箱(Windows Debugging Tools)帮助文件的“Bug Checks (Blue Screens)”一节中找到关于停止代码的信息(有关Windows调试工具箱的信息,请参见第1章)。你也可以在Microsoft的在线Knowledge Base(http://support.microsoft.com)中查找某一个停止代码和可疑的硬件或应用程序的名称。你可能会找到有关如何规避此问题、如何更新软件的信息,或者找到关于修正此问题的服务补丁包(service pack)的信息。Windows DDK中的Bugcodes.h包含了150个左右停止代码的一个完整列表,并且给出了有关某些停止代码的原因的额外信息。
通常你是在安装了一个新的软件产品或者硬件设备以后才开始看到蓝屏的。如果你刚刚添加了一个驱动程序,再重新引导,然后在系统初始化过程的早期看到了一个蓝屏,那么,你可以重新启动机器,在出现提示时按下F8键,然后选择“Last Known Good Configuration(最后已知的好配置)”。选择了最后已知的好配置选项,可以让Windows将注册表的设备驱动程序注册键(HKLM\SYSTEM\CurrentControlSet\Services)从最近一次成功引导的配置(在你安装该驱动程序以前)中恢复一份拷贝。从最后已知好配置的角度来看,一次成功的引导是指,所有的服务和驱动程序都已经完成了加载,并且至少有一次登录已经成功了(第5章更加详细地讲述了最后已知的好配置)。
如果你仍然看到蓝屏,那么,一种显而易见的办法是,卸掉在你第一次看到蓝屏之前刚刚加入的那些组件。如果从你上次加入新组件以来已经过了一段时间,或者你差不多同时加入了好多东西,那么,你需要将任何一个参数中引用到的设备驱动程序的名称记录下来。如果你识别出这些名称中有的名称跟你刚刚加入的组件有关联(比如,如果你安装了一个新的SCSI驱动器,你正好看到了Scsiport.sys),那么,你可能已经找到了罪魁祸首。
许多设备驱动程序有隐藏的名称,但是有一种办法可以让你找到哪个应用程序或者硬件设备是跟一个名称相关联的,那就是,通过在注册表的HKLM\SYSTEM\CurrentControlSet\Services键下面搜索该设备驱动程序的名称,你可以找到与该设备驱动程序相关联的服务的名称。注册表的这一分支正是Windows保存当前系统中每个设备驱动程序注册信息的地方。如果你搜索到了一个匹配,则寻找名为DisplayName和Description的值。有些驱动程序通过填充这些值来描述设备驱动程序的用途。例如,你可能会在DisplayName值中找到字符串“Virus Scanner”,这表明你已经运行了反病毒软件。你可以在计算机管理(Computer Management)工具(从Start菜单中,选择Programs/Administrative Tools/Computer Management)中显示驱动程序的列表。在Computer Management中,展开System Tools,System Information和Software Environment,然后选择Drivers即可。
然而,停止代码和四个关联的参数有时候还不足以诊断一次系统崩溃。例如,你可能需要检查内核模式的调用栈,才能查明触发此次崩溃的驱动程序或者系统组件。而且,因为Windows系统上的默认行为是在系统崩溃时自动重新引导的,因此,你可能没有时间记录下在蓝屏上显示的信息。所以,Windows在默认情况下,要试图将有关系统崩溃的信息记录到磁盘上,以便于事后进行分析。这也将我们带入到下一个话题:崩溃转储文件。







