如何在一分钟内解决Windows系统崩溃问题

来源:互联网 发布:微信端口号 编辑:程序博客网 时间:2024/05/01 23:26

Dirk A. D. Smith / translate by Minghui Li

系统崩溃了:如果你是幸运的,它只在你那天崩溃了。更可能的是,你好几天都会遇到这个问题,或者是几周、几个月。毕竟系统很少会只失败一次。甚至它会一直崩溃,直到你找到修复问题的方法。

本文将为你提供快速解决问题的方法,使用一个工具,你可以在几分钟内解决大约50%的windows服务器和工作站的崩溃问题,这个工具就是WinDbg,免费的Windows调试器(debugger)。

你可能从来没用过调试器,没有这个工具或者根本不想用它。毕竟它是一个开发者的工具,而不是管理员的,对吗?是的,但是你所需要知道的是它非常容易学习,如果你从根本上熟悉这个调试器,将会提升你的能力,会在你的履历上起到重要的作用。

还在犹豫吗?考虑一下这个:在重新启动一个崩溃的机器之后,我们带来了调试器,打开内存转储(dump)文件,给这个调试器一个命令,然后知道系统崩溃的原因是驱动程序,而且还知道驱动程序的名字——所有这些只用了不到一分钟。我们得承认,这要安装调试器和对其进行配置,而且我们知道应该使用什么命令,知道去找什么。

但是,看完这篇文章之后,你也会知道这些。

为什么Windows会崩溃?

时至今日,在x86处理器上Windows是使用最为广泛的系统,x86实现了保护机制使得多个程序可以同时运行,而不会“踩倒别人的脚趾”。这种保护来自四级特权,这四级特权具有不同的访问系统内存和硬件的能力。其中两级通常被称作内核模式(kernel mode)和用户模式(user mode)。

内核模式是x86上最有特权的一级。Windows操作系统和驱动程序被认为是最可信的,因此它们都运行在内核模式上。这就确保了它们可以无拘无束地访问系统的资源和最大化系统性能。其他的软件被赋予用户模式——x86上最没特权的一级,限制其直接访问系统的大部分内容。应用程序(例如Microsoft Word)也是运行在用户模式,使得应用程序不会使系统级的软件损坏。

尽管内核模式软件被保护着使得不会被用户模式应用程序损坏,但是它可能会受到其他内核模式软件损坏,例如,如果一个驱动程序错误地访问一部分内存,而这部分内存正被其他软件使用(或者这部分内存没有被标记为可以被驱动程序访问),那么Windows将会阻止整个系统。这被称为一个bug检查或者崩溃(crash),Windows显示出蓝屏死机(Blue Screen of Death,BSOD)。大约95%的Windows系统崩溃都是由有bug的软件(或者有bug的设备驱动程序)造成的,它们大部分来自于第三方公司。剩余的5%是由硬件设备的故障造成的,其经常提示为“损坏了内存内容的崩溃”。

另外一点常识是大多数的崩溃是可重复的。很少有管理员能立即解决系统崩溃问题,因此崩溃问题会一次又一次的出现,通常要过几星期或者几个月才能发现答案。在第一次出现问题之后,就立即解决问题,你可以避免时间消耗和重复崩溃的代价。

我们将焦点集中在解决Windows 2000、XP和Server 2003系统崩溃问题,这个过程对于Windows服务器和桌面系统是一样的。关于调试和解释的过程,通过很小的变化,它们可以应用于其他操作系统,如Linux、Unix和NetWare。

开始

使用WinDbg解决系统崩溃,你需要以下准备:

· 一个具有25M硬盘空间的PC,Internet连接和微软IE5.0或者更新版本。

· 一个运行着Windows Server 2003、Windows 2000或者Windows XP的PC

· 最新版本的WinDbg

· 一个内存转储(page file必需位于C: 盘,Windows可以保存内存转储文件)

内存转储是在系统崩溃时内存的一个快照。第一眼看上去,很少有东西比转储文件更神秘了,然而它是最好的地方来获得崩溃的信息。你可以用其他方法或者这个数据——用户或者管理员可能会记得系统在崩溃时的行为——但是他们可能也会忘记,提供不完整或者不准确的信息。

Windows Server 2003、2000和XP创建三种类型的内存转储文件:

小转储(Small or mini dump):它是一个很小的64K文件,它不包含内存中任何二进制和可执行文件。exe文件对于完整的和适当的崩溃分析是必需的,因此小转储的价值有限。然而,如果你在创建了转储文件的机器上调试,调试器会在系统根目录下发现它们,除非它们被系统更新改变了(我们将在后面为它提供一节)。XP和Server 2003在默认情况下会产生小转储,每次崩溃事件都会保存一个文件。和系统保存所有的小转储文件相比,系统只保存最近的完全转储。Windows 2000可以保存小转储,但是系统默认只保存一个完全转储。

