Windows内存技术
在WIN32中,每个应用程序都可“看见”4GB的线性地址空间, 其中最开始的4MB和最后的2GB由操作系统保留,低的2GB为进程的私有空间(如果在Boot.ini文件中使用“/3GB”的开关可以使进程的私有空间增大到3GB,系统空间1GB)。
对于每个进程来讲其虚拟的地址空间是连续的,实际上它们是以页面为单位离散的存在于物理内存中,一些可能被交换到硬盘上的页面文件中,而且还有大部分的空间是未提交(Uncommitted)的。
进程的地址空间是如何划分的
分 区 | x86 32位 Windows | 3 GB用户模式下的x86 32位 Windows | x64 64位 Windows | IA-64 64位 Windows |
空指针 | 0x00000000 0x0000FFFF | 0x00000000 0x0000FFFF | 0x00000000’00000000 0x00000000’0000FFFF | 0x00000000’00000000 0x00000000’0000FFFF |
用户模 | 0x00010000 0x7FFEFFFF | 0x00010000 0xBFFEFFFF | 0x00000000’00010000 0x000007FF’FFFEFFFF | 0x00000000’00010000 0x000006FB’FFFEFFFF |
64 KB禁入分区 | 0x7FFF0000 0x7FFFFFFF | 0xBFFF0000 0xBFFFFFFF | 0x000007FF’FFFF0000 0x000007FF’FFFFFFFF | 0x000006FB’FFFF0000 0x000006FB’FFFFFFFF |
内核模 | 0x80000000 0xFFFFFFFF | 0xC0000000 0xFFFFFFFF | 0x00000800’00000000 0xFFFFFFFF’FFFFFFFF | 0x000006FC’00000000 0xFFFFFFFF’FFFFFFFF |
CPU体系结构、对应的用户模式可用地址区间以及分区的大小
CPU体系结构 | 用户模式分区的可用地址区间 | 用户模式分区的大小 |
x86(普通) | 0x00010000 → 0x7FFEFFFF | ~2 GB |
x86w/3GB | 0x00010000 → 0xBFFEFFFF | ~3 GB |
x64 | 0x00000000’00010000 → 0x000007FF’FFFEFFFF | ~8192 GB |
IA-64 | 0x00000000’00010000 → 0x000006FB’FFFEFFFF | ~7152 GB |
C++程序是由一下几部分组成:
(1) 正文段 (.text)
程序代码就存储在.text 段,这是由CPU执行的机器指令部分。通常,正文段是可共享的,所以即使是经常执行的程序(如文本编辑程序、C编译程序、shell等)在存储器中也只需有一个副本,另外,正文段常常是只读的,以防止程序由于意外事故而修改其自身的指令。
当你在链接定位文件中将该符号放置在代码段后,那么该符号表示的值就是代码段大小,编译连接时,该符号所代表的值会自动代入到源程序中。
(2) 只读数据段 .rdata
只读数据是在程序运行期间不能被修改的,程序只能读取这些数据,因此编译器把他们组织在一起存储在只读数据段。
(3) 初始化数据段.data
data端用于存储经过初始化的数据,包括带初值的全局变量和static变量,都存储在data区。data段的起始位置也是由连接定位文件所确定,大小在编译连接时自动分配,和程序大小没有关系,但和程序使用到的全局变量,常量数量相关。
(3) 未初始化数据段.bss
bss这一名称来源于早期汇编程序的一个操作符意思是Block Started by Symbol(由符号开始的块),通常是指用来存放程序中未初始化的全局变量的一块内存区域,在程序载入时由内核清0。 BSS段属于静态内存分配。它的初始值也是由用户自己定义的连接定位文件所确定,用户应该将它定义在可读写的RAM区内,源程序中使用malloc分配的内存并不是这一块,它不是根据data大小确定,主要由程序中同时分配内存最大值所确定,不过如果超出了范围,也就是分配失败,可以等空间释放之后再分配。
(4) 栈stack
stack是用来存储局部变量以及每次函数调用时所需保存的信息。每次函数调用时,其返回地址、以及调用者的环境信息(例如某些机器寄存器)都存放在栈中。然后,新被调用的函数在栈上为其自动和临时变量分配存储空间。通过以这种方式使用栈, C函数可以递归调用。stack是一种“后进先出”(Last In First Out,LIFO)的数据结构,这意味着最后放到栈上的数据,将会是第一个从栈上移走的数据。对于哪些暂时存贮的信息,和不需要长时间保存的信息来说,LIFO这种数据结构非常理想。在调用函数或过程后,系统通常会清除栈上保存的局部变量、函数调用信息及其它的信息。栈另外一个重要的特征是,它的地址空间“向下减少”,即当栈上保存的数据越多,栈的地址就越低。栈(stack)的顶部在可读写的RAM区的最后。
(5) 堆heap
编译器通常在堆中进行动态内存分配。
由于历史上形成的惯例,堆位于非初始化数据段顶和栈底之间。堆是“先进先出”(First In first Out,FIFO)数据结构。它只允许在堆的一端插入数据,在另一端移走数据。堆的地址空间“向上增加”,即当堆上保存的数据越多,堆的地址就越高。
数据存储
(1) 常量
对于整型常量和字符型常量,由于不需要写操作,编译器会将其直接编译在代码之中,因此不需要存储。正文段 (.text)代码段
对于字符串常量,编译器将其放入只读数据端.rdata,同事对于相同的字符串常量,编译器会优化并只存储一次。只读数据段(.rdata) 数据段
(2) 变量
- 全局变量
未初始化的,存储于.bss ;未初始化数据段(.bss) 数据段
初始化的,存储于.data ; 初始化数据段(.data) 数据段
- 静态变量
未初始化的,存储于.bss ;未初始化数据段(.bss) 数据段
初始化的,存储于.data ; 初始化数据段(.data) 数据段
-自动变量
局部变量存储于stack ;
动态分配的内存,存储于heap。
- 寄存器变量
存储位置在CPU寄存器内。