目录
- 文本段
- 初始化数据段
- 未初始化数据段
- 堆
- 栈
- 文本段(Text)
文本段,也称为代码段。代码段中存放可执行的指令,在内存中为了保证不会因为堆栈溢出被覆盖,将其放在了堆栈段下面。通常文本段是可共享的,因此对于频繁执行的程序(例如文本编辑器、shell 等),只需要在内存中保存一个副本。此外,文本段通常是只读的,以防止程序意外修改其指令。
- 初始化数据段(Data)
初始化数据段,包含初始化的全局变量和静态变量。例如:
int val = 3;
char string[] = "Hello World";
这些变量的值最初存储在只读内存中(通常是文本段),在程序启动时复制到数据段中。如果变量是定义在一个函数中,那么它们默认存储在本地栈帧中。
- 未初始化数据段(BSS)
未初始化数据段,包含被初始化为零值或在代码中没有被显示初始化的全局变量和静态变量。
- 堆(Heap)
堆区域从未初始化数据段的末尾开始,并从那里增长到更高的地址。堆区域由进程中所有共享库和动态加载的模块共享。堆是发生动态内存分配的段。
- 栈(Stack)
栈是一个后进先出的结构,通常位于内存的高地址部分。一个栈指针寄存器跟踪栈的顶部,每次有数据被 push 到栈里,栈指针始终指向栈顶。为一个函数调用 push 的一组数据称为栈帧。
栈帧一般包括:
-
函数的返回地址和参数
-
临时变量,包括函数的非静态局部变量以及编译器自动生成的其它临时变量
-
保存的上下文,包括在函数调用前后需要保存不变的寄存器
程序的加载执行
-
为程序的执行分配内存。
-
将地址空间从二级存储器复制到一级存储器。地址空间是包含代码、数据段和堆栈的内存空间。换句话说,地址空间是程序在运行时所使用的所有数据。
-
将可执行文件中的代码段 (Text) 及初始化数据段 (Data) 部分复制到主存中。
-
将程序参数 (如命令行参数) 复制到栈中。
-
初始化寄存器,设置栈指针寄存器 (ESP) 指向栈顶。
-
跳转到启动程序。
C程序代码分布
a.out是 assembler output(汇编程序输出)的缩写形式。在这里,它不是汇编程序输出,而是链接器输出。汇编程序输出这个名字的产生纯属历史原因。在早期的语言中并不存在链接器,程序是这样创建的:先把所有源文件连接在一起,然后进行汇编,汇编产生的汇编程序输出保存在a.out中。即使最后有了链接器之后,最后一个环节的输出文件依然沿用了这个命名习惯。
C程序内存布局
典型的C程序内存布局:
进程在基地址上加载代码段及数据段等。栈向下增长,每个栈帧都会被一个防护页分隔开,以检测栈帧之间的栈溢出。堆向上增长。在进程地址空间的中间,有一个区域是为共享对象保留的。当一个进程被创建时,进程管理器首先将代码段及数据段从可执行文件中映射到内存中。如果程序头表明可执行文件是针对一个共享库链接的,进程管理器将在内存中加载这个共享库,然后将控制权传递给这个库中的运行时连接器代码。