转自http://nforcex.blogbus.com/logs/7537173.html
我们在这里拿最典型且开源的Unix/Linux平台举例:
1. 进程
进程:程序的一次动态执行过程,是操作系统管理系统活动的基本单位。从内核的角度看,进程是系统中的一个对象,它对应一个程序的执行流并且是一个资源分配(包括内存和文件等)的单位。
在Linux中,一个进程包括四个部分内容:指令段(正文段):存放程序的CPU指令代码。用户数据段:存放程序所需要的数据。用户堆栈段:程序执行所需要的堆栈空间。系统数据段:操作系统内核内的数据,每个进程对应一套数据,包括页表(page table)和进程控制块(process control block,PCB)。如下图所示:
+--------------------+ 内存低端
| 指令段 |
|----------------------|
| 用户数据段 |
|----------------------|
| 用户堆栈 |
+--------------------+ 内存高端
-----------------------------OS内核kernel32------------------------------
+---------------------|
| 系统数据段 |
|----------------------|
2. 过程、过程调用及函数调用
3. 堆 (heap。软件底层原理,而非数据结构)
堆位于RAM中,是一个通用的内存池,它一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。所有的对象都存储在堆中。堆是动态分配内存的,并且你可以分配使用很大的内存。但是用不好会产生内存泄漏。
4. 栈 (hardware stack。软件底层原理,而非数据结构)
栈存在于RAM中,它由编译器自动分配、释放。栈是动态的,它的存储速度是第二快的。在栈上存取数据比通过指针在堆上存取数据快些。不过它易遭受缓冲区溢出攻击的困扰。
5. 帧栈、帧指针、栈指针
帧栈(stack frame):在内存的程序栈中,为单个过程分配的那部分栈称为帧栈。
如下图所示。栈用来传递过程参数、存储返回信息、保存寄存器以供以后恢复之用,以及用于本地存储。栈底位于内存高地址,栈顶位于内存低地址。加入有2个过程P和Q,前者是调用者,后者是被调用者。Q的参数(实参)放在P的帧栈中。另外,当P调用Q时,P中的返回地址被压入战中,形成P的帧栈的末尾,返回地址(return address)就是当程序从Q返回时应该继续执行的地方。Q的帧栈从保存的栈指针的值(例如,%ebp)开始,后面是保存的其他寄存器的值。
帧指针(frame pointer)&栈指针(stack pointer):帧栈的最顶端是以两个指针定界的,寄存器%ebp作为帧指针,而寄存器%esp作为栈指。当程序执行时,栈指针是可以移动的,因此大多数信息的访问都是相对于帧指针的。
未完待续...
----------------------------------------------------------------------------------------
PS1:堆(heap)和栈(stack)是C/C++编程不可避免会碰到的两个基本概念。首先,这两个概念都可以在讲数据结构的书中找到,他们都是基本的数据结构,虽然栈更为简单一些。在具体的C/C++编程框架中,这两个概念并不是并行的。对底层机器代码的研究可以揭示,栈是机器系统提供的数据结构,而堆则是C/C++函数库提供的。
具体地说,现代计算机(串行执行机制),都直接在代码底层支持栈的数据结构。这体现在,有专门的寄存器指向栈所在的地址,有专门的机器指令完成数据入栈出栈的操作。 这种机制的特点是效率高,支持的数据有限,一般是整数,指针,浮点数等系统直接支持的数据类型,并不直接支持其他的数据结构。因为栈的这种特点,对栈的使用在程序中是非常频繁的。对子程序的调用就是直接利用栈完成的。机器的call指令里隐含了把返回地址推入栈,然后跳转至子程序地址的操作,而子程序中的ret指令则隐含从堆栈中弹出返回地址并跳转之的操作。C/C++中的自动变量是直接利用栈的例子,这也就是为什么当函数返回时,该函数的自动变量自动失效的原因。