13.5 用于进程/内存管理的数据结构
13.5.1 编译过程
前面已经讨论过UNIX用于管理文件系统的数据结构。这一节介绍UNIX用于操作进程的不同数据结构(假定内存管理是进程管理的一部分)。
为了理解UNIX维护的不同数据结构,有必要了解编译过程的基本知识。不管进程最终加载到什么位置,编译器生成由地址0开始的代码。例如,在COBOL中,数据部分从地址0开始。在定义所有的"fd"和工作存储器之后,也许会为过程部分留一部分空间以及分配的起始地址。过程部分中第一条指令的地址就变成了该程序"文本"区的虚拟起始地址。
编译完所有指令和生成地址后,编译器也许要为扩展部分留一些空间,然后为堆栈区定义一些空间。这个过程有两个限制:首先,分配给进程的总的虚拟地址空间(包括中间的间隙)不能超过硬件寻址能力。其次,区域的虚拟地址不能相互重叠。例如,"文本区"的起始虚拟地址要比"数据区"的最高虚拟地址更高。
编译器在可执行文件中保存所有与起始虚拟地址相关的信息,如图13-26所示。该文件为每个区域定义一个节。如图所示,编译器在可执行文件中保存节的类型、大小以及每个节的起始虚拟地址。
![]() |
(点击查看大图)图13-26 可执行文件 |
如图13-26所示,可执行文件分为四部分:
A 主标头
B 节标头或描述符
C 节内容
D 其他信息
主标头包括一个幻数,它指明可执行文件的类型、程序中节的个数。然后存储了寄存器初始值,这个初始值是在进程开始执行就要加载的。该部分还包括程序计数器(PC)的值。这个值指明程序从哪个地址开始执行。它是假定程序开始执行后"文本"区中某个指令的虚拟地址。
节标头指明节的类型,如前所述,同时还要保存节的大小以及起始虚拟地址。
节内容存储节真实的"内容",例如,"文本"节包含实际编译好的机器指令。"数据"节包含不同的I/O区以及其他预留给不同表和具有各自初始值(由赋初始值语句提供)的变量的单元空间。
其他信息包括符号表,符号表用在源码级调试程序。程序开始执行时,也要加载这个符号表。它提供了不同的数据和段落名(标签)及其机器地址。因此,当源代码级的调试程序想要显示计数器值时,调试程序知道从哪里可以获取计数器的值。
当创建进程,而且该进程想要从被当作文件名的程序名那里执行程序时,内核解析路径名,并得到路径名的索引节点。内核由该索引节点读取要分配给该可执行文件的实际块。根据扇区大小,向内存管理器发送请求,让其分配期望的存储单元,然后将实际的节加载到这些存储单元中。如果操作系统使用分页技术,还要设置用于地址转换的页面映射表,然后将寄存器初始值由主标头加载到寄存器中。节标头的信息用于设置UNIX维护的不同数据结构,如图13-27所示。
![]() |
(点击查看大图)图13-27 不同的UNIX数据结构 |
UNIX操作系统维护的数据结构如下所示:
进程表(Process Table,PT)
u区
每个进程区域表
区域表(Region Table,RT)
页面映射表(PMT)
内核堆栈
现在逐一讨论这些内容。