转自:
原文参见:
一)概述
.堆栈是一个用户空间的内存区域,进程使用堆栈作为临时。
.堆栈中存放的是函数中的局部变量,在函数的生命周期中可以将变量压入堆栈,编译器需要确保堆栈指针在函数退出前恢复到初始位置,也就是说,内存是自动分配和释放的,C/把在堆栈中的局部变量当作automatic存储,并使用auto关键字,这是局部变量的默认存储方式,所以现在没有人用auto关键词.
.与动态存储(auto)相对映的静态存储(static),也就是用static定义的局部变量,它不用堆栈来存储,而是使用数据段来存储(也就是说它的生命周期在整个程序运行期间)。
.堆栈的基地址位于用户空间的最高虚拟地址附近,并从那里向下延伸。
.一个进程开始时,堆栈的最大值就不能改变,如果占用的空间超过了堆栈大小,那么就会导致堆栈溢出。
二)进程的内存组织形式
进程被分为三个区域:文本、数据和堆栈。
1)文本区域:
文本区域也叫做代码段,是由程序确定的,
它包括代码(指令)和只读数据,该区域通常被标记为只读,任何对其写入的操作会导致段错误.
2)数据区域(静态内存分配(static)):
数据区域也叫做数据段,
它包括已初始化和未初始化的数据,静态变量存储在这个区域中,它的大小可以用系统调用brk(2)来改变。
详细了解数据区域:
分成初始化为非零的数据区、BSS和堆(Heap)三个区域。
初始化非零数据区域一般存放静态非零数据和全局的非零数据,属于静态内存分配(全局变量,static修饰的变量);
BSS(Block Started by Symbol)区域(都初始化为0了)一般存放未初始化的全局数据(默认值为0)和未初始化的静态数据(默认值为0),属于静态内存分配(全局变量、static修饰的变量);
堆区域一般存放运行时动态分配的内存空间,其大小不固定,可动态扩张或缩减。当调用malloc等函数分配内存时,新分配的内存被动态添加到堆上;当调用free等函数释放内存时,被释放内存从堆中被剔除。
代码段和数据段之间有明确的分隔,
但是数据段和堆栈段之间没有,而且栈是向下增长,堆是向上增长的,因此理论上堆和栈会“增长到一起”,操作系统的内存管理功能需要防止这样的错误发生。
3)堆栈区域(动态内存分配auto,默认,所以不用关键字auto):
堆栈区域也叫堆栈段,
它用于给局部变量动态分配空间,同样函数传递参数和函数返回值也要用到堆栈.
堆栈也可向下增长(向内存低地址)也可以向上增长,这依赖于具体的实现,
通常都是向下增长的,而SP(堆栈指针)也是指向堆栈的最后地址.
4)内存的分配区域:
根据前面所述,堆栈是位于最高虚拟地址附近,而数据段则位于堆栈段之后,最后是代码段。
也就是:
低地址 代码段 或 高地址 堆栈段
数据段 数据段
高地址 堆栈段 低地址 代码段
三)堆栈着色
当两个线程或进程使用相同的堆栈虚拟地址时,它们会争夺同一个cache行,导致竞争和降级行为.
堆栈着色的技术使每一个进程的基址都不相同,通过随机分配堆栈基址,多个进程会使用不同的cache行来避免.
四)堆栈的限制
堆栈空间的最大值是由setrlimit系统调用确定的,也可以通过bash内建的ulimit命令来设定和查看.
例如:
查看当前可使用的最大堆栈(以KB为单位)
ulimit -s
8192 //栈的大小默认是8M
设定为最大的使用堆栈为15KB
ulimit -s 15
此时执行ls将会得到一个段错误.
ls -l /etc/
total 1040
Segmentation fault
通过用strace跟踪ls命令,将发现有如下的系统调用
getrlimit(RLIMIT_STACK, {rlim_cur=15*1024, rlim_max=15*1024}) = 0
说明当前可用的堆栈空间,已经不足以运行strace命令了.
五)常驻内存和锁定内存
常驻内存专指存储在RAM中的内存部分,不包括存储在区和未存储的进程的内存.
锁定内存是常驻内存的子集,它指被进程明确地锁定到RAM的虚拟内存中,不能用于,并一直常驻于RAM中.