内存的管理
- 进程空间Win32的可执行文件在加载后,系统将为它建立一个它自己的虚拟内存空间,即进程空间,其容量达4G(64位系统中将更大,但我想64位的程序应该不叫做Win32程序了吧?)。这4G的空间划分为了几个区域,对于win98和win2000是不尽相同的。
Window2000 Windows98 NULL指针分配分区 0x00000000~0x0000ffff 0x00000000~0x00000fff MS-DOS/Win16兼容分区 无 0x00001000~0x003fffff 用户分区 0x00010000~0x7ffeffff 0x00400000~0x7fffffff 禁止访问分区(64K) 0x7fff0000~0x7fffffff 无 共享(MMF)分区 无 0x80000000~0xbfffffff 内核方式分区 0x80000000~0xffffffff 0xc0000000~0xffffffff
(上表资料来自:《Windows核心编程》)
- NULL指针分区是NULL指针的地址范围。对这个区域的读写企图都将引发访问违规。
- DOS/WIN16分区是98中专门用于16位的DOS和windows程序运行的空间,所有的16位程序将共享这个4M的空间。Win2000中不存在这个分区,16位程序也会拥有自己独立的虚拟地址空间。有的文章中称win2000中不能运行16位程序,是不确切的。
- 用户分区是进程的私有领域,Win2000中,程序的可执行码和其它用户模块均加载在这里,内存映射文件也会加载在这里。Win98中的系统共享DLL和内存映射文件则加载在共享分区中。
- 禁止访问分区只有在win2000中有。这个分区是用户分区和内核分区之间的一个隔离带,目的是为了防止用户程序违规访问内核分区。
- MMF分区只有win98中有,所有的内存映射文件和系统共享DLL将加载在这个地址。而2000中则将其加载到用户分区。
- 内核方式分区对用户的程序来说是禁止访问的,操作系统的代码与此。内核对象是否也驻留在此?我认为应该是的,因为应用程序不能直接访问内核对象。但是关于这点我没找到有关的叙述,不知哪位大侠有确切的依据?另外要说明的是,win98中对于内核分区本也应该提供保护的,但遗憾的是并没有做到,因而98中程序可以访问内核分区的地址空间。
-
进程的内存布局见下图.
=============================================================================== ----------------- 高地址 | | ---> 命令行参数和环境变量 (只读) ----------------- | 栈 | |- - - - - - - -| | | | | | \ / | | | | | | / \ | | | | | |- - - - - - - -| | 堆 | |---------------| | 未初始化的变量| | (bss) | ---> 由 exec 初始化为零 |---------------| | 初始化后的变量| \ |---------------| | | text(数据区)| | 由 exec 从程序中读取 | | / |---------------| 低地址 | 代码区 | |---------------| =============================================================================== (见http://263.aka.org.cn/Lectures/002/Lecture-2.1.6/Lecture-2.1.6/proc.html http://www.csdn.net/Develop/article/15%5C15747.shtm)
-
每一个进程在创建时都分配一个静态存储区域、一个堆和一个栈。
- 静态存储区域用于存储进程的全局变量和静态变量;这个对于理解MFC的theApp对象也挺有帮助的。全局变量和静态变量“从静态存储区域分配。程序在编译的时就已经分配好内存分布,这块内存在程序的整个运行期间都存在。”(见http://www.fanqiang.com/a4/b2/20020719/060200272.html)
- 堆可以动态分配内存。1)“程序在运行的时候用malloc或new从堆上动态申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。”(见http://www.fanqiang.com/a4/b2/20020719/060200272.html)
2)“堆应该是保留的一段内存空间,在进程初始化的时候,系统会提交一部分物理存储。进程的堆的初始大小默认是1MB,这个可以在连接时用/HEAP开关自己控制。物理存贮的提交和回收应该是由堆管理器来管理的。”“堆应该是自动增长的。”“我们说的“自动生长”指的是在预先保留的全部堆空间都不够用的时候,重新来从自由区域保留。”(见http://www.pcvc.net/category/content.asp?sendid=118)
- 每一个函数都可以对栈操作,用于存储参数和局部变量。1)“在执行函数时,函数的参数和函数内的局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。”(见http://www.fanqiang.com/a4/b2/20020719/060200272.html)
2)“(在函数内)声明变量/数组的方式,是在栈中分配内存。”“当调用完此函数返回的时候,栈空间自动被收回,其中的内容也就全部无效了。”(见http://www.pcvc.net/category/content.asp?sendid=114)
例子:(引用朱茂盛提供的)
1)定义函数
char *testfunc()
{
char *pret="return character";
return pret;
}
然后调用
char *pp = testfunc();
otherfunc();
printf(pp);
这样有问题吗?
2)定义函数
char *testfunc()
{
char pret[]="return character";
return pret;
}
然后调用
char *pp = testfunc();
otherfunc();
printf(pp);
这样呢?
分析:1)中虽然变量pret分配在栈中,但"return character"是分配在进程的数据区的,在整个进程的生命期内有效;2)中"return
character"是数组的内容,是分配在栈中的,一旦出了这个函数,就从栈中释放。所以1)中没问题而2)中有问题。 - 有人说:“DLL 可 以 有 自 己 的 数 据 段, 但 是 它 没 有 堆 栈 段, 使 用 的 是 调 用 程 序 的 堆 栈。”(见http://www.2000n.com/02.htm) 也有人说:“DLL有自己的堆栈”。(见http://www.csdn.net/Develop/article/15%5C15747.shtm)
而朱茂盛说:在DLL中的函数里面分配的空间,是不允许在调用DLL的程序里面释放的。这么说来,DLL确实是有自己的堆栈的。
- 线程具有自己的内存布局。“进程中每个线程都有自己的堆栈,这是一段线程创建时保留下的地址区域。”(见http://www.pcvc.net/category/content.asp?sendid=118)
-