内存模型

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