核心内存转储(Kernel dump):它等价于被操作系统内核占用的RAM的数量。对于具有512M RAM的XP系统的PC,它大约是60M,但是它可能会有些变化。对于大多数场合,这个崩溃转储是最有用的。它比完全转储要小得多,但是它只忽略了那些不太可能涉及崩溃的内存。

完全转储(Complete or full dump):它等价于机器中RAM的全部数量,因此具有512M的RAM的机器将会创建一个512M的转储文件(再加一点点)。一个完全转储包含了内存中提供的所有数据和可执行程序,它的规模使得保存或者转移到另一个机器上调试变得很笨拙。Windows 2000默认情况下产生一个完全转储。

因为XP和2003被设置成为每次崩溃事件都保存一个小转储,那么自从机器启动之后的每次崩溃都会有一个小转储文件,这个数据是十分重要的,它能给你提供一个丰富的历史来供你调查。

保存内存转储

为了通过调查内存转储来解决系统的崩溃,需要设置你的服务器或者PC自动地保存它们,按照如下步骤:

1. 右键单击“我的电脑”

2. 选择“属性”

3. 选择“高级”

4. 在“启动和故障恢复”部分,选择“设置”。其显示了“启动和故障恢复”对话框

5. 在“写入调试信息”部分,选择“核心内存转储”

当还在“启动和故障恢复”对话框中时,确保“系统失败”部分下面的选项也被选择了:

· 将事件写入系统日志

· 发送管理警报

· 自动重新启动

在写调试信息时,你可以选择只保存最新的转储文件或者让系统在创建新的文件之前重命名现有的转储文件。我们倾向于保存转储文件,因为之前的转储文件可以提供一些额外的或者不同的信息,然而,空间是一个问题,因此根据你的需要设置这个选项。

写调试信息部分也告诉你转储文件创建的位置,在XP和2003系统中,小转储文件位于%SystemRoot%/Minidump或者c:/Windows/Minidump;核心和完全转储文件位于%SystemRoot%/MEMORY.DMP或者c:/Windows/MEMORY.DMP。对于Windows 2000,内存转储文件位于c:/winnt/memory.dmp。

如果在你的机器上没有转储文件,你可以从另外一个机器上得到或者从这里下载。这个核心转储文件大约是20M的压缩文件,释放之后大约60M。它是用一个产生系统崩溃的测试工具创建的。

获得调试器

调试器是免费的,可以从微软的网站下载。在这个站点上,向下滚动,直到你看到标题“Installing Debugging Tools for Windows.”选择这个连接,“Install 32-bit version…”,然后选择最近的non-beta版本并安装。最近的版本大约为12M的下载。你可以在PC上安装而不用重新启动(不要怀疑这个站点是不是变了,微软每年都会发布至少一次,来保持对它的支持)。

这个发布包括了KD.EXE——命令行的内核调试器,NTSD.EXE——命令行的用户模式调试器,CDB.EXE——命令行的用户模式调试器(ntsd.exe的一个变化)和WinDbg——GUI版本的调试器。WinDbg支持内核模式和用户模式的调试,因此WinDbg是我们所使用的。

设置调试器

有两种方法查看崩溃数据:查看系统stop时的内存中的内容(通过一根null-modem电缆连接一台正在运行的PC,或者调用你之前安装在系统中的一个软件,例如SoftICE,它能让你在一行一行地执行内存中的代码)。

Null-modem电缆是串行电缆,被设置为在两个串行端口中发送数据,在大多数计算机商店中都有销售。不要混淆null-modem电缆和标准的串行电缆——它们不连接穿行端口。

最小化中断是大多数管理员的目标,我们选择第二种方法:重新启动服务器或PC,启动调试器,然后打开转储文件。

从程序中的Debugging Tools for Windows,选择WinDbg,然后调试器就出现了,你将立即注意到很多……,没关系。一个空白的屏幕,这是因为你需要指定一个转储文件来分析和下载符号表用于分析。让我们首先看一下符号文件。

符号表是编译的副产品。当程序被编译时,源代码被从高级语言翻译为机器代码,与此同时,编译器创建符号文件,其包含了标识符列表和它们在程序中的位置和它们的属性。一些标识符是全局或者局部的变量和函数调用。一个程序在执行时不需要这些信息,因此它可以被取出并存放在另一个文件中,从而减少最终执行文件的大小。

