- [P18]目前的主流CPU架构都是使用栈来进行函数调用的,栈上记录了函数的返回地址,因此,通过递归是寻找放在栈上的函数返回地址,便可以追溯出当前线程的函数调用序列,这便是栈回溯的基本原理。
这里有两个信息需要注意:
1.函数调用的栈式结构是有着非常底层的硬件支持的,如CPU就直接提供了支持。
2.这段话表明了得到当前函数的调用路径技术,也就是通过递归方式查找栈上函数的返回地址。
- [P19]有时我们对高级语言的某一条语句的执行结果百思不得其解时,可以看一下它所对应的汇编代码,这时往往可以更快发现问题的症结。以下一节(1.6.1)将介绍的bad_div函数为例,看一下汇编指令,我们就可以知道编译器是将C++中的除法操作编译为无符号整除指令(DIV),而不是有符号整除(IDIV),这正是错误所在。
需要注意两点:
1.这段话表明了反汇编在软件调试中的一个重要作用,即对高级语言的执行结果不理解的时候可以通过看汇编代码的方式了解缘由。
2.CPU底层提供了DIV和IDIV两个指令,如今脚本动态语言比较热门,一个var关键字就可以描述一切变量,似乎不用关心变量的具体类型了,但是在CPU的底层还是有类型的区别,而且用法涉及到结果的正误。
- [P19]像调试WinDBG这样的调试器支持同时调试多个进程,每个进程又可以包含多个线程。调试器提供了单独挂起和恢复某一个或多个线程的功能,这对调试多线程和分布式软件是很有帮助的。
WinDBG好厉害。
- [P20]说锐不可当是因为核心的调试技术大多源于CPU和操作系统的直接支持,因此具有非常好的健壮性和稳定性,有较高的优先级。
- [P21]在软件工程中,一个值得注意的问题是不要把Bug轻易归咎于某一个程序员,讨论Bug的时候不要使用“你的Bug”这样的说法……,所以,一种较好的方式是称呼某某模块的Bug,而不要说成是某某人的Bug。
讲得很好,能说出这样话的人是真正懂软件这个行业规律的,是内行。可惜的是,据我所知国内有非常知名的IT公司都不懂这个道理,很可悲。
- [P25]调试器是逆向工程的主要工具。符号文件、跟踪执行、变量监视和观察、断点这些软件调试技术都是实施逆向工程时经常使用的技术手段。
这段话表明了调试与逆向工程的关系,逆向工程利用了调试技术。
- [P29]尽管现代CPU的集成程度不断提高,机构也变得越来越复杂,但是它在计算机系统中的角色仍然非常简单,那就是从内存中读取指令,然后解码和执行。指令是CPU可以理解的并执行的操作,是CPU能够“看懂”的唯一语言。
这段话讲解的十分清楚明白,对第二章有提纲挈领的效果。
- [P33]分页机制,即以页为单位来组织内存,通过页表进行管理,暂时不用的页可以交换到硬盘等外部存储器上,需要时再交换回来。分页机制是实现虚拟内存的硬件基础。
CPU有两个内存管理机制:段机制和页机制,首先由80386所支持。
- [P46]保护模式是为实现多任务而设计的,其名称中的“保护”就是保护多任务环境中的各个任务安全。多任务环境的一个基本问题 就是当多个任务同时运行时,如何保证一个任务不会受到其它任务的破坏,同时也不会破坏其他任务。
这段话说明了保护模式的主要作用以及多任务环境所面临的主要问题。
- [P48]IA-32处理器定义了4个特权级,又称为环,分别用0、1、2、3表示。0代表的特权级最高,3代表的特权级最低。最高的特权级通常是分配给操作系统的内核代码和数据的。比如windows操作系统的内核模块是在特权级0运行的,windows下的各种应用程序(例如MS Word、Excel等)是在特权级3运行的。因为特权级0下运行的通常是内核模块,所以人们便把在特权级0运行说成是在内核模式运行,把在特权级3运行说成是用户模式运行。
时间一长就忘记基本的概念了,比如内核模式、用户模式,这段话讲解的非常清楚,一下子就明白了。
- [P67]至此我们可以归纳出中断和异常的根本差异是:异常来自于CPU本身,是CPU主动产生的;而中断来自于外部设备,是中断源发起的,CPU是被动的。
中断和异常的比较是计算机中比较重要的概念,这本书专门用了一章的篇幅讲解这两个概念。
- [P163]操作系统是计算机系统的基本软件,它负责统一管理系统中的软硬件资源,为系统中运行的应用软件提供服务,是应用软件运行的基础……如何支持调试也是操作系统的一项根本任务。
读到这里,我发现原来调试是一项多么基本的计算机功能,而且这项功能贯穿计算机的各个层次。最底层的CPU,以及上层的操作系统、应用软件,无不提供对调试的支持。那么很自然,我会很好奇操作系统是怎么支持调试的。
- [P166]Windows2000第一次将桌面版本和服务器版本统一起来,尽管Windows2000也有服务器版本和个人版本之分,但是二者的操作系统内核是相同的。不过像Windows2000那样用一个产品线来覆盖台式机和服务器这两大市场的模式很快就遇到了困难,因为服务器版本强调安全,台式机版本强调易用。
这是windows的一段历史,以前我还真不知道。
- [P167]进程和程序的关系好比是类和实例的关系。一个类可以有多个实例,一个程序也可以有多个实例在运行。比如我们可以启动记事本程序多次使用不同的实例操作不同的文件。有些程序在启动时会检测是否已经有自己的实例在运行,如果有,新的实例就会立即退出,以保证只有一个实例在工作。
这段话印证了我以前的一个猜测。
- [P167]对于不同的硬件平台,进程的空间大小也有所不同。对于32位的x86系统,每个进程的进程空间是4GB,即地址0x00000000到地址0xFFFFFFFF。为了高效地调用和执行操作系统的各种服务,Windows会把操作系统的内核数据和代码映射到系统中所有进程的进程空间中。因此,4GB的进程空间总是被划分为两个区域:用户空间和系统空间。
竟然每个进程空间都有操作系统内核数据和代码的映射,竟然是这样一种结构。以前读这些知识,并没有注意到“每个”,这次注意到了,感到神奇,还有那个2GB的映射,也很神奇。
- [P200]为了支持调试,系统会把被调试程序中发生的所有异常发给调试器。
注意是所有。
- [P291]为了让系统和应用程序代码都可以简单方便的支持异常处理,Windows定义了一套标准的机制来规范异常处理代码的设计(对程序员)和编译(对编译器),这套机制被称为结构化异常处理,简称SEH。
Windows针对软件异常的编写和编译是有专门规范的,这点值得注意。
网友评论