程序内存空间布局以及对栈顶,栈底的理解

一、程序的内存的空间布局:

栈 

由编译器自动分配释放管理。局部变量及每次函数调用时返回地址、以及调用者的环境信息(例如某些机器寄存器)都存放在栈中。新被调用的函数在栈上为其自动和临时变量分配存储空间。通过以这种方式使用栈,C函数可以递归调用。

需要由程序员分配释放管理,若程序员不释放,程序结束时可能由OS回收。通常在堆中进行动态存储分配。

 非初始化数据段

通常将此段称为b s s段,这一名称来源于早期汇编程序的一个操作符,意思是“block started by symbol(由符号开始的块)”,未初始化的全局变量和静态变量存放在这里。在程序开始执行之前,内核将此段初始化为0。函数外的说明:long sum[1000] ; 使此变量存放在非初始化数据段中。

初始化的数据

通常将此段称为数据段,它包含了程序中需赋初值的变量。初始化的全局变量和静态变量存放在这里。例如,C程序中任何函数之外的说明:int maxcount = 99; 使此变量以初值存放在初始化数据段中。

正文段

C P U执行的机器指令部分。通常,正文段是可共享的,所以即使是经常环境指针环境表环境字符串执行的程序(如文本编辑程序、C编译程序、s h e l l等)在存储器中也只需有一个副本,另外,正文段常常是只读的,以防止程序由于意外事故而修改其自身的指令。

size命令可以看到一个程序的正文段(text)、数据段(data)、非初始化数据段(bss)及文件长度.

生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。

二、程序栈是什么样

/*程序执行/函数调用过程:

函数调用时:

建立新栈帧,传递参数,修改“当前代码行”;

函数退出时:

删除栈帧,处理返回值,修改“当前代码行”

*/

过程调用 
call   首先将被调函数的参数入栈,最后是返回地址入栈,再跳到被调函数起始地址
leave  准备返回时的桢栈 : 令栈指针指向先指向当前桢的起始处(这里保存的是调用者桢的起始地),出栈(桢指针重置为调用者桢的起始;且栈指针指向返回地址)

       等同于 :   
       movl %ebp,%esp 
       popl %ebp

ret    (栈指针指向返回地址)出栈并跳到那个位置(返回地址).

寄存器

(1)esp:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。
(2)ebp:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。(ebp在当前栈帧内位置固定,故函数中对大部分数据的访问都基于ebp进行)
(3)eip:指令寄存器(extended instruction pointer),其内存放着一个指针,该指针永远指向下一条等待执行的指令地址。 可以说如果控制了EIP寄存器的内容,就控制了进程——我们让eip指向哪里,CPU就会去执行哪里的指令。eip可被jmp、call和ret等指令隐含地改变(事实上它一直都在改变)(ret指令就是把当前栈顶保存的返回值地址 弹到eip中)

函数栈帧的大小并不固定,一般与其对应函数的局部变量多少有关。函数运行过程中,其栈帧大小也是在不停变化的。eax一般用来保存函数的返回值,记住esp是栈顶指针寄存器,ebp是栈底指针寄存器。

ESP 中的指针将一直指向这个新位置, 所以 ESP 中的地址数据是动态的.