较小的可执行程序占用较小的磁盘空间,载入内存更快。但是也有不好的一面:当程序导致一个问题时,OS只知道问题出现在的16进制地址。你需要其他信息来确定哪个程序在使用那部分内存,在那做什么事情。Windows符号表可以提供答案,访问这些表就象在你的系统内存中放置一个地图。

Windows符号文件可以从微软站点免费获得,调试器可以自动地检索它们。为了设置调试器做这件事情,首先验证你有Internet连接和在WinDbg中设置符号文件路径:选择File | Symbol File Path,然后输入如下的字符串:

SRV*c:/local cache*http://msdl.microsoft.com/download/symbols

替换你自己的目录路径C:/local cache。例如,如果你想让符号表位于C:/symbols,那么你可以设置符号路径为:

SRV*c:/symbols*http://msdl.microsoft.com/download/symbols

当打开一个内存转储文件是,WinDbg会查找EXE/DLL并抽取版本信息。然后它创建一个对微软的符号服务器的request,其包含了这个版本信息,然后定位准确的符号表并从中取得信息。如果你对于检索符号文件有困难,那么请检查你的防火墙,使之允许访问http://msdl.microsoft.com

如果你将调试器限制在你机器上的内存转储文件,你就需要额外一点硬盘空间来存放符号表,在多数情况下,5M就足够了。但是如果你打算查看其他机器(不同的Windows版本和patch级别)上的转储文件,你需要更多的空间来存放额外的符号文件(支持这些Windows版本)。

系统更新

如果你努力分析一台机器上的小转储文件,而这个机器在创建转储文件之后更新了,或者如果你分析另一个机器上的转储文件,在你的系统根目录下的驱动程序将是不同的,比小转储文件创建时候的要新。为了解决这个问题,设置可执行程序镜像文件路径(File | Image File Path),然后输入如下字符串:c:/windows/System32; c:/windows/system/System32; http://www.alexander.com/SymServe

载入转储文件

为了打开你要分析的转储文件,选择File | Open Crash Dump,你将会被问及“if you want to save workspace information”,选择yes如果你想让它记住转储文件的位置。WinDbg查找Windows符号文件,它参考符号文件路径,访问microsoft.com,然后显示结果。关闭Disassemble窗口使得你可以在命令窗口中工作。

NOTE:如果调试器在打开转储文件时看起来很繁忙,不要感到奇怪,尤其在你第一次打开时。它需要检索符号,对于小转储,它需要检索binaries,这需要几分钟的时间。另外,最新的WinDbg会花费更多的时间来检索驱动数据。耐心,值得等待!

这个时候,WinDbg可能会返回一个错误消息,例如:

*** ERROR: Symbol file could not be found. Defaulted to export symbols for ntoskrnl.exe -

它表明不能找到正确的符号文件。如果是这样,请假查下面三件事情是否正确:

· 你的路径不正确,请检查确保没有打字错误。

· 你的连接失败,检查你的Internet连接,确保它能正常工作。

· 你的防火墙阻止访问符号文件或在访问过程中损坏了符号文件。

如果你的路径和连接都是可靠的,那么问题很可能就是你的防火墙。如果防火墙初始化为阻止WinDbg下载符号文件,它会导致破坏的符号文件。取消防火墙的阻止,尝试重新下载符号文件,如果还是不正常,那么符号文件被损坏了。最快的解决方法就是关闭WinDbg,删除符号目录(你最可能的设置是c:/symbols),然后unblock防火墙,现在重新打开WinDbg和一个转储文件,调试器将会重新建立一个文件夹并重新下载符号文件。

如果你看到这个消息“***** Kernel symbols are WRONG. Please fix symbols to do analysis.”,WinDbg不能正确地检索符号,它会求助使用默认的符号表。但是正如消息提示的,它不能产生精确的结果。记住符号表示在程序编译的时候产生的,因此对于每个版本的Windows、每个patch、每个hot fix等,都有一个符号表文件。使用错误的符号去查找崩溃的原因就像使用圣地亚哥的地图来驾驶一艘船到波士顿。你必须使用正确的那个文件,因此返回上面提到的,确保正确的路径、良好的连接和防火墙没有被阻止。

浏览WinDbg的输出,你可能看到一个类似下面的错误消息:

*** ERROR: Module load completed but symbols could not be loaded for driver.dll
Unable to translate address bf9a2700 with prototype PTE
Probably caused by: driver.dll (driver+44bd)

