本文是“深入理解调试器”系列的第二部分,着重介绍了运行程序的内部结构。
**从程序的本质出发**
文章将程序比作老式游戏机的游戏卡带,解释了程序在多任务操作系统中的虚拟化实现。为了实现程序的独立运行,操作系统引入了虚拟地址空间、执行线程、可执行映像、加载器和进程等概念。
**虚拟地址空间**
虚拟地址空间为每个程序提供独立的地址范围,避免程序之间相互干扰,并允许程序访问比物理内存更大的地址空间。文章详细解释了页表的作用,以及操作系统如何利用页表进行地址转换和内存管理,从而高效利用物理内存。
**执行线程**
执行线程是CPU执行指令流的状态集合,包括寄存器状态、指令指针等。操作系统通过调度多个线程来模拟多个程序同时运行,并利用上下文切换在不同线程之间快速切换。
**可执行映像**
可执行映像是程序的非活动表示形式,包含机器指令、头信息和元数据等。文章以PE格式为例,介绍了可执行映像的结构,并展示了如何使用反汇编工具查看机器指令和数据。
**加载器和模块**
加载器负责解析可执行映像并将程序加载到内存中,使其能够执行。模块是加载后的可执行映像的等价物。文章用一个玩具可执行映像格式的例子说明了加载器的基本工作原理,包括如何解析映像、分配内存、加载数据和设置内存保护等环节。
**导入和导出**
加载器还负责处理导入和导出,实现不同模块之间的函数调用。文章介绍了动态加载和隐式加载两种方式,以及地址稳定性和重定位的概念。
**进程**
进程是运行程序的实例,包含若干个线程和模块,并拥有一个虚拟地址空间。文章总结了进程的概念,以及调试器如何与进程交互,并在最后预告了后续文章将介绍调试器如何与操作系统交互。
作者希望通过本文帮助读者理解运行程序的细节,为后续深入学习调试器奠定基础。