---摘自UNIX系统编程第二章
1程序与进程的转换
C编译器将每个源文件翻译成一个目标文件,然后编译器将这些单个的目标文件同必需的一些库相链接,形成一个可执行模块。程序运行或执行时,操作系统将可执行模块拷贝到主存储器的程序映像(program image)中去。
当操作系统向内核添加了适当的信息,并为运行程序代码分配了必要的资源之后,程序就变成了进程。进程拥有地址空间(它可以访问的内存)和至少一个被称为线程的控制流。进程的变量可以在进程的生命周期中始终存在(静态存储),也可以在执行进入一个程序块时自动分配,离开这个程序块时将其释放(自动存储)。
2 程序映像的布局
加载之后,可执行程序看起来占据了一个连续的内存块,这个连续的内存块被称为程序映像。图1显示的是程序映像在它的逻辑地址空间中的一个而已实例。程序映像有几个不同的分区。程序文本或代码显示在内存低端地址中。在映像中已初始化和未初始化的静态变量也有自己的分区。其他的分区包括堆、栈和环境。
图1
活动记录:
是在进程栈顶端分配的一个内存块,用来装载调用过程中函数的执行上下文。每次函数调用都在栈上创建一个新的活动记录。假如嵌套的函数调用按照后调用先返回的次序工作,那么函数返回时会将活动记录从栈中删除。
活动记录包括返回地址、参数(参数值从相应的命令行参数中拷贝而来)、状态信息和调用时某些CPU寄存器值的拷贝。进程人记录表示的调用中返回时,要恢复寄存器的值。活动记录中还包括函数执行时在其内部分配的自动变量。活动记录的特定格式取决于硬件和编程语言。
在声明时没有显示初始化的静态变量占据不同的分区。通常,已初始化的静态变量是磁盘上可执行模块的一部分,而未初始化的表态变量则不是。当然,自动变量不是可执行模块的一部分,因为只有当定义它们的程序块被调用时,它们才被分配。除非程序显示地对自动变量进行初始化,否则,它们的初始值是不确定的。
尽管程序映像看起来占据了一个连续的内存块,但实际上,操作系统将程序映像映射到不一定连续的物理内存块中。通常的映射将程序映像划分成相同大小的片,这些片被称为页。操作系统将这些页加载到内存中,当处理器引用某页上的内存时,操作系统会从一个表中查找这一页的物理位置。
在程序映像中,已初始化的静态变量和未初始化的静态变量占据不同的分区。通常,已初始化的静态变量是磁盘上可执行模块的一部分,而未初始化的静态变量则不是。
例:用ls -l对下面两个C程序的可执行模块的大小进行比较,会得到不同的结果。
版本1:
int arry[50000] = {1, 2, 3, 4};
int main()
{
arry[0] = 3;
return 0;
}
版本2:
int arry[50000];
int main()
{
arry[0] = 3;
return 0;
}
版本1的可执行模块比版本2的长大约200000字节,因为1中的array是已初始化的静态数据,是可执行模块的一部分。