它表明不能定位第三方驱动程序的符号。这意味着调试器发现了一个驱动程序是错误的,但是由于它是第三方的,因此没有对应的符号(微软不提供所有第三方的驱动)。你可以忽略这个问题。Vendor公司在发布驱动程序时一般不提供符号文件,而且他们对于你的工作是没有必要的,你可以不用它们就能查出有问题的驱动程序。

调试命令

在WinDbg载入转储文件之后,需要开始查找诊断信息。但是有大量的命令,你需要的只有两个:!analyze –v和lmv。

!analyze –v显示的信息描述了系统在崩溃时的状态、遭遇的错误和谁是首要“疑犯”。

lmv显示驱动程序列表以及它们的路径、版本和vendor信息,还经常包括产品的描述。

如果你是软件工程师,或者至少你不想被认为是无能的,那么你会这么读第一条命令:“bang analyze dash vee.”

Analysis with !analyze –v

在命令窗口底部的命令行中输入!analyze –v,它给出的解释是英文和程序员语言的组合,但是这就是一个伟大的开始。实际上,在大多数情况下你不需要再做什么,如果你认识到了崩溃的原因,那么你已经完成任务了。

这是一个例子。在输入!analyze –v之后,我们收到下面的输出:

kd> !analyze -v

KERNEL_MODE_EXCEPTION_NOT_HANDLED (8e)

(This is a very common bugcheck. Usually the exception address pinpoints the driver/function that caused the problem. Always note this address as well as the link date of the driver/image that contains this address.)

Arguments:
Arg1: c0000005, The exception code that was not handled
Arg2: bf9bc4bd, The address that the exception occurred at
Arg3: f69f02bc, Trap Frame
Arg4: 00000000

Debugging Details:
------------------
EXCEPTION_CODE: c0000005

FAULTING_IP:
vdriver+44bd
bf9bc4bd 8b4014   mov  eax,[eax+0x14]

TRAP_FRAME: f69f02bc -- (.trap fffffffff69f02bc)
ErrCode = 00000000
eax=00000000 ebx=01740000 ecx=010886a0 edx=f69f069c esi=fa07d400 edi=e161f7f8
eip=bf9bc4bd esp=f69f0330 ebp=f69f0344 iopl=0   nv up ei pl nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000    efl=00010202
vdriver+44bd:
bf9bc4bd 8b4014   mov  eax,[eax+0x14] ds:0023:00000014=????????

DEFAULT_BUCKET_ID: DRIVER_FAULT
BUGCHECK_STR: 0x8E
LAST_CONTROL_TRANSFER: from bf9ba5cf to bf9bc4bd

STACK_TEXT:
f69f0344 bf9ba5cf e161f7f8 e17f8e30 e21e4530 vdriver+0x44bd
f69f06b0 f69f06e0 e2638678 f69f06e4 f69f0890 vdriver+0x25cf
e1bd6b90 1f0507b6 00000000 e1622008 00000010 0xf69f06e0
00000000 00000000 00000000 00000000 00000000 0x1f0507b6
f69f0bf0 805766ef f69f0c78 f69f0c7c f69f0c8c nt!KiCallUserMode+0x4
f69f0c4c bf8733cd 00000002 f69f0c9c 00000018 nt!KeUserModeCallback+0x87
f69f0ccc bf8722a5 bc667998 0000000f 00000000 win32k!SfnDWORD+0xa0
f69f0d0c bf873b38 7196bc2d f69f0d64 00affed0 win32k!xxxDispatchMessage+0x1c0
f69f0d58 805283c1 00afff2c 804d2d30 ffffffff win32k!NtUserDispatchMessage+0x39
f69f0d58 7ffe0304 00afff2c 804d2d30 ffffffff nt!KiSystemService+0xc4
00afff08 00000000 00000000 00000000 00000000 SharedUserData!SystemCallStub+0x4

FOLLOWUP_IP:
vdriver+44bd
bf9bc4bd 8b4014   mov  eax,[eax+0x14]

FOLLOWUP_NAME: MachineOwner
SYMBOL_NAME: vdriver+44bd
MODULE_NAME: vdriver
IMAGE_NAME: vdriver.dll
BUCKET_ID: 0x8E_vdriver+44bd

找到“Debugging Details.”部分,然后向下查找DEFAULT_BUCKET_ID:,它提供了错误的一般类型,这里是DRIVER_FAULT,表明一个驱动程序出错了。在向下查找IMAGE_NAME,我们看到vdriver.dll,我们找到了疑犯。

lmv分析

下一步是要确认疑犯的存在并找到它的详细信息,在命令行中输入lm,它会显示被加载的模块;字符v会告诉调试器采用详细的(verbose)输出,显示模块的所有详细信息,这有许多信息。定位到感情趣的驱动程序需要花费一段时间,因此通过选择edit|Find简化过程。

