《软件调试》导读之CPU篇
《软件调试》的第2篇是CPU的调试支持,由第2~7章组成,共有136页,是全书的第一个核心部分。写作和阅读这一篇的主要目标有如下几个:
- 介绍大多数软件工程师需要补充的CPU基础。
- CPU对软件调试核心功能的支持。
- CPU对软件调试扩展功能的支持。
- CPU中用于调试系统故障和自身问题的设施。
- 现代CPU和集成芯片所使用的硬件调试方案。
针对以上目标,第2、3章是满足目标1的,4~7章依次是满足另外四个目标的。下面对各部分的重点内容分别略作介绍。
一、介绍一个调试高手应该掌握的CPU层的基础知识。第2章和第3章是专门服务于这一目的的。调试好比行医看病,病人是计算机系统,要能看懂这个系统的毛病然后再对其施以治疗或者手术,那么必须了解其五丈六腑的结构,血脉流通的路线,生息运转的机理。要做到这一点,深刻理解计算机系统中硬件部分的核心——CPU——很重要。有人说,CPU是重要,但有什么必要在一本《软件调试》的书中写这个呢?调试高手还需要数学基础和语文基础呢,怎么不开两章讲讲呢?这一拮问不是没有道理,因此作者考虑到这一点,慎重选择了要讲的内容,并严格控制了篇幅。入选的内容要符合三个条件:一是够重要,二是够常用,三是与调试密切相关。于是,《软件调试》最后选择如下一些内容:
指令集的概念(2.1节):有几次面试年轻的程序员时,我询问:“你熟悉哪种CPU架构和指令集呢?”不止一次,有人不能理解这个问题。“CPU还有很多种(架构)吗?”x86太成功了!指令是CPU的语言,理解指令集是为构筑软件知识大厦打下一块不可少的基石。
IA-32处理器(2.2节):这也是一个备受胡略和误解的重要概念。有人说都进入64位时代了,还学IA-32干吗?殊不知,今天大多数PC使用的x64架构只是原有32位架构中的一种新的操作模式(e.g. IA-32e)。要理解x64,还需要先理解32位的情况。或者说,如果有扎实的32位基础,那么可以很容易理解64位,反方向则很难走通。因为长达三十多年的广泛应用(从1985年80386的推出算起),IA-32架构对计算机的影响太深入了,甚至超出了CPU,影响到了系统总线和外设的设计。2.2节使用4页半的篇幅全面浏览了已经推出的每一代IA-32 CPU,从386到今天的Core 2系列。
CPU的操作模式(2.3节):经常遇到的重要概念,扼要介绍。
寄存器(2.4节):将IA CPU的所有寄存器分为五类做了介绍:通用寄存器、标志寄存器、MSR寄存器、控制寄存器和其它寄存器。除了介绍概念外,这一小节还有一个目的是用作“调试手册”,我在调试时,时常还翻到这几页内容,查寄存器的位定义。
保护模式(2.5-2.7节):这几乎是我做面试时必问的一个概念。深刻理解这个概念,才能深刻理解今天的计算机系统在如何求解计算机领域的一个永恒课题——多任务。保护数据是保护模式的一个主要任务,虚拟内存是目前实现这一任务的主要方法。页机制是今天所有的商业操作系统都依赖的一种机制。理解虚拟内存和页机制对于软件调试也非常重要。因为如果搞不清楚这些概念,那么就会被虚拟地址、线性地址、物理地址、IO地址这些概念所搞晕。也不容易建立起计算机世界的空间概念——内存空间、进程空间等。
中断和异常(第3章):这两个概念有联系,又不同。有时又被混用,所以不少程序员对其模棱两可。IA-32架构将异常分为三种,这更是很多人闻所未闻。记得有个陌生的青年写EMAIL给我,对《软件调试》76页的说法“当CPU产生异常时,其程序指针是指向导致异常的下一条指令的”提出质疑:
“CPU产生异常时,其程序指针不是指向原来那条指令吗?”
我回信解释说“因为异常不同,异常发生时程序指针的取值是不同的”,他更加困惑:
“我还是不能理解,难道异常还有两种吗?”
我知道了他迷惑的根本原因,把表3-1“异常分类”发给了他,这下他彻底清楚了。是啊,如此重要的概念,我们的大学教育(包括计算机科学的研究生)里根本没有,也不能完全怪个人。
另外,3.4节的“中断和异常优先级”是写给高水平读者的较难内容。
二、CPU对软件调试核心功能的根本支持,即第4章。这是全书的核心内容之一,深入介绍了断点指令、调试寄存器和支持单步执行的陷阱标志。这三大支持是构筑今日调试技术的三大基石,很多至关重要的调试功能都是建立在这三个基础之上的,包括设置断点、变量监视、各种各样的跟踪执行、调试信息输出、内核调试等等。这一章共有32页,读者应该认真阅读每一页。4.4节的实模式调试器例析也值得仔细读,最好是把Tim Paterson先生所写的汇编代码打印出来对照阅读(链接为: http://www.patersontech.com/dos/Docs/Mon_86_1.4a.pdf ),同时又可以学习汇编和欣赏大师的“手笔”。
三、CPU对软件调试扩展功能的硬件支持,即第5章。第4章介绍的根本支持是从80386开始就定形了的东西。软件在不断发展,调试支持明显跟不上速度。因此今天的调试技术很多时候很乏力。第5章介绍的分支记录和性能监视机制可以说是从奔腾开始的新一代处理器引入的最重要调试支持。它是今天的大多数软件分析(profiling)和性能调优(performance tune)工具所依赖的基础设施。
四、机器检查机制(第6章)。这是奔腾CPU引入的一个旨在报告硬件问题的错误记录和报告机制。这一机制对很多软件工程师可能都很陌生。了解这一机制,不仅可以学到这样一个CPU层的基础知识,而且有助于建立“可调试性”的思想,也就是第5篇重点讨论的东西。
五、硬件调试方案(第7章)。软件问题是千变万化的,甚至可以说,很难找到两个负责的软件问题是一模一样的。因此,不同的调试工具和调试技术都有它的适用范围,不是无所不能的。比如有些问题,就适合用用户态调试会话来跟踪,有些问题使用内核调试比较合适,有些问题只使用单纯的软件调试器可能根本不行。这时就要使用硬件调试工具来帮忙。JTAG是CPU和其它大规模集成芯片普遍使用的测试和调试方案,广泛用于调试芯片自身、系统软件、固件和特殊复杂的软件问题。从某种程度上讲,基于JTAG技术的硬件工具只是为调试器访问调试目标提供了一种“硬件化”的通信方法。在大多数情况下,还是要依赖软件调试器这样的软件工具来实现各种调试。或者说,大多时候是把这种调试方式统一到纯软件的调试方案架构中,这样调试者就可以使用熟悉的调试功能来实现调试。比如,INTEL的ITP工具就以COM组件方式为WinDBG提供了服务,使WinDBG可以通过ITP工具进行内核调试,参见http://advdbg.com/blogs/advdbg_system/articles/903.aspx。
最后想提一下2.8节——系统概貌。这一节用一页的篇幅介绍了今天PC系统的硬件架构,也就是图2-13。在脑海中能有这样一幅图对于理解计算机系统很有好处。这里介绍了一系列常用的术语,比如南桥、北桥、总线、芯片组等。CPU、北桥和南桥是目前主板上的三颗最重要芯片。即将推向市场的下一代芯片组将把北桥的功能整合到CPU和南桥中,即所谓的“双芯片架构”,不过这仍不影响基本的概念,比如内存控制器(memory controller)依然存在,只不过是移到CPU内部。
总体来说,第2篇的内容都比较好理解,篇幅也不是很长。稍微用点毅力就可以将其通读一遍。希望读者看过后能对CPU的认识有一个明显的提高,使自己的硬件基础更扎实些。当然重中之重是调试支持,后面几篇还会接着讲......