内存模型
Heap(堆)
程序运行的时候,操作系统会给它分配一段内存,用来储存程序和运行产生的数据。在程序运行过程中,对于动态的内存占用请求,因为用户主动请求而划分出来的内存区域,叫做 Heap(堆),堆从低地址向高地址生长,堆不会自动消失,需要手动释放或者被垃圾回收机制回收
Stack(栈)
除了 Heap 以外,其他的内存占用叫做 Stack(栈)。简单说,Stack 是由于函数运行而临时占用的内存区域。当程序执行函数时,会在内存中建立一个帧,所有这个函数的内部变量都会保存在这个帧里,函数运行结束后,就会回收这个帧的空间。当一个函数运行过程中调用了另外一个函数时,系统也会为另一个函数创建他的帧,储存他的内部变量,所以一般调用栈有多少层就有多少帧。等被调用的函数运行结束后,他的帧就会被回收,系统就回到刚刚主函数中断的位置继续运行,通过这样的机制就实现了函数的层层调用。所有帧都储存在栈中,栈的特点是先进后出,从内存高地址向低地址生长
CPU指令
汇编程序例子
_add_a_and_b:
push %ebx
mov %eax, [%esp+8]
mov %ebx, [%esp+12]
add %eax, %ebx
pop %ebx
ret
_main:
push 3
push 2
call _add_a_and_b
add %esp, 8
ret
push
push指令用于将运算子放入栈中 例子中的push的作用就是将3写入_main的帧中 push会先取出ESP里的地址,将其减去4字节,然后把新地址写入ESP,用减法是因为栈从高地址向低地址生长,4个字节是因为3是int类型大小为4字节。得到新地址后,3就会写入这个地址开始的4个字节
call
call指令用来调用函数,例子中call指令执行时程序就回去寻找_add_a_and_b标签,并为_add_a_and_b建立一个新的帧,然后开始执行_add_a_and_b的代码
mov
mov用于将第二个运算子的值写入到第一个运算子里 例子中 mov %eax, [%esp+8] 的作用就是将ESP寄存器中的地址加上8个字节获得的新地址中对应的值写入到EAX寄存器当中
add
add指令的作用是把两个值相加,把结果存到第一个运算子内
pop
pop指令用来取出栈中最近写入的一个值,即最低地址的值,并把这个值写入到运算子指定位置,pop指令还会将ESP寄存器内的地址加4,即回收4个字节
ret
用于终止当前函数的执行,将运行权交还上层函数,回收当前函数的帧
参考链接
https://www.ruanyifeng.com/blog/2018/01/assembly-language-primer.html