这是使用lmv命令的输出例子:

kd> lmv
bf9b8000 bfa0dc00 VDriver  (no symbolic information)
Loaded symbol image file: VDriver.dll
Image path: /SystemRoot/System32/VDriver.dll
Checksum: 00058BD5 Timestamp: Fri Sep 28 10:12:47 2001 (3BB4855F)
File version:  5.20.10.1066
Product version: 5.20.10.1066
File flags:  8 (Mask 3F) Private
File OS:   40004 NT Win32
File type:  3.4 Driver
File date:  00000000.00000000
CompanyName:  Video Technologies Inc.
ProductName:  VDisplay Driver for Windows XP
InternalName:  VDriver.dll
OriginalFilename: VDriver.dll
ProductVersion: 5.20.10.1066
FileVersion:  5.20.10.1066
FileDescription: Video Display Driver
LegalCopyright: Copyright© Video Technologies Inc. 2000-2004
Support:   (800) 555-1212

使用Edit|Find定位可以的驱动程序,如果vendor是周到的,那么完整的driver/vendor信息就会显示出来。

你看到的信息的数量取决于driver verdor,一些vendor在他们的文件中只放了少量的信息;其他的,例如Veritas放入了包括从公司名字到支持电话号码等信息。如果vendor是周到的,命令结果和这里显示的很类似。

在找到vendor的名字之后,到他们的网站检查更新、知识库文章和其他支持信息。如果不存在这些条目或者问题的解决方法,那就与他们联系。他们可能会让你发送调试信息(很容易考贝调试器中的输出内容到email或者word文档),或者他们会让你发送内存转储文件(现压缩成zip,这样可以减小文件,还可以保护数据的完整性)。

并不是总是这么容易

找到错误常常是简单的过程,但是并不是总是这样。至少50%(常常是70%)的情况下,调试器找到了崩溃的原因,但是有时候,它提供的信息是误导性的或者不充分的。那你该怎么做呢?

不一致的答案

如果你重现了崩溃,但是没有得到明确的或者一致的原因,它可能是内存的问题。下载这个免费的测试工具Memtest86,这个简单的诊断工具很快而且很好用。

许多人对内存问题大打折扣,因为它们引起系统崩溃的比例很小,然而它们经常使你花很多时间。

操作系统是疑犯

不太可能!这看起来很可疑,操作系统很少出错。如果ntoskrnl.exe(Windows内核)或者win32.sys(是主要负责Windows上的GUI层的驱动)是疑犯的话,而且它们经常是,不要很快接受这个建议。很可能是一些错误的第三方设备驱动程序调用Windows组件去执行一个操作或传递了一个错误的指令,例如告诉它去写一个不存在的内存。因此当操作系统可能存在错误时,排除其他所有可能的错误再给微软打电话!在调试Unix、Linux和NetWare时也是这样。

错误的驱动程序名字

你经常会看到反病毒驱动成为崩溃的原因。例如,在使用!analyze –v之后,调试器在IMAGE_NAME向你报告是反病毒程序的驱动,但是你要记住这样的驱动经常被指定,但却不是“凶手”。这是原因:对于反病毒代码,它必须查看所有的被打开和关闭的文件,为了完成这个,代码位于操作系统中的一个较低的层次并不断地工作着。事实上,它是非常繁忙的,当崩溃发生时,它经常位于函数调用堆栈上,即使并不是它引起的。因为位于堆栈上的任何第三方驱动会立即成为疑犯,因此它经常被指定。从数学的角度很容易看到它是多么容易位于堆栈上,并确实引起了问题。

很少或者没有vendor信息

并不是所有的vendor都包含了必要的信息(甚至它们的名字都没有!)。如果你使用lmv命令,但是什么也没得到,查看一下image路径的子目录(如果有的话),经常会有vonder的名字或者缩写。另外一个办法是搜索一下Google,输入驱动的名字和/或文件夹的名字,你可能会得到vendor的信息或者其他人发表的关于驱动的信息。

总结

当系统崩溃,你的首要任务是让它起来继续运行。第二件事情是修复问题,避免将来再次崩溃。原意是用任何能帮助你的工具——甚至是Windows调试器。它不会给你每次崩溃事件的原因,但是只用两个简单的命令,它就能帮助你解决50%甚至更多的问题。

作者Smith是Alexander LAN, Inc.的主席和创始人,可以用 dirk@alexander.com 与他取得联系。

From: How to solve Windows system crashes in minutes

原创粉丝点击