书名:软件调试(第2版)卷2:Windows平台调试(上、下册)
ISBN:978-7-115-53838-3
本书由人民邮电出版社发行数字版。版权所有,侵权必究。
您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。
我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。
如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。
著 张银奎
责任编辑 吴晋瑜
人民邮电出版社出版发行 北京市丰台区成寿寺路11号
邮编 100164 电子邮件 315@ptpress.com.cn
网址 http://www.ptpress.com.cn
读者服务热线:(010)81055410
反盗版热线:(010)81055315
本书是国内当前集中介绍软件调试主题的权威著作。本书第2卷分为5篇,共30章,主要围绕Windows系统展开介绍。第一篇(第1~4章)介绍Windows系统简史、进程和线程、架构和系统部件,以及Windows系统的启动过程,既从空间角度讲述Windows的软件世界,也从时间角度描述Windows世界的搭建过程。第二篇(第5~8章)描述特殊的过程调用、垫片、托管世界和Linux子系统。第三篇(第9~19章)深入探讨用户态调试模型、用户态调试过程、中断和异常管理、未处理异常和JIT调试、硬错误和蓝屏、错误报告、日志、事件追踪、WHEA、内核调试引擎和验证机制。第四篇(第20~25章)从编译和编译期检查、运行时库和运行期检查、栈和函数调用、堆和堆检查、异常处理代码的编译、调试符号等方面概括编译器的调试支持。第五篇(第26~30章)首先纵览调试器的发展历史、工作模型和经典架构,然后分别讨论集成在Visual Studio和Visual Studio(VS)Code中的调试器,最后深度解析WinDBG调试器的历史、结构和用法。
本书理论与实践结合,不仅涵盖了相关的技术背景知识,还深入研讨了大量具有代表性的技术细节,是学习软件调试技术的珍贵资料。
本书适合所有从事软件开发工作的读者阅读,特别适合从事软件开发、测试和支持的技术人员阅读。
2007年,我和Raymond(张银奎)第一次在上海见面。他对Windows操作系统的浓厚兴趣给我留下了深刻的印象,他的兴趣遍及有关Windows操作系统的所有细节,包括这个产品背后的人及其演变过程。
Raymond已经在软件开发岗位工作了十几年。现在他把多年的经验和对Windows操作系统的深刻理解结合起来,创作了这本关于调试的著作。调试是计算机领域中最耗费时间和充满挑战的任务之一,也是许多软件工程师需要提高的一个领域。
这本书所覆盖的主题之广度是惊人的。从最底层硬件对调试的支持落笔,Raymond带你遍历了系统中支持调试的所有层面——从用户态到内核态。此外,他还全面深入地介绍了编译器的调试支持、调试工具和各种基础设施。
据我多年在VMS操作系统开发团队工作的经验,我发现有些工程师掌握了调试技术,而有些工程师并不具备这样的能力。利用可用工具插入合适的断点和分析追踪信息都需要特殊的技巧。
细细品味这本书,你将获得这些重要的软件开发技巧,提升控制软件和编写代码的能力。
我多么希望这本书是用英文写的!
——大卫·所罗门(David Solomon)
Windows Internals(Microsoft Press)一书的合著者
David Solomon Expert Seminars公司总裁
Raymond’s intense interest in the details of the Windows operating system—including the people behind the product and its evolution—impressed me from our first meeting in Shanghai in 2007.
Raymond has been working as a software developer for more than 10 years. Now he has taken his years of software development experience and intimate knowledge of the Windows operating system and created this incredible book on debugging. Debugging is one of the most time-consuming and challenging tasks in computer world. It’s an area to improve for a lot of software engineers.
The breadth of topics in this book is impressive. Starting from the lowest machine level hardware support for debuggers, Raymond takes you through the entire stack of debugging support—from user to kernel mode—as well as a comprehensive look at compiler’s debug support, the debugging tools and infrastructure.
In my years working in the VMS operating system development group, there were those who could debug and those that couldn’t. It takes a special skill to be able to leverage the available tools to insert the appropriate breakpoints and debug trace messages.
Digesting this book will help you gain these important software development skills, strengthen your capability to control software and write code.
I only wish it was in English!
—David Solomon
co-author, Windows Internals (Microsoft Press)
President, David Solomon Expert Seminars, Inc.
《软件调试(第2版):卷2》就要出版了,虽然在正文中已经写了近百万言,但笔者还是想在前言中再写一些话送给读者。
首先,软件是信息时代里的主角,它已经对人类社会产生了巨大的影响,而且正在产生更大的影响。如果你在做软件方面的工作,那么你应该感觉很幸运;如果你在学习软件,那么你选择了前景无限广阔的方向,一定要坚持。
如何把软件学好呢?这是一个比较大的话题,笔者愿把亲身经历分享给读者。
虽然笔者在读高中时就编写过基本的BASIC程序,但是那时根本没有对程序和软件建立起什么概念。1992年,笔者进入大学后,开始比较系统地学习计算机的硬件和软件。还记得笔者学习的第一门计算机课是Fortran语言,学得也是云里雾里。而后笔者陆续学习了C语言、数据结构和微机原理。
微机原理有两门课:一门讲8051单片机,比较偏硬件;另一门讲8086汇编语言编程,比较偏软件。教8086汇编语言的老师30多岁,个头很高,说话不多。如今,笔者已经记不起他在课堂上讲过的任何技术内容,但是清晰地记得他使用了一种很独特的教学方法。与其他老师详细地讲解每个知识点不同,他只是给大家留了一个大作业,让大家自己用汇编语言写一个程序,自己决定程序的功能和界面。留了这个作业后,同学们几乎再没见到过这位老师。一到他的课,同学们就到机房里上机,闷头写程序,各写各的。
课堂上的那点时间不够用,所以同学们经常在课外时间做这个作业。当时的计算机资源还比较宝贵,系里的机房常常人满为患。于是同学们有时要到计算机中心去上机,上机不是免费的,需要花钱购买机时,当时流行用饭票来支付。
就在那个学期,一个同班同学提议合伙买一台个人计算机(Personal Computer,PC),笔者很感兴趣,一边给家人写信申请,一边用开学时带的钱“入股”。于是笔者和另外3位同学合伙,一起买了一台PC,硬件配置是386DX/40,1.2MB + 1.44MB软驱,4MB内存,210MB硬盘,TVGA显卡,0.28mm点距的彩色显示器,安装的操作系统是DOS和Windows 3.1。我们在上海华山路上一家经营办公用品的店里购买了一台组装机,一共花了6505元,又花了100多元的出租车费,把主机和CRT显示器运回上海交通大学闵行校区。笔者翻开了久未打开过的大学日记,才发现购买那台PC的时间是1994年11月11日,星期五。日记中还夹着父亲当年为笔者汇款时用的专用信封,附言部分写着8个字“学习正用,买也值得”。
现在回想起来,笔者真正的编程经历是从有了那台PC开始的。每次轮到笔者上机的时候,笔者都写代码和调试,从来不打游戏,也不做其他事情。为了更好地利用上机时间,笔者常常先把代码写到本子上,上机时快速录入计算机,然后编译、运行、看结果,再调试。当时笔者就喜欢用调试器,单步跟踪程序的每一行代码。现在清楚记得的一个细节是,为了能在DOS下显示出更好看的界面,笔者在DOS程序里用汇编语言中的软中断指令调用Video BIOS的服务,把系统的显示模式从文本模式改为图形模式。当笔者单步执行切换图形模式的指令时,CRT显示器上会出现五颜六色的光斑和各种诡异的画面……
因为投入的时间很多,所以笔者的“8086汇编语言”程序越写越大,功能越来越多,不仅有图形化的下拉菜单,炫耀技术的内存驻留(Terminate and Stay Resident,TSR)功能,还可以驱动PC喇叭播放《两只老虎》的音乐。有了比较扎实的汇编语言基础后,笔者又继续用C语言编写Windows程序。
回顾笔者学习软件的亲身经历,如果要分享经验,那么第一条经验就是多写代码,多调试。笔者一直觉得自己不是一个聪明的人,如果说笔者的软件技术学得还可以,那么靠的就是这条经验:几乎每一天都写代码,几乎每一天都调试,坚持了二十多年,已经成为了习惯。
既然你选择了这本书,那么你多半是同意笔者的“调试方法论”的,剩下的问题就是如何能比较快地学会调试技术。接下来,笔者简要介绍一下《软件调试》的写作历程和阅读方法。
2002年,在泰为科技公司工作时,笔者有一天晚上加班赶进度,在紧急时刻却出现了一个诡异的Bug,当时的程序是为Nokia手机编写的,不太好调试,同事们一遇到Bug,主要用print。但笔者还是喜欢在模拟器中用调试器来调试。对于那天晚上的问题,print方法很不奏效,笔者用调试方法抽丝剥茧,一步步排查,终于找到了问题的根源。问题解决了后,大家都笑逐颜开,一起加班的好朋友刘伟力很感慨地说:“断点真神奇!”他的这句话让笔者萌生了写一本调试之书的念头,目的是把笔者喜欢的调试方法分享给更多人。
2003年5月,笔者加入英特尔,办公地点在上海浦东外高桥保税区,离市区比较远,每天乘坐班车上下班,几乎从不加班,这给了笔者比较充足的时间来研究调试,并把研究的结果写成书稿。
用了大约5年时间,推翻重来几次,书稿终于完工了,因为篇幅过大,删除了近200页后提交给了博文视点公司,书名就定为《软件调试》。2008年5月,100多万字的《软件调试》问世了,一共1006页。一位欧洲同行发现了后,发帖说:“中国出了一本1000页的调试之书。”
2018年11月,《软件调试(第2版):卷1》由人民邮电出版社出版,与第1版刚好间隔了10年。
第2版的卷1交稿后,笔者便开始规划卷2。卷2的主题是Windows平台调试。加入英特尔后,虽然笔者也时常做一些Linux平台方面的工作,但是Windows平台是“主场”,是笔者最熟悉的平台,笔者的编程之路从Windows平台开始,毕业之后的最初十年里也主要在Windows平台上工作。因此,当笔者规划卷2时,想写的内容很多。但考虑到篇幅限制,笔者不得不反复筛选。筛选的最重要原则是“实用性”,特别是如下两个指标。
对读者理论知识的帮助有多大?
对读者实战能力的帮助有多大?
经过多次调整,最后确定的卷2由五篇组成,共30章。
第一篇(第1~4章)介绍Windows平台的发展历史,进程和线程,架构和系统部件,以及启动过程。目的是把我对Windows平台的总体认识分享给读者,让读者对Windows平台建立起一个总的认识,也就是大局观。
第二篇(第5~8章)选择Windows系统中非常有特色的几个方面进行深入剖析,分别是特殊的调用过程,包括异步过程调用、延迟过程调用、本地过程调用和远程过程调用,用于解决应用程序兼容问题的垫片机制,曾被给予厚望但实际让人失望的.NET技术,以及具有时代特征的Linux子系统。
第三篇(第9~19章)介绍操作系统的调试支持。我一直喜爱Windows系统的一个主要原因就是它的调试设施特别丰富。该篇的目标就是从不同角度呈现Windows平台上的各种调试设施。先从用户空间讲起,介绍支持应用程序调试的调试模型和调试API以及用户态调试过程,异常处理机制以及处置未处理异常的方法和过程。然后过渡到内核空间,介绍硬错误和蓝屏。接着介绍全局性的错误报告、日志、事件追踪和旨在记录硬件错误的Windows硬件错误架构。最后介绍非常强大而且有特色的内核调试引擎和验证机制。
第四篇(第20~25章)介绍Windows平台上的开发工具的调试支持,首先介绍编译器的编译期检查和运行期检查,然后深度解析栈的结构和函数调用的细节,最后解析堆的结构和调试支持、异常处理代码的编译过程以及调试符号。
第五篇(第26~30章)介绍调试器。调试器无疑是征服软件世界最有力的武器。该篇首先概览调试器的发展历史和工作模型,然后分别介绍Windows平台上的常用调试器,包括老牌的集成在Visual Studio环境中的VsDebug、新兴的Visual Studio(VS)Code调试扩展,以及著名的WinDBG。
相对于读者熟悉的第1版,第2版的变化主要如下。
新增了第一篇和第二篇,目的是让读者不仅要熟悉调试设施,还要对“战斗”的环境和调试目标有比较深的认识。
在第五篇中新增了开发阶段常用的VsDebug调试器,详细介绍了有一定难度的功能,比如硬件断点、追踪点、多线程调试等。
在第五篇中新增了一章介绍近几年流行的Visual Studio Code开发环境,特别介绍了“调试扩展”的结构和工作原理。
新增了“老雷评点”版块,有的是评论技术背景,有的是介绍写作过程,有的是分享感悟。
为了便于携带和阅读,装订为上、下两册,上册包含前三篇(即前19章),下册包含后两篇(即后11章)。
阅读本书的最好方法是根据书中的提示,调试各种代码,多动手,多实践。本书的配套网站(http://advdbg.org)上有本书示例程序的源代码和编译好的二进制文件。
在过去的20多年中,计算机硬件和软件的变化可谓翻天覆地。从硬件的角度看,变化最大的有两个指标——空间和速度。以笔者1994年的第一台PC和写本序言使用的笔记本电脑为例,内存空间从4MB变为8GB,外存空间从210MB变为1.24TB(1TB机械硬盘加240GB固态硬盘),CPU的个数从1变为8(4核加超线程),主频从40MHz变为1.8GHz。
软件方面变化也很大。首先,体量变大,1994年用1张1.44MB的软盘就可以做一张DOS操作系统的工作盘,里面还可以放上很多常用的工具。2020年,随随便便的一个软件就有几十兆字节甚至上百兆字节的安装程序。
软件体量的迅猛增长带来的结果是软件的复杂度飞升。复杂度飞升导致的结果是软件的瑕疵增多,测试团队难以发现所有的Bug,于是越来越多的软件带着Bug发布。产品期瑕疵的增多催生了一个新的角色,那就是调试工程师,他们的主要工作便是调试客户报告的问题。今天,很多公司都有专门的调试团队,虽然名字不尽相同,但是大同小异。相对于开发阶段的调试,产品期调试的难度显然更大,对调试者的技术水平要求也更高。
随着专业调试工程师的出现和增多,软件调试技术正在受到越来越多的重视,正在由隐学转变为显学。我相信,很快会有大学开设专门的“软件调试”课程。
展望软件的未来,旧的技术将进一步融合,新的技术继续涌现,软件的规模和复杂度持续增大,对软件调试技术的需求也越来越多。
衷心祝愿本书的读者能使用调试之剑在软件世界中自由驰骋,游刃有余。
张银奎
于上海格蠹轩
2020年6月21日
本书由异步社区出品。异步社区(https://www.epubit.com/)为您提供相关资源和后续服务。
作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。
当您发现错误时,请登录异步社区,按书名搜索,进入本书页面,单击“提交勘误”,输入勘误信息,单击“提交”按钮即可,如下图所示。本书的作者和编辑会对您提交的勘误进行审核,确认并接受后,您将获赠异步社区的100积分(积分可用于在异步社区兑换优惠券、样书或奖品)。
我们的联系邮箱是contact@epubit.com.cn。
如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。
如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们;有意出版图书的作者也可以到异步社区在线投稿(直接访问www.epubit.com/ selfpublish/submission即可)。
如果您所在学校、培训机构或企业想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。
如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接发邮件给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。
“异步社区”是人民邮电出版社旗下IT专业图书社区,致力于出版精品IT图书和相关学习产品,为作译者提供优质出版服务。异步社区创办于2015年8月,提供大量精品IT图书和电子书,以及高品质技术文章和视频课程。更多详情请访问异步社区官网https://www.epubit.com。
“异步图书”是由异步社区编辑团队策划出版的精品IT专业图书的品牌,依托于人民邮电出版社近30年的计算机图书出版积累和专业编辑团队,相关图书在封面上印有异步图书的LOGO。异步图书的出版领域包括软件开发、大数据、AI、测试、前端、网络技术等。
异步社区
微信服务号
要想成为一名软件调试高手,至少要从两个方面下工夫。一方面是调试工具,另一方面是被调试对象。这与医生做手术很类似,不仅要能熟练使用医疗工具,还要对人体结构了如指掌。
本书第1版侧重于调试工具,大部分篇幅用于介绍不同类别的调试设施。但只熟悉调试设施是不够的,为此,本书第2版特意新增章节来介绍被调试对象。进一步说,当我们调试应用程序时,被调试对象一般就是某个进程;当我们调试内核空间和系统级别的问题时,被调试对象扩大为整个软件世界,其核心便是操作系统。
为了避免与已有的介绍Windows系统的书籍重复,本书力求从不同的视角以不同的方法来让大家快速地理解Windows操作系统的宏观架构和关键基因。为此我们分两篇来介绍,本篇侧重介绍Windows系统的总体结构和具有普遍性的全局特征,目的是建立理解Windows系统的大局观。
本篇内容分为4章。第1章简要介绍Windows操作系统的发展历史,以帮助读者认识它的演变过程。第2章介绍Windows世界里组织空间和“生命”的方法。现实世界中,城市、山林、公园、住房等为人类活动提供了空间,有了空间,“生命”才有活动的场所,软件世界里如何组织空间也是一个非常关键的问题。了解了Windows系统组织空间的基本方法后,第3章介绍生活在这个空间里的不同角色,包括Windows系统的内核、执行体、关键进程和关键模块。在第2章和第3章分别介绍空间和生活在空间中的角色后,第4章让各个角色活动起来,看它们如何在用户按下电源按钮后,从黑暗中崛起,粉墨登场,以电的速度奔跑,在纵横交错的复杂逻辑中,闪转腾挪,忙而不乱,在几秒内给用户呈现出一个多姿多彩的软件世界。
1975年4月4日,一对小学时就是同学的好朋友合伙创建了一家公司,他们一个20岁,名叫比尔·盖茨,另一个22岁,名叫保罗·艾伦,他们创建的公司名叫微软(Microsoft,最初写为Micro-Soft)。1986年,微软公司上市,市值5.2亿美元,比尔·盖茨持有其中45%的股份,即2.34亿美元。从此,伴随着PC产业的蓬勃发展,微软公司不断壮大,成为PC时代的翘楚,至今仍是软件领域的顶尖企业。
1978年在英特尔公司的历史中是很不平凡的一年。这一年,它满10岁了,员工数首次超过10000。这一年,它卖掉了竞争激烈的电子表(digital watch)业务,推出了具有跨时代意义的8086芯片。在宣传8086的海报上,画着一轮初升的太阳,标题写着“8086的时代到了”。
与其说8086的时代到了,不如说是PC(个人计算机)的时代到了。小巧的8086芯片给计算机系统提供了新的大脑,为计算机个人化奠定了关键的硬件基础。很多公司开始研发基于8086的计算机产品。聪明的比尔·盖茨也看到了这个机会,但与其他公司主要研发硬件不同,盖茨的思路是为个人计算机硬件提供操作系统软件。从头开发操作系统太花时间了,1981年7月,微软从一家名为西雅图计算机产品(Seattle Computer Products)的公司购买了一款名为86-DOS的系统软件,将其改名为MS-DOS,然后授权给急需系统软件的IBM公司。同年8月12日,IBM公司推出了它的第一代个人电脑产品,型号为IBM 5150。5150的CPU是8086的裁剪版本(数据总线从16位减少到8位),名为8088。5150的系统软件有3种,即DOS、CP/M-86和UCSD p-System,后两种主要是为了兼容当时的应用软件,方便把旧的软件移植到DOS。长话短说,年轻的微软公司利用收购的DOS系统软件成功地傍上了IBM PC这条“大腿”,从此便顺着PC产业的大潮扬帆万里了。
DOS是字符界面的,人机交互的主要形式是命令行,这对于普通用户来说是很不友好的。因此,微软一方面与IBM联合开发代号为CP/DOS的图形界面操作系统,另一方面自己也在开发名为Windows的图形界面操作系统。
1985年11月,微软发布了第一个版本的Windows软件,此时这款软件还不是完整的操作系统,是基于DOS的,用户需要先启动DOS,然后在DOS上启动Windows系统。准确地说,当时的Windows系统只是DOS系统上的一个图形接口外壳(GUI Shell)。
Windows 1.0的功能很有限,其核心功能是图形界面的文件管理器和程序管理器,加上一些附件程序,包括画笔和时钟等。根据史蒂夫·鲍尔默(Steve Ballmer)当年为Windows 1.0做广告的视频,Windows 1.0的售价为99美元。
1987年,微软发布改进了的Windows 2.0版本,但因为内存方面的局限,窗口操作很不灵活,当年比尔·盖茨做演示时,移动窗口如同搬重物一样。
1990年,进一步改进的Windows 3.0版本面世,最大的变化是引入了对CPU保护模式的支持,应用程序可以运行在保护模式下,每个程序有自己的虚拟内存空间,可用的内存数量大大增加。摆脱了内存方面的束缚后,窗口操作变得非常流畅;同时也让更多的应用程序可以同时运行,从此Windows系统走上了成功之路。
1992年4月6日,微软发布Windows 3.1版本。该版本抛弃了实模式支持,要求硬件必须支持保护模式,也就是不再支持8086或者8088等老的CPU,要求至少是286或者386。除了丢掉实模式的包袱,Windows 3.1还引入了一些新的功能,包括TrueType字体、文件拖曳、自带屏幕保护程序、多媒体支持和注册表等。销售量方面,Windows 3.1大卖,仅在最初的6个月内就销售了1600万份复制件[1]。经过约十年的努力,Windows系统终于赢得了用户的肯定,得到了市场的认可。
老雷评点
老雷读大学时,刚好是Windows 3.1大行其道的时候。当年使用3英寸软盘来安装Windows 3.1,但先要安装DOS系统。笔者的很多编程实践也是在Windows 3.1上开始的。
1995年8月15日,微软宣布新一代的Windows产品——Windows 95。Windows 95把DOS和Windows融合在一起,让用户可以直接在硬件上安装系统,不再像以前那样要先购买和安装DOS,再安装Windows。技术方面,Windows 95从原来的16位协作式多任务调度过渡到32位抢先式调度,系统更加稳定。
Windows 95推出后,Windows系统变得更加流行,在此后的十几年中,Windows系统不断发展,推出了很多个版本,如Windows 98、Windows Me、Windows 2000、Windows XP、Windows Server 2003、Windows Vista、Windows 7、Windows 8等。
Windows系统流行后,独立的DOS产品逐渐退出了市场。不过,直到今天,在每个Windows系统里,还都保存着DOS的痕迹。例如,在所有Windows系统的可执行文件(称为PE格式)的开头,一般都有一个DOS头(图1-1)。DOS头总是以MZ(十六进制4D 5A)开头,代表一位DOS开发者的名字Mark Zbikowski。DOS头后面一般还有一小段16位的DOS程序,当用户在DOS下执行这个文件时,这一小段程序会提醒用户该程序不可以运行在DOS模式下。
图1-1 在PE文件中保留的DOS背影
根据各个Windows系统版本所基于的内核,可以把它们分为三大类。
第一类,16位的Windows,包括Windows 1.0(1985年发布),Windows 2.0(1987年发布,能够访问1MB内存),Windows 3.0(1990年5月22日发布,支持16位的保护模式,可以访问16MB内存),Windows 3.1(1992年4月发布,要求CPU必须支持保护模式),以及Windows 3.2(中文版本)等。16位的Windows系统源于DOS操作系统,目前该版本已经很少有人使用。
第二类,Windows 9x,包括Windows 95(1995年8月发布)、Windows 98(1998年6月发布)和Windows ME(2000年9月发布)。Windows 9x是从16位的Windows系统发展而来的,这条产品线在推出Windows Me后便不再开发了。
第三类,Windows NT(New Technology)系列,包括Windows NT 3.1(1993年7月发布)、Windows NT 4.0(1996年7月发布)、Windows 2000(1999年12月发布)、Windows XP(2001年10月发布)、Windows Server 2003、Windows Vista、Windows Server 2008、Windows 7、Windows Server 2012、Windows 8、Windows 10等,这些版本都源于Windows NT内核。
在Windows 9x的几个版本中,Windows 95是开山之作,Windows 98是巅峰,Windows Me是一个败笔。1996年到2000年,正是个人电脑走向巅峰的时候,很多城市出现了电脑城。以上海为例,1997年,上海最大的PC集散地是福州路上的科技图书馆,简称“科图”。随着PC的火热,“科图”里的PC店铺越来越多,挤满了三四层楼的每个角落。人流密集,每到周末,更是摩肩接踵,熙来攘往。一两年后,“科图”实在不堪重负,在2000年左右,位于徐家汇的百脑汇和太平洋电脑商城接过了重任,一直红火到2010年左右,随着PC产业的衰退逐渐萧条。笔者记得非常清楚,在1996年到2000年期间,电脑城里的大多数PC安装的是Windows 95/98。
回顾PC的历史,16位Windows和Windows 9x都当过主角。讲到这里,笔者不禁想到一个对Windows 9x做过重要贡献的人——保罗·马瑞兹(Paul Maritz)。他出生在津巴布韦共和国,于1981年加入英特尔公司,为x86 CPU编写软件,后于1986年加入微软。在研发Windows 98期间,马瑞兹是领导Windows团队的副总裁。1997年7月23日,他以平台战略与开发部门副总裁的身份宣布开发代号为MEMPHIS的下一代Windows,正式名称是Windows 98[2]。
前面简略介绍了16位Windows和Windows 9x,下面我们把目光转移到Windows NT上。
与针对PC市场的16位Windows和Windows 9x不同,Windows NT在设计之初就是针对硬件配置较高的办公环境和服务器市场的。因此,Windows 9x和Windows NT这两条产品线虽然相互借鉴,在API一级也保持着很好的兼容性,但是由于其内核的根本差异,二者在很多方面还是不同的,系统的很多关键设施是不一样的。
从开发团队的角度讲,NT团队的核心成员大多来自小型机时代的旗舰公司DEC(Digital Equipment Corporation),其核心人物便是有着“NT之父”之称的大卫·卡特勒(Dave Cutler或者David Cutler)。
卡特勒出生于1942年,1965年从Olivet College毕业后加入杜邦公司。一次计算机方面的培训,让原本不是学习软件的卡特勒爱上了编程。1971年,自学成才的卡特勒加入了DEC公司。加入DEC后,卡特勒先为著名的PDP-11小型机开发了实时操作系统软件RSX-11M,表现出了其在软件方面的天赋。4年后,已经有一定声望的卡特勒被任命为项目带头人,为DEC的32位处理器VAX(Virtual Address Extension)开发操作系统。VAX是计算机历史上最早使用虚拟内存技术的计算机系统,它的操作系统名字就叫作虚拟内存系统(Virtual Memory System,VMS)。VAX与VMS一同推向市场后,非常畅销。VMS的成功让卡特勒成了操作系统领域的著名专家。1988年10月,卡特勒在DEC领导的Prism项目被取消,他非常气愤,想辞职离开。这时急需操作系统人才的微软公司听到了这个消息,邀请卡特勒加入。双方一拍即合,当月的某个周五谈好,下个周一卡特勒便到微软上班了。卡特勒是在DEC公司工作了16年后加入微软的,这时他46岁,在某些程序员看来,这似乎应该是转向管理岗位或者退居二线的年龄,但是对于卡特勒来说,这恰恰是他人生最辉煌篇章的开始。
卡特勒加入微软后,他在DEC时的多位下属和同事也都跳槽到微软,与他一起开发NT。
NT的最初计划是18个月,实际上用了将近5年。直到1993年7月,第一个版本的Windows NT对外发布,版本号为3.1,目的是与当时流行的16位版本Windows 3.1保持一致,兼容相同的Windows API,让原本运行在16位Windows系统上的应用程序也可以运行在Windows NT上。
回望历史,NT内核绝对称得上是计算机历史上的一座丰碑,一座难以重新登顶的高峰。1988年11月,NT团队初建时只有几个人,其中6名来自DEC,1名是微软的老员工。NT 3.1发布时,团队的规模介于150~200人[1-2]。
在伟大的NT内核背后,有一个伟大的团队,其中最重要的核心人物无疑是卡特勒。他是一位代码圣斗士(Code Warrier),亲手编写了NT内核中的大量核心代码,包括任务管理、调度、中断异常处理、内核调试引擎等。他执着地坚守在技术第一线,从精神如虎一直到满头白发。他也是一位杰出的技术领袖,与团队成员一起编写代码,修正瑕疵,身先士卒,绝不指手画脚,光说不做。在成功地领导了前3个版本(3.1、3.5和4.0)的NT开发后,他卸去管理职务,甘心做一个个人贡献者(Individual Contributor)。2008年,卡特勒获得美国的“国家技术创新奖”[3]。2016年2月,美国计算机历史博物馆授予卡特勒院士荣誉,表彰他在操作系统领域锲而不舍耕耘50年。
除了卡特勒,还有很多人为NT内核做出了重大贡献。微软内部有一种“12核心”(Core 12)的说法[4],指的就是NT内核开发早期的12位核心成员(表1-1)。
表1-1 NT内核开发早期的12位核心成员
人 名 |
部 件 |
在微软工作的时间 |
备 注 |
---|---|---|---|
David Cutler |
KE(内核) |
从1988年至今 |
DEC |
Lou Perazzoli |
MM(内存管理) |
1988—2000年 |
DEC |
Mark Lucovsky |
Win32 |
1988年11月—2004年11月 |
DEC |
Steve Wood |
Win32, OB(对象管理) |
1983—? |
— |
Darryl Havens |
I/O(输入/输出) |
1988年11月—2015年3月 |
DEC |
Gary Kimura |
FS(文件系统) |
1988年11月—? |
DEC |
Tom Miller |
FS(文件系统) |
— |
DEC |
Jim Kelly |
SE(安全) |
1988—? |
DEC |
Chuck Lenzmeier |
Net(网络) |
— |
— |
John Balciunas |
Bizdev |
— |
— |
Rob Short |
Hardware(硬件) |
— |
DEC |
Ted Kummert |
Hardware(硬件) |
— |
— |
除了表1-1中列出的核心成员,还有几位值得特别介绍。一位是约翰妮·卡伦(Johanne Caron),她是NT团队里为数较少的女程序员,她负责把16位Windows系统的程序管理器和注册表功能移植到NT中。移植旧的代码是很费心费力的工作,卡伦扛起重担,努力修正各种瑕疵。为了排解压力,她爱上了空手道[5]。
另一位值得提及的是海伦·卡斯特(Helen Custers),她是NT团队的技术编辑,是著名的《NT工作手册》(NT Workbook)的编辑者。NT发布后,她出版了一本关于NT内核技术的书,名为《Windows NT技术内幕》(Inside Windows NT),这本书便是后来畅销不衰的Windows Internals的第1版。
根据使用模式和针对的用户,可以把Windows系统分为主要面向个人用户的终端版本以及面向企业用户的工作站和服务器版本(忽略市场份额一直很小的手机版本),前者用于笔记本电脑或台式机,满足办公室职员和家庭用户的需要,后者用于工作站和服务器系统。图1-2显示了1992年到2008年Windows系统的终端和服务器版本的发展历程。
图1-2 Windows产品线示意图(1992—2008)
图 1-2 中上面的一条线代表客户端版本,下面的一条线代表工作站和服务器版本。其中实线箭头代表直接的版本演进,箭头指向的版本是基于前一版本的代码库(Code Base)而开发的。虚线箭头代表借鉴和影响关系。从图中可以看出,在Windows 2000之前,源于DOS的Windows 3.1和Windows 9x针对终端市场,Windows NT针对工作站和服务器市场。
Windows 2000第一次将桌面版本和服务器版本统一起来,尽管Windows 2000也有服务器版本和个人用户版本之分,但是二者的操作系统内核是相同的。不过,像Windows 2000那样用一个产品线来覆盖台式机和服务器这两大市场的模式很快就遇到了困难,因为服务器版本强调安全性,台式机版本强调易用性,而安全和易用很多时候是难以两全的。因此在Windows 2000之后,又分成了Windows XP和Windows Server两条产品线,Windows XP针对台式机,而Windows Server(如Windows Server 2003)针对服务器市场。Windows XP除了有家庭版、专业版这些针对普通台式机和笔记本电脑的版本,还有用于Tablet PC的版本和针对嵌入式系统的版本(称为Windows XP Embedded)。
从NT内核的传播角度来看,Windows 2000是让NT内核走向普通用户的开始。在此之前,NT的主要使用场景是专业的工作站系统,面向的是企业用户。从Windows 2000开始,更多的软件开发人员和普通用户开始使用NT。笔者也是从Windows 2000开始接触NT内核的,从使用多年的16位Windows和Windows 9x系统转向NT系统。
1996年7月31日,Windows NT 4.0发布,此时NT团队的规模已经很大了,开发工程师有400人(此处根据卡特勒的回忆和表1-2,总人数为800)。为了可以专心做自己喜爱的技术工作,卡特勒卸去了管理职务,把管理角色交给了陆·佩拉佐利(Lou Perazzoli)。佩拉佐利和卡特勒是在DEC的老同事。与卡特勒的倔强不同,佩拉佐利的性格很温和,如果把卡特勒比作咸涩的盐,那么佩拉佐利便是可以缓解咸味的糖,更适合管理职务。在开发最初版本的NT时,佩拉佐利负责设计和实现内存管理器(Memory Manager,MM)。承担起NT团队的管理角色后,佩拉佐利领导团队开发NT 5.0,也就是后来的Windows 2000。
在1997年,《Windows NT技术内幕》第2版出版时,佩拉佐利正在担任NT团队的领导角色,他为这本书写了序言。在序言中,他回忆了NT的发展历程,特别提到了1988年11月,团队组建时只有6个人,也就是我们在表1-1中列出的前6位——David Cutler、Lou Perazzoli、Mark Lucovsky、Steve Wood、Darryl Havens和Gary Kimura。在序言的末尾,佩拉佐利签名上面所署的职务是“Windows NT核心操作系统主任”(Director, Windows NT Core OS)。
佩拉佐利领导团队顺利地完成了Windows 2000的开发,造就了NT历史上一个“前无古人后无来者”的独特版本。Windows 2000是成功的,它不仅继续保持了NT内核在服务器和工作站领域的领导地位,还让NT内核进入PC领域,证明了这个强大的内核具有足够的灵活性来满足不同类型用户的需要。
Windows 2000的成功,也加速了Windows 9x系列的终结,让积蓄已久的NT团队和微软老牌Windows团队之间的矛盾到了收场的时候。1999年,微软内部调整组织结构,把负责Windows 9x系列的消费者部门(Consumer Division)和负责NT的企业商务部门(Business & Enterprise Division)合并,成立了核心操作系统部门(Core OS Division,COSD)。可能与这次重组有关,佩拉佐利和原本领导Windows 9x部门的布拉德·西尔弗伯格(Brad Silverberg)都离开了微软。吉米·奥尔钦(Jim Allchin)接管合并后的新部门,担任主帅,一直到Windows Vista发布(参见下文关于Vista的介绍)。
在2000年8月举行的“第4届UNIX用户群Windows系统研讨会”(4th Usenix Windows Systems Symposium)上,马克·拉科夫斯基(Mark Lucovsky)做了一个题为“Windows——软件工程领域的一次长征”的报告[2]。在这个报告中,马克从软件工程的角度比较详细地介绍了Windows NT项目,特别是,他把最初的Windows NT版本和当时最新的Windows 2000版本做了详细比较,表1-2是其中的一个部分。
表1-2 最初的Windows NT版本与Windows 2000版本的比较[2]
版 本 |
开发团队人数 |
测试团队人数 |
平均每人每年瑕疵数 |
修正每个瑕疵的时间/min |
每天的瑕疵数量 |
修正每个瑕疵的总时间/min |
---|---|---|---|---|---|---|
Windows NT 3.1 |
200 |
140 |
2 |
20 |
1 |
20 |
Windows NT 3.5 |
300 |
230 |
2 |
25 |
1.6 |
41 |
Windows NT 3.51 |
450 |
325 |
2 |
30 |
2.5 |
72 |
Windows NT 4.0 |
800 |
700 |
3 |
35 |
6.6 |
228 |
Windows 2000 |
1400 |
1700 |
4 |
40 |
15.3 |
612 |
在为《Windows NT技术内幕》第5版所撰写的“历史回眸”中,卡特勒特别提到了Windows 2000,称这个版本用时3年半,是“测试和优化最好的版本”。
在整个Windows操作系统的历史上,Windows 2000具有很独特的地位,它展示了NT内核的强大实力,证明了这个内核不仅可以用在工作站和服务器等专业领域,还可以用在个人电脑上,让微软彻底抛弃了陈旧的Windows 9x产品线,从此把精力集中在NT内核上。
2001 年 8 月,微软发布同时面向消费者市场和商务市场的 Windows XP 操作系统。Windows XP发布后,彻底取代了Windows 9x,横扫整个PC市场,从此开始的大约十年里,全世界难以计数的PC上大多运行的是这个操作系统。
Windows XP流行后,很少有人使用独立的Windows 9x系统了,但在所有Windows系统中,都还保持着Windows 9x的一些代码,最主要的就是Windows子系统,即用户态的USER32、GDI32和内核空间的Win32K。NT团队的人是瞧不起老的Windows代码的,在给Windows 2000取名时,把NT从名字中拿掉,NT团队的一些人都有顾虑,怕单纯地叫Windows会影响到产品的名声。
今天,如果你想看一下老的Windows系统的代码到底怎么样,其实很容易,只要看一下系统中的Win32K函数就会有感受了,清单1-1列出了经典的挖地雷小游戏调用ShowWindow API时的执行过程。栈帧#5~#0就是进入内核空间后执行Win32K中函数的过程。Win32K的很多函数以x开头,而且x的个数不同,图中显示的几个函数都是以3个x开头,让人看着有些别扭。
清单1-1 NT系统保留的传统Windows函数
# ChildEBP RetAddr
00 8eaf1b40 904ddfb3 win32k!ParentNeedsPaint+0x1f
01 8eaf1b4c 904efeca win32k!xxxDoSyncPaint+0xd
02 8eaf1ba8 904f016c win32k!xxxEndDeferWindowPosEx+0x28d
03 8eaf1bc8 904f4109 win32k!xxxSetWindowPos+0xf6
04 8eaf1c04 904f427f win32k!xxxShowWindow+0x25a
05 8eaf1c24 8286b87a win32k!NtUserShowWindow+0x8b
06 8eaf1c24 773b7094 nt!KiFastCallEntry+0x12a
07 0006fe78 75baf2b5 ntdll!KiFastSystemCallRet
08 0006fe7c 0100235f USER32!NtUserShowWindow+0xc
09 0006fee4 01003f95 winmine!WinMain+0x16f
0a 0006ff88 770fed6c winmine!WinMainCRTStartup+0x174
0b 0006ff94 773d377b kernel32!BaseThreadInitThunk+0xe
0c 0006ffd4 773d374e ntdll!__RtlUserThreadStart+0x70
0d 0006ffec 00000000 ntdll!_RtlUserThreadStart+0x1b
2003年4月24日,面向服务器市场的Windows Server 2003发布。以Windows 2000的服务版本为基础,Server 2003吸收并整合了Windows XP的先进功能,是Windows服务器产品线中的一个经典版本。
2002年1月,比尔·盖茨致信微软所有员工,倡导“可信计算”(trustworthy computing),要求在整个公司推行新的安全开发流程(Security Development Lifecycle,SDL)。Server 2003是微软全面重视安全和使用SDL流程后的第一个NT版本。
Windows Server 2003的团队规模也是空前的,在2003年4月23日微软官方发布的关于发布Server 2003的新闻稿中,列出了如下一连串数据:超过5000名开发者工作了3年多,超过2500名测试者运行各种评估和测试,总计将近1万人参与了这个项目。这篇新闻稿的标题叫“从数字看Windows Server 2003:微软历史上最大的产品之一发布”[6]。
从功能角度看,Windows Server 2003包含了IIS 6.0,改进了Windows 2000服务版本引入的活动目录(Active Directory)功能,引入了基于硬件的“看门狗”(watchdog)功能。
从开发工具角度看,Windows Server 2003版本的SDK(面向应用开发者)和DDK(面向驱动程序开发者)也达到了非常完美的状态,笔者至今仍经常使用这两个软件包中的工具和资源。
Windows XP和Windows Server 2003是合并后的COSD团队的第一代产品,一个面向桌面用户,一个面向服务器用户,一个是终端,一个是服务器,让Windows徽标遍及全世界,某种程度上实现了十多年前所制定的“Windows无处不在”(Windows Everywhere)的愿景。
Windows XP和Windows Server 2003也开创了交替开发桌面版本和服务器版本的模式,从此,COSD团队基本上按这种模式运作:发布一个桌面版本后,再基于它开发一个服务器版本,然后再开发下一个桌面版本,如图1-2所示。这与英特尔的Tick-Tock有些类似,好像两条腿,左脚前进一步,右脚前进一步,交替动作。
在开发Windows XP和Windows Server 2003期间,马克·拉科夫斯基在团队中担任架构师角色,把握关键的技术方向,对完善和优化NT内核做出了很多贡献,举例来说,他重构了用户态调试子系统,弥补了原来的重大不足,后文会详细讨论。
2004年11月,马克告别了工作了16年的微软,投奔谷歌。时任微软CEO的鲍尔默得知这个消息后,非常愤怒。
2001年5月,Windows XP将要发布的时候,微软公布了代号为Blackcomb的新一代Windows系统,并且定义了一个过渡性的版本,代号为Longhorn,因为XP的代号为Whistler。这几个代号都是哥伦比亚著名滑雪胜地的地名,Whistler和Blackcomb是两座山峰的名字,Longhorn是二者之间的山谷的名字,代表着从一个山峰到另一个山峰必须经过的过渡地带。
处于巅峰状态的Windows团队,准确说是一些领导者,为新一代Windows系统绘制了非常宏伟的蓝图,准备打造理想中的软件平台,这些宏伟的蓝图包括:
(1)基于.NET的新一代开发接口WinFX,为了支持这个目标,据说曾经尝试在内核空间中增加.NET支持;
(2)名为WinFS的新一代文件系统;
(3)代号为Avalon的新一代GUI编程技术。
为了实现这些宏伟计划,微软投入了大量的人员,使Windows开发团队的规模超过前所未有的6000人。但是开发进程并不顺利,进展缓慢,问题很多。按照微软最初的计划,应该在2003年左右发布Longhorn,但是实际进展远远慢于预期。2004年8月,团队做了一个重大的决定,重启开发过程(development reset),更换基础代码,所有新功能都要基于新的Sever 2003 SP1源代码重新整合。
领导这个庞大团队的主要是上文提到过的吉米·奥尔钦,他1990年加入微软,曾在1991~1996年期间领导过代号为“Cairo”的操作系统项目,但是用了5年时间都没能发布这个项目。于是,有些人不由得把Longhorn项目与Cairo项目联系起来,把Longhorn称为新的Cairo,或者叫Cairo.NET。奥尔钦也有程序员和工程师背景,但是从Cairo和Longhorn项目都可以看出,他对技术和软件的理解深度显然与卡特勒和佩拉佐利不可同日而语。
2005年年中,微软正式把Longhorn定名为Windows Vista,随后开始发布Beta版本,供微软外部的志愿者们进行测试。
2006年11月,Windows Vista终于发布,历时5年半,比最初版本的NT还长,成为Windows历史上开发时间最久的一个版本。
尽管用时最久,也没能完全实现最初的目标,比如抛弃了WinFS功能,其他一些功能也做了裁剪。从Vista实际发布的功能来看,比较显著的有以下几项。
(1)重新规划和设计的GPU软件栈和Windows显示驱动程序模型(Windows Display Driver Model,WDDM)。WDDM实现了针对GPU的很多高级功能,比如显存虚拟化、GPU任务的并行和抢先式调度、多GPU支持等。直到今天,这些功能仍是Windows系统领先于Linux系统的地方。笔者认为,WDDM可以算是Vista对NT系统所做的最大贡献。
(2)重构的Windows驱动程序模型——Windows Driver Foundation(WDF)支持使用C++编写用户态的驱动程序,使用面向对象技术对原来的WDDM驱动模型做了一些封装,本意是简化驱动程序开发,但是增加了一层封装后,也增加了模糊性和开发者要学习的内容。另一个副作用是,原来DDK中的一些经典例子被删除了,可能是因为难以转换为WDF风格。
(3)安全方面,引入了用户账号控制(User Account Control,UAC),限定普通应用程序只有标准用户权限,提升特权时需要用户同意。因为牵涉用户交互和程序界面,所以UAC对Windows系统的影响是广泛和深远的。
很多用户对Windows Vista的反馈并不好,觉得它庞大而且缓慢,最典型的例子就是开始菜单反应迟钝,很多操作会导致卡顿和等待,屏幕上出现Vista引入的圆形光标,反复旋转,不知转到何时,让用户看得心烦。用卡特勒的话来说,“它可能是我们已经发布的所有Windows系统中接受度最差的一个版本[1]”。
Windows Vista的庞大也直接体现在它的安装盘大小和安装后所占的磁盘空间上,都比Windows XP增大了数倍。
Windows Vista发布后,奥尔钦即宣布退休,退休后,他投身到他喜爱的音乐领域。他擅长弹奏吉他,并且能创作和演唱,录制了很多蓝调(布鲁斯)风格的音乐,有一些单曲一度占据流行排行榜的头名。
领导Vista开发的还有一位著名的人物,名叫布莱恩·瓦伦丁(Brian Valentine)。1987年,28岁的瓦伦丁从英特尔辞职,到微软工作,投奔在那里工作的前英特尔同事保罗·马瑞兹。瓦伦丁具有管理天赋,擅长鼓舞士气,曾经在Windows 2000、Windows XP和Windows Server 2003开发期间起过重要的领导作用。Server 2003发布后,奥尔钦邀请他作为自己的副手领导Vista的开发,在2005年左右,他又被提拔,与奥尔钦一起作为“共同总裁”(co-president)领导Windows团队。在Vista正式发布前,瓦伦丁离开微软,到亚马逊工作。
在开发Vista期间,卡特勒仍然以个人贡献者身份工作,与其他几个人一起负责64位Windows操作系统的相关的工作。2016年,当他接受计算机博物馆的采访时,回忆到Vista他说Vista是一个巨大的烫手山芋(dilemma),让人感觉两面为难,“它看起来不太好,与Windows XP不同,与我们之前发布的任何版本都不同,它是不同的。”对于这样一个奇葩,人们很纠结“是该把它发布出去,还是应该回炉重来?”
大约在2006年8月,Vista发布前夕,卡特勒也离开了Windows团队,到微软的其他部门工作。
从1988年11月NT团队组建,到2006年11月Vista发布,整整18年。Vista发布后,NT初始团队的很多成员要么离开了微软,要么离开了Windows部门。NT从无到有,从出生到鼎盛,从鼎盛转向衰落,Vista一战,算得上一个分水岭。从此,原本满身肌肉的NT身上出现了很多赘肉,变得臃肿,老态尽显。任何事物都会从年轻转向衰老,趋势不可逆转,NT也是如此。我们以Vista的发布为界,把NT的历史分为两个部分——前NT时代和后NT时代,Vista的发布是分界线,前NT时代结束,后NT时代开始。
Vista之战落幕后,奥尔钦退休,瓦伦丁离开,卡特勒换了部门,Windows团队大换血,两位来自Office团队的微软老将接替重任,共同掌管Windows团队,他们是史蒂文·辛诺夫斯基(Steven Sinofsky)和乔恩·德瓦恩(Jon DeVaan)。他们分别于1989年和1982年加入微软,都因为领导Office产品开发而建立功勋,并树立了非常良好的“准时发布”口碑。乔恩还曾与比尔·盖茨亲密合作,领导全公司范围内的“杰出工程(Engineering Excellence)”运动。
大约在2007年7月,微软宣布Windows 7作为下一代Windows产品的开发代号,计划3年内完成,重点是改进性能。
2008年8月,MSDN官网上出现名为E7的博客(blog),文章内容都是关于Windows 7项目的,E7是Engineering Windows 7的简称,E7博客的文章都来自Windows 7开发团队,很多文章就出自史蒂文之手。最初几篇文章末尾的署名都是“Steven and Jon”,代表Windows 7项目的两位共同领导者。在团队内部,乔恩负责内核部分,即调整后的COSD,史蒂文领导IE等用户态部件,称为UEX(代表用户体验)。
2008年10月,微软宣布Windows 7不仅是开发代号,也将是下一代Windows系统的正式名字。2009年7月22日,微软宣布Windows 7发布,7月13日编译的版本通过所有测试,成为RTM(Release To Manufacturing)版本,构建号码为7600.16385.090713-1255。
从2006年下半年开始,到2009年7月发布,Windows 7用时不到3年,不仅没有延期,还比事先公开宣布的2009年10月22日整整提前了3个月。这是整个Windows历史上从来没有过的,这样的“准时发布”为Windows 7团队在微软公司内外都树立了非常好的威望。
从架构角度看,Windows 7引入了所谓的MinWin结构,梳理模块之间的接口,减少耦合,努力减小整个系统的体量(footprint)。MinWin是从2003年开始的一个微软内部项目,简单说,就是裁剪得非常精悍的Windows系统,只有150个二进制文件,只需要25MB的磁盘空间,工作时,占用的物理内存(工作集)也只有40MB。
使用MinWin一方面是为了降低耦合,让不同模块(特别是内核和用户空间的模块)可以独立开发,提高团队效率;另一方面,MinWin也代表着要把Windows做小的理念,让每个团队成员都知道把系统做小的重要性,“如果不能做小,就要回家”(Go small or go home)。
Windows 7还引入了与MinWin有关的一项变化,即对Kernel32.DLL做了重构,引入Kernelbase.DLL,把原本实现在Kernel32.DLL中的逻辑移到Kernelbase中,Kernel32只保留接口,这样修改后,负责用户空间开发的团队只需要使用稳定版本的Kernel32.DLL,不需要频繁更新,负责内核空间的团队如果对底层做修改,一般只需要修改Kernelbase.DLL,不需要更新Kernel32.DLL,两个团队之间的相互牵制大大减少。
因为这一改动,在Windows 7或者更高版本的Windows系统中,Kernel32.DLL中的API入口大多只剩一条无条件跳转指令(jmp),比如ReadFile API:
0:011> u kernel32!readfile
KERNEL32!ReadFile:
00007ffb`f64d0cc0 ff25e26d0500 jmp qword ptr [KERNEL32!_imp_ReadFile
(00007ffb`f6527aa8)]
上面的_imp_ReadFile是导入表项,相当于函数指针,jmp指令跳转的目标是指针的内容,使用ln命令观察这个指针。
0:011> ln poi(KERNEL32!_imp_ReadFile)
(00007ffb`f5282ac0) KERNELBASE!ReadFile Exact matches:
KERNELBASE!ReadFile (void)
可以看到,_imp_ReadFile指向的目标就是kernelbase中的ReadFile函数。
从功能角度来看,Windows 7引入的全新功能不多,比较显著的要数虚拟磁盘支持,其核心设施是一个新的系统服务,名为vds,专门用来支持虚拟磁盘。
Windows 7所做的更多是对Vista引入的新功能进行改进和优化,把慢的加快,把不方便的改得方便,把瑕疵去掉,把棱角打平。举例来说,安装Windows 7时,Windows 7的安装程序就会在硬盘上建立一个特殊的分区,安装一个用于修复故障的简易Windows系统,称为Windows恢复环境(Windows Recovery Environment,WRE)。在系统盘上,可以看到一个名为Recovery的隐藏目录,里面放着包括WRE磁盘映像在内的一些文件(图1-3)。当正常的Windows系统无法启动时,Windows 7可以让用户从高级启动选项(按F8键)中选择“Repair Your Computer”,进入WRE。
图1-3 Windows 7把WRE预装在硬盘上
WRE功能强大,使用方便,笔者曾多次使用它拯救被IT部门的同事宣判“死刑”(需要重装)的Windows系统。其实,Windows Vista也有类似的功能,但需要用户使用安装光盘来启动,不但不方便,而且很多时候变得不可用,因为很多系统已经没有光盘驱动器。通过这个例子来看,Windows 7从用户角度出发,改进功能,虽然费力不多,但是产生的效果是非常显著的。因此,有人说,Windows 7其实没做什么事情,事情都是Vista做的,用一句俗话来说就是“牛打江山马享功”。某种程度来说,这句话也有一定道理,但是Windows 7的改进之功也是不可轻视的。一方面因为它实实在在地改进了用户体验,另一方面因为Windows XP确实有些老了,因此,Windows 7的市场接受度很好,发布后,大量用户将系统升级到了Windows 7。
Windows 7的成功,让后NT时代的第一代NT团队精神振奋,备受鼓舞。2009年7月,史蒂文被提拔为Windows部门的总裁,独立领导名为Windows 8的下一代Windows开发,不再是和乔恩一起担任共同总裁。
与开发Windows 7时的E7博客类似,在开发Windows 8期间,史蒂文创建了一个名为b8(Building Windows 8)的博客,作为Windows团队的窗口与外部沟通。与以前一样,史蒂文亲自写了很多文章发表在b8博客上。看起来,Windows 8团队很喜欢“构建”(build)这个词,可能是因为这个词代表着构建理想中的Windows系统,也代表着构建心中的梦想。除了博客的名字中包含Build之外,从2011年起,针对Windows系统开发者的年度技术大会的名字也改名为Build。
2009年11月,史蒂文和哈佛商业学院教授Marco Lansiti合著的书出版,书名为《单一策略:组织、计划和决策》(One Strategy: Organization, Planning, and Decision Making)。在这本书中,史蒂文阐述了他的管理理念,明确强调了单一策略和强控制力在庞大组织中的重要性。
史蒂文的管理理论绝不只是写在书本上的,在现实工作中他也是知行合一的。举个例子来说,2010年,史蒂文和微软的另一位高管雷·奥兹(Ray Ozzie) 发生了争执。奥兹是Groove Network的创始人,2005年微软收购这家公司,奥兹随之加入微软,2006年,他接替比尔·盖茨的首席架构师一职。可以说,奥兹是比尔·盖茨亲手选定的人才,盖茨希望他能够掌握公司的远景规划。奥兹不负期望,在2008年宣布了著名的微软云平台——微软Azure。2009年左右,卡特勒就在奥兹的部门工作,领导代号为“红狗”(Red Dog)的颠覆性创新项目。2010年,奥兹的团队开发了一种文件同步技术,名叫“Live Mesh”,该技术可以把本地文件和云端的文件无缝融合。当时,奥兹召集了一个大约50人的团队,准备实施这个项目。但是,这和史蒂文团队的SkyDrive (空中网盘)项目发生了冲突,遭到史蒂文的坚决反对。史蒂文认为让Windows依赖其他团队的部件违背了他的“单一策略和强控制力”原则,降低了他的控制力,可能影响Windows 8的发布时间,并将这件事情闹到了当时的CEO鲍尔默那里。鲍尔默最终决定把 Live Mesh 项目合并到史蒂文的部门,这引起了奥兹的激烈反对。2010年10月,奥兹宣布离开微软,盖茨选定的人才就这样离开了。
坚信单一策略原则的史蒂文以强大的控制力大刀阔斧地革新Windows系统。全面改造Windows系统这架有些陈旧的马车,让它改头换面,以适应移动互联网时代。
Windows 8所做的最大改动要算用于支持移动应用的Metro功能了。Metro起初是微软设计团队创建的一种UI设计风格,灵感来源于地铁站中的方向指示牌。在Windows 8中,Metro被赋予新的含义,代表具有Metro UI风格的新型Windows应用,简称Metro应用(App)。有了Metro应用后,它成为Windows系统中的“新型公民”,以前的Windows应用变成了“旧派”。Windows 8为新的Metro应用设计了新的API、新的运行时(Windows Runtime),也配备了新的桌面(图1-4(a)),系统启动后,首先看到的是新桌面,用户可以切换到旧的桌面。如图1-4(a)与图1-4(b)所示,一个系统上有新旧两套桌面,好像是一个双面人。
从架构角度(图1-5)来看,Metro应用和旧的Win32应用(也称为桌面应用)都运行在NT内核之上,共享系统服务和基础的软件库。
因为Metro这个名字与著名的麦德龙超市同名,为了避免商标争议,Metro应用后来改名为“商店”应用(Store App)。
(a)
(b)
图1-4 Windows 8的两套桌面
图1-5 Windows 8的架构
开发Windows 8期间,苹果公司的iPad产品大行其道。漂亮的外观、触摸式操作和丰富的应用,让这款产品风靡全球,也让全世界的无数IT团队都想模仿它的功能,Windows 8也不例外。为了让古老的Windows能像iPad那样适合触摸式操作,Windows 8团队做了很多努力,改造传统的界面,把一切不适合触摸的都纳入改造范围,包括控制面板和开机启动菜单。为了让控制面板里的设置功能适合触摸,Windows 8团队把很多设置都重写了。
控制面板的功能重写起来还好,不是那么困难,但启动菜单的改造就没那么简单了。在启动菜单阶段,只有简单的软件环境,读写文件都依靠裁剪过的文件系统函数,要支持图形和触摸需要较大的工作量。于是不知道是哪位同行想出了一个糟糕的方法,启动内核后,再运行一个应用来显示启动菜单。这样实现起来是简单了,但是逻辑上乱套了,启动菜单本来的作用是配置内核启动选项,现在需要内核启动起来才能显示菜单,当内核启动不起来时,启动菜单也弹不出来了。
这个负责显示启动菜单的程序名叫BOOTIM。在Windows 8或者更高版本的Windows系统中,在“开始”菜单处输入bootim并执行它,就会出现启动菜单画面,一般只包含一个“关闭电脑”选项(图1-6),看到这个画面,你可能被吓一跳,以为系统突然重启了,其实不必害怕,它只是一个全屏显示的普通应用,按Alt + Tab组合键就可以把它切换到后台了。
图1-6 启动菜单画面中的“关闭电脑”选项
另外,可能是受iPad“无须关机”特征的影响,Windows 8的“开始”菜单中也删除了关机功能。这让很多老用户很不习惯,因为已经习惯了不用计算机就要关机,现在找不到“关机”菜单,怎不让用户着急?于是很多网站发表文章教用户如何寻找Windows 8的“关机”菜单。
其实,即使用户找到并选择了关机,Windows 8实际执行的也不再是传统的关机过程,而是改进了的休眠过程,把系统内核的执行状态保存在磁盘上。当用户下一次开机时,Windows 8会从磁盘上恢复上次的执行状态,迅速地显示出桌面,让用户感觉启动速度非常快,这个功能称为“混合启动”(hybrid boot)。
安全方面,Windows 8引入了名为ELAM(Early Launch Anti-malware)的机制,允许安全软件在启动早期便得到执行机会。
Windows 8树立的一个宏伟目标是彻底改变人们对Windows的印象(Windows Reimagined)。为了实现这个目标,Windows 8还修改了经典的蓝屏死机(Blue Screen of Death,BSOD)画面。经典的蓝屏死机画面使用的是文本模式,显示的字符串较多,Windows 8将其修改为图形模式,显示一个具有时代感的悲伤符号(图1-7),只显示较少的文字,背景颜色也从原来的蔚蓝色改为Windows 8风格的蓝色。
图1-7 Windows改造后的蓝屏死机画面
2012年8月1日,Windows 8的 RTM版本发布,构建号码为6.2.9200.16384。
随着Windows 8开发工作接近尾声和如期发布,无论是微软公司内部还是外部,关于史蒂文替代鲍尔默成为下一任CEO的传言越来越多。这不仅因为史蒂文领导着微软最重要的旗舰产品,还因为能与他竞争此职位的高管多已离去。但是,2012年12月31日,一条爆炸性的新闻流传开来,史蒂文离开微软。10月9日,他还在b8博客上发表文章,对Windows 8的全面发布做规划[7]。没想到,这篇文章成为b8博客的最后一篇文章,史蒂文离开了。Windows 8发布后,用户反应平平,花费大量精力打造的应用商店根本没有流行起来。2013年10月,Windows 8.1发布,纠正了Windows 8的一些过激行为,比如改进“开始”菜单,恢复电源按钮等。2014年年初,乔恩也离开微软,后NT时代的第一代主帅谢幕离场。
2013年7月,微软调整组织架构,成立了一个新的操作系统工程部门,并提拔泰瑞·迈尔森(Terry Myerson)作为这个部门的执行副总裁。这个新的部门把微软所有的操作系统团队整合在一起,除了传统的Windows系统,还包括Windows手机版本和面向游戏市场的Xbox系统。
统一了的Windows团队为下一代Windows系统所制定的主题就是“统一”。进一步说,就是要为不同大小和形式的硬件打造一个统一的Windows平台(Universal Windows Platform,UWP)。UWP可以在台式机、笔记本电脑、平板电脑、智能手机、一体化设备等上运行,以统一的接口支持上面的应用软件。2014年9月,迈尔森在与媒体见面时,宣布下一代Windows系统的名字叫Windows 10。
为了实现统一平台的目标,Windows 10做了很多努力,从底层的驱动程序模型到顶层的应用程序接口。从应用程序的角度讲,Windows 10使Windows 8引入的商店应用演进为“统一应用”(Universal App),也称UWP应用。UWP应用具有非常好的兼容性,可以运行在UWP支持的不同类型的设备上,包括PC、平板电脑、智能手机、嵌入式设备、Xbox One、Surface Hub以及混合现实设备等。
驱动程序方面,Windows 10引入了“通用驱动程序”(Universal Driver)的概念,驱动开发者可以创建单一的驱动程序包,来为不同类型的系统安装硬设备。
在推动和实现UWP平台的Windows 10设计者中,有一个很熟悉的名字,叫丹·鲍克斯(Don Box),他写过包括《COM本质论》(Essential COM)在内的多本技术畅销书。
2015年3月,微软恢复中断了6年之久的WinHEC(Windows Hardware Engineering Conference),并把会议地点定在中国深圳。迈尔森和丹·鲍克斯等Windows 10团队的很多重要人物出席了会议。这个会议的一个重要目的就是为Windows 10的正式发布做准备。
2015年7月29日,第一个版本的Windows 10正式发布。之所以叫“第一个版本的Windows 10”,是因为从Windows 10开始,微软改变了传统的每隔几年发布一个新版Windows系统的做法。新的方式是始终保持Windows 10之名和10这个主版本号,每年做年度更新,更新小版本号。迄今为止,微软一直坚持着这种做法,2015年11月发布了代号为Threshold 2的更新,2016年8月发布了代号为Redstone 1的周年更新。从2017年开始,微软每年春秋两季各发布一个更新版本,已经发布了4个更新版本,代号分别为Redstone 2、Redstone 3、Redstone 4和Redstone 5。在写作本章内容时,笔者使用的Windows 10便是2018年4月发布的Redstone 4,其构建号码为17134。在命令行中执行ver命令得到的版本信息如下:
Microsoft Windows [版本 10.0.17134.523]
在Windows 10的预览版本中,曾用过6.4的版本号,正式发布后,便一直使用10.0的版本号了。这种保持固定版本号的做法遭到了很多人的批评,主要原因是不再可以用简单的方法识别系统版本,让版本机制失去了本来应该有的作用。Windows开发社区中一位非常著名的开发者Tim Roberts先生也认为,这是微软做的一个非常差劲的决定[8]。
从功能角度来看,Windows 10引入的最大新功能莫过于适用于Linux的Windows子系统(Windows Subsystem for Linux,WSL)。启用了WSL功能后,用户可以在Windows系统中运行原生的Linux程序,但这些程序依赖的不是Linux内核,而是经典的NT内核,某种程度上说,这称得上是NT内核的一次重生。
另外一个非常大的新功能便是VBS,全称是基于虚拟化的安全(Virtualization-Based Security)。启用VBS功能后,Windows 10会启动集成的Hyper-V的虚拟机监视器(VMM),然后启动两个虚拟机,一个运行NT内核,另一个运行专门用于安全目的的安全内核(SecureKernel.EXE),其架构如图1-8所示。
在安全内核上面,运行着一些特殊的应用程序,这些程序是使用特殊工具开发的,运行在隔离的用户空间(Isolated User Mode,IUM)中,普通内核上面即使有恶意软件,也很难攻击到IUM中的程序。从安全的角度来看,安全内核提供了一个隔离的环境,执行证书验证等安全有关的功能操作,这是有价值的。但是,从用户的角度来看,把普通的程序和NT内核运行在虚拟机里,是会损失性能和降低用户体验的。
Windows 10引入的另一个功能是名为小娜(Cortana)的语音助理,与iPhone上的Siri类似,难免有仿效之嫌。
图1-8 Windows引入的VBS功能架构
在Windows 10的第一个版本发布前,微软把设备集团(Devices Group)与操作系统集团(Operating Systems Group)合并组成新的Windows和设备集团(Windows and Device Group),仍由迈尔森领导,这让迈尔森在微软的职业生涯达到了巅峰。但好景不长,2018年3月,微软进行大规模的组织架构调整,拆散Windows和设备集团,迈尔森离开微软。在告别微软的公开邮件中,迈尔森深情地回顾了他在微软的21年工作经历[9]。1997年,因为创建的公司被微软收购,迈尔森加入了微软,从一家小公司的CEO变成微软的一个产品单元经理(Product Unit Manager)。2001年,迈尔森加入Exchange团队,工作8年而成名,而后领导Windows Phone团队达5年之久,最后5年在Windows 10团队,是顶峰也是终点。
在第一个版本的Windows 10发布前的WinHEC会议中,笔者以MVP身份受邀参加,有机会与Windows 10团队的部分成员近距离交流,包括迈尔森(图1-9)和丹·鲍克斯。
图1-9 为迎接Windows 10发布的WinHEC会议晚宴(中间背包站立者为迈尔森)
迈尔森离开后,Windows团队被分散到多个产品部门,微软主攻以Azure为核心的云服务,不再像以往那样重视Windows了,Windows在微软的旗舰地位不复存在。这可能是微软的错误,但无论如何,一个时代结束了。
Windows是个庞大的系统,流行了两个时代,至今仍有广泛的用户群体。表1-3列出了源于NT内核的各个Windows版本的推出时间和内部版本号。
表1-3 源于NT内核的Windows操作系统
产品名称 |
内部版本号 |
发布日期 |
备 注 |
---|---|---|---|
Windows NT 3.1 |
3.1 |
1993年7月 |
— |
Windows NT 3.5 |
3.5 |
1994年9月 |
— |
Windows NT 3.51 |
3.51 |
1995年5月 |
— |
Windows NT 4.0 |
4.0 |
1996年7月 |
— |
Windows 2000 |
5.0 |
1999年12月 |
分为台式机版本(Windows 2000 Professional)和服务器版本 |
Windows XP |
5.1 |
2001年8月 |
巅峰的桌面版本 |
Windows Server 2003 |
5.2 |
2003年3月 |
巅峰的服务端版本 |
Windows Vista |
6.0 |
2006年11月 |
— |
Windows Server 2008 |
6.0 |
2008年2月 |
— |
Windows 7 |
6.1 |
2009年7月 |
— |
Windows Server 2012 |
6.2 |
2012年8月 |
— |
Windows 8 |
6.2 |
2012年10月 |
— |
Windows Server 2012 R2 |
6.2 |
2013年10月 |
— |
Windows 10 |
10.0 |
2015年7月 |
— |
Windows Server 2016 |
10.0 |
2016年9月 |
— |
Windows Server 2019 |
10.0 |
2018年10月 |
— |
考虑到16位的Windows和Windows 9x都已经过时,所以本书主要讨论的是表1-3列出的源于NT内核的各个Windows版本。如无特别说明,我们介绍的内容都是针对这些版本的。为了行文方便,我们大多时候就使用Windows来指代这些版本。
[1] Oral History of David Cutler. Computer History Museum. 2016.
[2] Windows, A Software Engineering Odyssey. Mark Lucovsky. 2000.
[3] The engineer’s engineer: Computer industry luminaries salute Dave Cutler’s five-decade-long quest for quality.
[4] It was 20 years ago today…
[5] Showstopper!: The Breakneck Race to Create Windows NT and the Next Generation at Microsoft. G. Pascal Zachary.
[6] Windows Server 2003 by the Numbers: One of the Biggest Product Launches in Microsoft History.
[7] Updating Windows 8 for General Availability.
[8] Windows 10 GetVersionEx.
[9] Thank you for 21 years, and onto the next chapter...
在古老的儒家经典《礼记》中,有一句话叫“致广大而尽精微”。告诉我们学习时既要有广博的视野,又要了解关键细节。本书前两篇的目标是帮助读者快速认识Windows系统。如果说上一篇的目标是尝试“致广大”,那么本篇的目标便是“尽精微”了。
Windows是一个庞大的系统。如果要讨论它的细微处,那么可以选的内容太多了。经过很多次筛选和变更,最终本篇选择了4个方面的内容。
第5章探索Windows系统里几个独特的函数调用机制——APC、DPC、LPC和RPC。选择这个内容的原因是多方面的,一方面,它们是Windows系统里常用的机制,调试时经常遇到,而且常常成为拦路虎,追踪到这里就难以推进了。另一方面,理解它们有较大的难度,已有的资料大多比较抽象晦涩。我们使用不同的方法,通过具体实例,上调试器,让抽象的概念具体化。
第6章探索的是Windows系统中神秘的垫片(shim)机制。自从Windows XP引入这个机制后,它便成为Windows系统中解决各种软件兼容问题的核心机制。今天,随着向64位Windows系统过渡和Windows on ARM(WoA)的发展,垫片机制的应用范围更加广泛。很多时候,会看到垫片模块的身影。垫片模块会改变软件的原本执行路径,让本来已经充满不确定性的软件世界变得更加飘忽不定。
不管你是否喜欢.NET,它都已经成为Windows系统的一部分。既然无法回避,就面对它。因此,本书第7章让你快速了解托管世界的核心技术和关键细节。
Windows 10引入的WSL(Windows Subsystem for Linux)技术让Linux应用程序可以运行在NT内核之上。这代表着Windows和Linux两大软件平台从对立走向合作,从各行其道到交叉和融合。WSL给经典的NT内核添丁增户,让它在新时代焕发活力。第8章探索WSL的架构、组件和关键细节。
说到探微,自然想到《庄子》里的经典篇章《庖丁解牛》。
庖丁为文惠君解牛。手之所触,肩之所倚,足之所履,膝之所踦,砉然向然,奏刀騞然,莫不中音:合于《桑林》之舞,乃中《经首》之会。
文惠君曰:“嘻,善哉!技盖至此乎?”
庖丁释刀对曰:“臣之所好者,道也;进乎技矣。始臣之解牛之时,所见无非牛者;三年之后,未尝见全牛也。方今之时,臣以神遇而不以目视,官知目而神欲行。依乎天理,批大郤,导大窾,因其固然,技经肯綮之未尝,而况大軱乎!良庖岁更刀,割也;族庖月更刀,折也。今臣之刀十九年矣,所解数千牛矣,而刀刃若新发于硎。彼节者有间,而刀刃者无厚;以无厚入有间,恢恢乎其于游刃必有余地矣!是以十九年而刀刃若新发于硎。虽然,每至于族,吾见其难为,怵然为戒,视为止,行为迟。动刀甚微,謋然已解,如土委地。提刀而立,为之四顾,为之踌躇满志;善刀而藏之。”
文惠君曰:“善哉!吾闻庖丁之言,得养生焉。”
——《庄子·养生主》
庄子写这个精彩的故事本来是借庖丁之言来说明养生道理的,但对于软件调试也很适用。
操作系统(Operating System,OS)是计算机系统中的基本软件,它负责统一管理系统中的软硬件资源,为系统中运行的应用软件(application)提供服务,是应用软件运行的基础。操作系统所提供的服务因其设计目的和使用环境不同而有所差异,但通常都包括文件管理、内存管理、进程管理、打印管理、网络管理等基本功能。除了这些功能,如何支持调试也是操作系统设计的一项根本任务,从被调试对象的角度来看,可以把操作系统的调试支持分为以下3个方面。
第一,对应用程序调试(application debugging)的支持,即如何简单高效地调试运行在系统中的各种应用程序。应用程序通常在操作系统分配的较低优先级下运行,其代码属于操作系统不信赖的代码。
第二,对设备驱动程序调试(device driver debugging)的支持,设备驱动程序或其他运行在内核模式的模块是操作系统的可信赖代码,通常与操作系统运行在同一个优先级下和同一个地址空间中,因此调试这些模块通常与调试应用程序有很大不同。
第三,对操作系统自身调试的支持,即如何调试操作系统的各个组成部分。例如调试正在开发的系统模块,以及定位产品发布后出现的系统故障。
调试器(debugger)是软件调试最重要的工具,使用调试器调试是解决复杂软件问题的首选途径。但是在某些情况(比如产品发布后在用户环境中出现问题)下,并不具备使用调试器调试的条件,因此就必须考虑如何在没有调试器的情况下进行调试。从这个意义上讲,对于以上每类调试对象(任务),还必须考虑两种情况。
第一,使用调试器的调试,即通过有效的模型和系统机制来支持调试器软件操纵和访问被调试对象。
第二,不使用调试器的调试,即通过操作系统的基础服务,支持软件实现各种不依赖于调试器的调试途径,比如错误提示、事件追踪、日志和错误报告等。
综合以上分析,可以把操作系统的调试支持归纳为如下表所示的6个问题。
对比项目 |
使用调试器的调试 |
不使用调试器的调试 |
---|---|---|
调试应用 |
如何使系统中的应用程序可以被调试器调试?(问题A) |
如何使系统中的应用程序在没有调试器时也具有很好的可调试性?(问题B) |
调试驱动程序 |
如何使系统中的驱动程序可以被调试器调试?(问题C) |
如何使系统中的驱动程序在没有调试器时也具有很好的可调试性?(问题D) |
调试操作系统自身 |
如何使操作系统自身的代码可以被调试器调试?(问题E) |
如何使系统本身在没有调试器时也具有很好的可调试性?(问题F) |
本篇以Windows操作系统为例详细探讨操作系统对软件调试的支持,主要分为如下5个板块。
第9章和第10章着重讨论问题A,包括支持应用程序调试的用户态调试子系统、调试会话的建立过程及调试事件的产生和分发机制等。
与CPU异常相呼应,第11章和第12章从操作系统的层面分析异常的分发和处理机制及系统处置未处理异常的方法。该板块的主题是异常处理,它与6个问题都相关。
第13~17章把视线转向调试器之外的辅助调试机制(问题B、D和F)。第13章分析Windows系统提供的错误提示机制。第14章介绍Windows XP系统引入的错误报告(WER)机制。第15章分析Windows的事件日志(event log)机制。第16章分析Windows事件追踪(ETW)机制。
内核调试对于解决系统级的问题和学习操作系统有着非常重要的意义,因此,第18章深入介绍Windows系统的内核调试引擎。
第19章介绍用于提高测试和调试效率的验证机制,包括应用程序验证和驱动程序验证。
在计算机科学尤其是软件的发展历史中,格蕾丝·穆雷·赫柏(Grace Murray Hopper)(1906—1992)是一位做出了重大贡献的杰出女科学家。她的主要成就之一就是发明了编译器——可以将程序语言翻译成机器码的自动编码工具。格蕾丝于1951年到1952年间开发了A-0编译器,A-0编译器被公认为计算机历史上的第一个编译器。
编译器的出现为编程语言的繁荣和软件产业的形成奠定了基础。有了编译器,人们把烦琐的编码工作交给机器来完成,编码时间一下减小到几乎可以忽略的地步,以至于今天我们很少再关心机器码的产生过程,就连编码这个词的主要含义也由本来的编写机器码演变为编写源代码。
编译器的出现,对软件调试也有着极其重要的意义。在1955年题为“数字计算机自动编码”的著名演讲中,格蕾丝在谈到自动编码的优势时,首先讲的是它缩短了调试时间,因为编译器编码比人工编码更加准确,而且可以做很多自动的检查。可见,格蕾丝在设计第一个编译器时便考虑到了检查编程错误和支持软件调试。今天的编译器尽管与50多年前格蕾丝所发明的编译器相比有了很大的变化,但是有一点没有变,那就是支持调试仍然是编译器设计中的一项基本任务,很多调试技术都是建立在编译器的有关支持基础上的。可以把编译器的调试支持概括为以下几个方面。
(1)编译期检查。编译器在编译过程中,除了检查代码中的语法错误,还会检查可能存在的逻辑错误和设计缺欠,并以编译错误或警告的形式报告出来。
(2)运行期检查。为了帮助发现程序在运行阶段所出现的问题,编译器在编译时可以产生并加入检查功能,包括内存检查、栈检查等。
(3)调试符号。今天的大多数软件是使用更易于人类理解的中高级编程语言(如C/C++、Pascal等)编写的,然后由编译器编译为可执行程序交给CPU执行。当调试这样的程序时,我们可以使用源程序中的变量名来观察变量,跟踪或单步执行源程序语句,仿佛CPU就是在直接执行高级语言编写的源程序。我们把这种调试方式称为源代码级调试(source code level debugging)。要支持源代码级调试,调试器必须有足够的信息将CPU使用的二进制地址与源程序中的函数名、变量名和源代码行联系起来,起到这种桥梁作用的便是编译器所产生的调试符号(debugging symbol)。调试符号不仅在源代码级调试中起着不可缺少的作用,在没有源代码的汇编级调试和故障转储文件分析时,也是非常宝贵的资源。有了正确的调试符号,我们便可以看到要调用的函数名称或要访问的变量名(只要调试符号中包含它)。当对冗长晦涩的汇编指令进行长时间跟踪时,这些符号的作用就好像是黑夜中航行时所遇到的一个个灯塔。
(4)内存分配和释放。使用内存的方法和策略关及软件的性能、稳定性、资源占用量等诸多指标,很多软件问题也都与内存使用不当有关。因此,如何降低内存使用的复杂度,减少因为内存使用所导致的问题便很自然地成为编译器设计中的一个重要目标。例如,在编译调试版本时,编译器通常会使用调试版本的内存分配函数,加入自动的错误检查和报告功能。
(5)异常处理。Windows操作系统和C++这样的编程语言都提供了异常处理与保护机制。编译包含异常保护机制的代码需要编译器的支持。
(6)映射(MAP)文件。很多时候,我们可以得到程序崩溃或发生错误的内存地址,之后我们很想得到的信息就是这个地址属于哪个模块、哪个源文件甚至哪个函数更好。调试符号包含了这些信息,但是调试符号通常是以二进制的数据库文件形式存储的,适合调试器使用,不适合人工直接查阅。映射文件以文本文件的形式满足了这一需要。
本篇将先介绍编译有关的基本概念和编译期检查(第20章),然后介绍运行时库和运行期检查(第21章),接着分别介绍栈与堆的原理和有关问题(第22章和第23章),随后介绍编译器编译异常处理代码的方法(第24章),最后将详细讨论调试符号(第25章)。
调试器(debugger)是解决复杂软件问题的最重要工具。如果把软件调试技术比作一门武功,那么调试器就是应用这门武功时必不可少的武器。
简单来说,调试器就是用来提供调试功能的软件或硬件工具。到底要提供哪些功能才能称得上是调试器没有明确的标准。我们通常认为一个调试器至少应该具有如下两项功能:可以控制被调试程序的执行,包括将其中断到调试器,单步跟踪执行,恢复运行,设置断点等;可以访问被调试程序的代码和数据,包括读写数据、观察数据、反汇编成代码、读写寄存器等。
以上两项功能是相辅相成的,前者让被调试程序根据调试人员的要求运行或停止,后者对被调试程序进行分析。二者结合起来就可以将被调试程序中断在几乎任意时间或空间位置,然后对其进行观察、分析和修改,待分析结束后再让被调试程序继续运行,这种调试方式称为交互式调试(interactively debugging),这是区别调试器和其他普通工具的最重要标准。这种将被调试对象静止下来,然后无限期地“慢慢”分析其状态,而后再像什么也没发生过似的让其继续运行的交互式诊断方法是软件调试所特有的,在硬件和传统的医学或其他领域都是很难施行的。例如,医生诊断心脏疾病时是不可能将病人的心脏停止在某个位置来观察的。
根据被调试程序的工作模式,可以把调试器分为用户态调试器和内核态调试器,前者用于调试用户态下的各种程序,如应用程序、系统服务,或者用户态的DLL模块;后者用于调试工作在内核态的程序,如驱动程序和操作系统的内核模块。第26章将讨论调试器的通用模型、一般原理、基本任务和常见调试器的分类。
Visual Studio是Windows平台上的经典开发工具,它集成了简单易用的调试功能,是开发期调试的首选工具。第27章将详细介绍Visual Studio集成环境中的调试器VsDebug,包括它的结构、工作原理和一些重要功能。
Visual Studio Code(VS Code)是近年来流行的一个开发工具,具有轻便灵活、跨平台、开源等优点,它继承了Visual Studio的很多界面风格、使用习惯等经典特征,也开创了一些新的功能和设计模式,比如引入Web技术和以扩展包来组织功能模块的设计思想等。VS Code不仅具有很强的实用性,而且代表了软件开发的一些新方向。第28章将探讨VS Code中蕴含的软件智慧,包括它的独特设计和调试功能。
WinDBG是Windows平台中使用非常广泛的著名调试器,它既可以用作用户态调试器,也可以用作内核态调试器,是调试Windows操作系统下各种软件的一个强有力的工具。本篇的第29章和第30章将分别讨论WinDBG调试器的工作原理与使用方法。
调试器的设计初衷是辅助软件开发人员定位和去除软件故障,但因为调试器对软件所具有的强大控制力和观察力,其应用早已延伸到了很多其他领域,比如逆向工程、计算机安全等。可以说,调试器是几乎所有软件高手的必备工具,他们都非常擅于使用调试器。因此,每个软件工程师都应该把学习和熟练使用调试器当作一门必修课。
2005年6月12日,乔布斯(Steve Jobs,1955—2011)出席斯坦福大学的毕业典礼,并做了大约15分钟的演讲。在这次著名的演讲中,乔布斯讲了他人生中的3个重大事件:第一个是他从大学辍学,第二个是他被迫离开自己创建的苹果公司,第三个是他在前一年里被查出癌症。结合这3个重大事件,乔布斯分享了他的人生哲学。其中,他反复强调的一个词汇就是热爱。“我坚信唯一使我一直走下去的,就是我热爱我所做的事情。你必须找到你所爱的东西。这不仅对于恋爱是真理,对于工作也是如此。你的工作会在你的生命中占据大部分时间。让你真正感到满足的唯一方法就是要做你认为伟大的工作。而要做伟大工作的唯一方法就是要热爱你所做的。如果你现在还没有找到,那么继续寻找,不要停止。”
结束演讲时,乔布斯送给学生们两句非常简单却意味深长的话:“Stay hungry. Stay foolish.”。对于这两句话,不同人可能有不同的理解。中文的翻译方法也有很多种,比如,“求知若饥,虚心若愚。”放下第一句不谈,对于第二句,我觉得这里的foolish与中文的“痴”字意思很近。
在英文中,foolish的本意就是“傻”,这也正是“痴”字的基本含义。在《现代汉语词典》中,痴的第一个含义就是“傻,愚笨”。《现代汉语词典》为“痴”字列出的第二个含义是“极度迷恋”。这刚好与乔布斯在演讲中说了11次的“love”(爱)一致。
1936年,林语堂(1895—1976)告别上海,到美国生活(参见卷1之跋语)。到了美国后,非常热爱生活的林语堂很快找到了一项既有趣又别致的活动,那就是到大海里钓鱼。
在等待鱼儿上钩的时候,林语堂一边欣赏海上的风景,一边张开想象的翅膀,思接千载。他思考苏东坡是否钓过鱼,思考陆游是否钓过鱼,思考的结论是他们都不曾钓鱼,原因是像苏东坡和陆游那么爱写诗的人,如果钓过鱼,那么一定有诗记录。他继续思考古代的文人和士大夫为何不钓鱼,得到的结论是他们认为自己的身份不该做钓鱼那样的体力活,“文人不出汗,出汗非文人”。
林语堂把这些思考写了下来,这便是著名的散文《记纽约钓鱼》。“纽约处大西洋之滨,鱼很多,钓鱼为乐的人亦自不少。长岛上便有羊头坞,几十条渔船,专载搭客赴大西洋附近各处钓鱼。”
“记得一晚,是九月初,蓝鱼已少,但特别大。我与小女相如夜钓,晨四点回家,带了两条大鱼,一条装一布袋,长三尺余,看来像两把洋伞,惊醒了我内人。”
多么生动的描写啊!除了优美的叙事,在这篇散文里,林语堂还提出了一个鲜明的论点,这便是著名的林氏名言:“人生必有痴,而后有成。”
林语堂从钓鱼想到痴,因为他爱上了这个活动,经常这么做,以此为乐。不过,钓鱼只能算是一种休闲活动,与乔布斯所说的能让人实现满足感的工作是不同的。林语堂或许意识到了这一点,在文中特别解释道:“痴各不同,或痴于财,或痴于禄,或痴于情,或痴于渔。各行其是,皆无不可。”
或许,把对钓鱼的“痴”换成另一个汉字“癖”更合适。“癖”字的结构与痴很类似,都带“病”字旁,也是常常贬义的,类似的字还有“疵”,即瑕疵的疵。关于“癖”和“疵”,明末清初的著名学者张岱(1597—1679)有一句名言:“人无癖不可与交,以其无深情也。人无疵不可与交,以其无真气也。”
在张岱看来,人有癖才会有深情,有疵才会有真气。仔细体味,特别是结合生活中或者历史上的人思考一下,便觉得更加贴切。
如果把钓鱼这样的癖好算作小痴的话,那么乔布斯所说的对伟大工作的不懈追求则可谓“大痴”。除了有深情和有真气外,要做到“大痴”还必须有耐力,坚韧不拔,有恒心和毅力。以《富春山居图》著称的元代大画家黄公望很喜欢用的一个别号便叫大痴。他创作《富春山居图》时,已经年近八十,用了几年时间才画成。
林语堂痴于钓鱼,因为他和他之前那些代的文人很难把兴趣和工作合二为一。感谢计算机先驱们的努力,让我们能以软件为生。根据笔者20多年来的经验,要把软件做好,也需要有一颗痴心。要写出优秀的代码需要有真气,要和团队协作一起开发出好的产品需要有深情,要解决复杂的问题需要有毅力。年轻人要做到这三点,则需要长时间的修炼。如何才能长期修炼而又保持热情呢?
《论语》里给出的答案是“知之者不如好之者,好之者不如乐之者”。有人把它看作学习的三层境界——知、好、乐。其实不仅学习如此,工作也是如此。做学问如此,做软件也是如此。
如何能沿着孔子说的“知、好、乐”三层境界不断提升呢?南宋的著名理学家张栻(1133—1180)为我们准备了一个很好的回答:“知而不能好,则是知之未至也;好之而未及于乐,则是好之未至也。”
张栻话中的“至”代表一种程度。简单解释上面这句话,就是说不能从第一层境界“知”升级到第二层“好”境界的原因是知道的知识还不够多,不能从第二层境界“好”升级到第三层境界“乐”的原因是喜好和热爱的程度还不够。
那么如何才能推进“知”的数量和“好”的程度呢?华罗庚引用了韩愈的“书山有路勤为径,学海无涯苦作舟”。朱熹的回答是“格物”,今日格一物,明日格一物,日积月累便会融会贯通。以在荒漠中隐居著称的圣安东尼(St. Anthony,约251—356)教父的回答是“苦修”。乔布斯给出的建议是“Stay foolish”,笔者提议把它翻译为“持之若痴”。
《软件调试》第1版出版12年后,第2版之卷2即将付印,略缀数语于书后,与亲爱的读者共勉,并向本卷的编辑团队陈冀康、吴晋瑜和谢晓芳致敬。
张银奎
2020年7月5日
于上海