Site Overlay

程序的内存布局、栈帧简述(Linux x64)

正文

程序在内存中是什么样子的呢?

我们新建一个文件:

$ vi memory-layout.c

写入如下的代码:

#include <stdio.h> 

int global; /* Uninitialized variable stored in bss*/

int main(void) 
{ 
    return 0; 
} 

编译:

$ gcc memory-layout.c -o memory-layout

然后使用 size 指令:

$ size memory-layout
text       data        bss        dec        hex    filename
 960        248         12       1220        4c4    memory-layout

可以发现,程序在文件里有三个部分:Text, Data 和 Bss。$960+248+12 = 1220 = 4c4\text{H}$

text:程序代码段。存放程序的指令。只读不写,编译时确定。写死

data:静态初始化数据段,包括所有有初始值的全局变量和 static 修饰的变量。已初始+全局

bss:未显式初始化的全局变量、静态变量。运行一开始就全部清零。未初始+全局

而当程序运行之后,三个段都会被复制到内存当中。布局如下:

typora\20201211220654_f79dd819f6274ca520010179e1f0165a.png

注意两个箭头之间的空白,表示的是程序的剩余内存。内存又分为栈内存堆内存。上面向下指的箭头就是栈内存空间。箭头代表的是 %rsp,指向栈顶(和我们想象中可能不一样,它的顶是朝下的!所以往栈里放(push)东西之后,汇编指令是将 rsp 减去一定的值)

栈的基本组成单位是栈帧(Stack Frame)。而栈帧的构成如下:

typora\20201211220708_0395f22fa2c42f5709ea0e74b081b7cf.png

由于内存分配方向的问题,这个图形的“顶”部反而是在底部。因此用上下来描述容易引起误解,所以我改用前后来描述。前面,表示的是更低的内存位置,也就是栈增长的方向,后面则反之。

我们重点关注 rsp/esprbp/ebpepb 的后面是返回地址(Return address)当函数调用完毕之后,会将返回值(从 rax)传递到返回地址。而前面是当前帧。栈的最前面则是栈“顶”,存放于 esp。其次,在 epbesp 之间,还存放着我们的局部变量,保存的寄存器值等等。

再来简单说一说堆内存,堆内存实际上就是 malloc 取得的内存,也需要我们手动释放。C++/Java 等高级语言中的 new ,底层调用的就是 malloc ,因此也是用的堆内存。

下面是转贴
-------------------------
堆和栈的区别

一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其
操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回
收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的
全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另
一块区域。 - 程序结束后由系统释放。
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。

例子

This problem concers the m.o module from Figure 7.5 and the following version of the swap.c function that counts the number of times it has been called:

extern int buf[];

int *bufp0 = &buf[0];
static int *bufp1;

static void incr()
{
    static int count = 0;
    ++count;
}

void swap()
{
    int temp;
    incr();
    bufp1 = &buf[1];
    temp = *bufp0;
    *bufp0 = *bufp1;
    *bufp1 = temp;
}

For each symbol that is defined and referenced in swap.o, indicate if it will have a symbol table entry in the .symtab section in module swap.o. If so, indicate the module that defines the symbol (swap.o or m.o), the symbol type (local, global, or extern), and the section (.text, .data, or .bss) it occupies in that module.

分析

data 是静态初始化的数据段和全局变量段。上面符合这一条件的是 buf*bufp0

*bufp1 虽然是 static 的,但是没有初始化,所以放在 bss 代码段(没有初始化的全局变量)。

而 count 是局部静态变量,在 bss 段。swap 和 incr 是函数,固定不变,在 text 段。

Answer

Symbol swap.o .symtab entry? Symbol type Module where defined Section
buf Y Global m.o .data
bufp0 Y Global swap.o .data
bufp1 Y Local swap.o .bss
swap Y Global swap.o .text
temp N -- -- --
incr Y Local swap.o .text
count Y Local swap.o .bss

总结

全局的:

extern type xxx; // 取决于定义的地方
static type xxx; // bss(未初始+全局)
type xxx = xxx; // data(已初始+全局)
type function(){} // text (写死)

局部的变量:在栈上。不在文件里。

参考

内存中的堆和栈到底是什么:https://www.jianshu.com/p/52b5a1879aa1

Linux下堆栈结构分析:https://blog.lao-yuan.com/2018/05/29/Linux%E4%B8%8B%E5%A0%86%E6%A0%88%E7%BB%93%E6%9E%84%E5%88%86%E6%9E%90.html

X86-64寄存器和栈帧:https://blog.csdn.net/u013737447/article/details/49154509

发表评论

电子邮件地址不会被公开。 必填项已用*标注