内存管理(一)
2010-05-29 11:12
183 查看
一. Windows内存管理体系结构
1.0 进程的虚拟地址空间
对于32位进程虚拟地址空间大小为4GB,64为16EB(exabytes,百亿字节),每个进程都有自己的私有地址空间,要想正常访问数据需要把物理存储器分配或映射到相应的地址空间。
说明:在windows中正在运行的线程看不到属于操作系统本身的内存,这意味着它不能无意间访问到操作系统的数据。
1.1 虚拟地址空间的划分
32和64有同样的分区,不同的是分区尺寸和位置。
a.空指针分配区域
这一分区是为了帮助程序员捕获对空指针的赋值,对这一分区的操作将会引发访问违规。
如:int* pnSomeInteger = (int*) malloc(sizeof(int));
*pnSomeInteger = 5;
如果分配函数不能发现足够的内存来满足请求,返回NULL。然而,这个代码没有检查那样一种可能-假设分配成功并且进行访问在地址00000000上的内
存。因为这个地址空间的区域是禁止访问的,内存访问违例发生并且进程被终止。(违例和终止)有助于开发者发现他们程序的bug。注意没有任何办法可以使我们分配到在这个地址范围的虚拟内存即使你使用
win32api。
b.用户模式分区
这个分区是进程的地址空间驻扎的地方。有用的地址范围和用户模式分区的大致地址依赖于处理器架构,显示在下表:
一个进程不能使用指针用任何方式读取写入访问另一个进程的驻扎在这个分区的数据。所有程序,这个分区是进程的大量数据被维护的地方。
说明: 在windows中,所有的EXE和DLL都会载入到这里的不同地址(虽然可能性很小)。系统同时会把该进程可以访问的所有内存映射文件映射到这一分区。
进程可用地址空间的量少于总量的一半。内核模式分区需要整个地址空间的上半部分,系统需要这个空间为了内核代码,设备驱动代码,设备输入输出缓存缓冲区,非分页池分配,进程页面表,等待。事实上,微软已经将内核压缩到这2GB空
间中。在64位windows,内核终于得到它真正需要的空间。
c.在x86windows得到一个更大的用户模式区域
一些应用程序,诸如微软的sql服务程序,将会从用户模式大于2Gb获得益处为了增强效能和可测量性通过有更多的程序数据可以寻址。所以
x86windows版本提供升级用户模式分区到3G的方式。为了让所有进程使用大于2G用户模式分区和小于1G内核模式分区,你需要对windows中的配置数据(boot configuration data简称BCD)进行设定并且重启机器(详见MSDN)
一个解决方案允许应用程序工作在一个大的用户模式的地址空间环境。当系统将要运行一个应用程序,它检查看是否应用程序使用了/LARGEANNRESSAWARE开关。如果程序没有使用这个开关,操作系统会保留用户模式分区2GB以上到内核模式开始处。
注意所有的被内核需要的数据和代码被紧紧的压缩到2G分区。所以减少内核地址空间少于2G减少了线程、栈其他系统能够创造的资源的数量。除外,系统能够使
用最大仅仅64G内存,不象128G最大可用当内核默认的2G被使用的时候。)
用3GB开关:http://msdn.microsoft.com/en-us/library/ff556232.aspx
说明:操作系统在创造进程的地址空间时检查可执行文件的这个开关标志。对于动态连接库则被系统忽略。因而动态连接库必须正确编写。
d.在64位windows在得到2GB用户模式分区
e.内核模式分区
驻留在这一分区的任何东西为所有的进程所共有。该分区的所有代码和数据都被保护了起来,如果应用程序试图读取或改写这里的内存地址,会引发访问违规。
1.2 地址空间的区域
系统创建进程并赋予它地址空间时,可用地址空间中的大部分都市闲置的(free)或尚未分配的(unallocated),为了使用我们必须调用VirtualAlloc来预订(reserving).当应用程序预订地址空间的时候系统会确保区域的起始地址正好是分配粒度(allocation granularity)的整数倍,不同的CPU平台而有所不同,目前所有的CPU都使用64KB。
应用程序预订地址空间时会确保区域大小正好是系统页面大小的整数倍,页面是一个内存单元,系统通过它来管理内存。x86和x64使用的页面大小为4KB而IA-64为8KB.
说明:有时系统会以应用程序的名义来预订地址空间区域(如PEB,TEB)。虽然系统规定应用程序在预订地址空间区域时起始地址必须是分配粒度的整数倍,但系统自己却不存在同样的限制,非常有可能出现系统为PEB,TEB预订的区域地址并非64KB的整数倍。但是这些区域仍然必须是CPU页面大小的整数倍。
如果应用程序试图预订一块大小为10KB的地址空间区域,那么系统会自动取页面大小的整数倍,这意味着在x86和x64上会预订12KB的区域,在IA-64中16KB.
当应用程序不在需要预订区域时调用VirtualFree来释放该区域。
1.3 给区域调拨物理存储器
为了使用预订的地址空间区域,我们还必须分配物理存储器,并将其映射到所预订的区域。这个过程被称为调拨(committimg)物理存储器。物理存储器始终都以页面为单位来调拨(使用VirtualAlloc).当我们调拨物理存储器给区域时,并不需要给整个区域都调拨。当程序不需要时我们应该撤销调拨(decommitting)物理存储器,通过VirtualFree函数来完成。
1.4物理存储器和页交换文件
当应用程序调用VirtualAlloc函数来把物理存储器调拨给地址空间区域时,该空间实际上是从硬盘的页交换文件分配得到的。
当用户要求执行一个应用程序的时候。系统会打开该应用程序对应的EXE文件并计算出应用程序的代码和数据的大小。然后系统会预订一块地址空间,并注明与该区域相关联的物理存储器就是EXE文件本身。系统并没有从页交换文件中分配空间,而是将EXE文件的实际内容(或文件映像,即file image)用作程序预订的地址空间区域。这样一来,不但载入程序非常快,而且页交换文件也可以保持一个合理的大小。
当把一个程序位于硬盘上的文件映像(即一个EXE或DLL文件)用作地址空间区域对应的物理存储器时,我们称为这个文件映像为内存映射文件(memory mapped file)。当载入一个EXE或DLL时系统会自动预订地址空间区域并把文件映像映射到该区域。但系统也提供一组函数可以让开发人员把数据文件映射到地址空间。
windows可以使用多个页交换文件。如果多个页交换文件位于不同的物理硬盘上,那么系统就可以运行的更快,这是因为系统能同时写入多个硬盘。
说明:当windows从软盘载入EXE或DLL文件时,系统会把整个文件从软盘复制到内存中。此外,系统还会从页交换文件中分配足够的存储空间来存放文件映像。只有当系统需要把一个页面换出内存,而页面又包含该文件映像的一部分时,系统才会写入页交换文件。如果系统的内存负载很轻,那么文件总是从内存中直接运行。Microsoft必须让在软盘上执行的文件以这种方式运行,因为只有这样安装程序才能正常运行。通常,安装程序会从第一张软盘开始运行,然后再换下一张,由于系统已经将文件复制到内存中(并以页交换文件为后备存储器),因而安装不会遇到问题。除非映像文件是用/SWAPRUN:CD或SWAPRUN:NET开关链接的,否则系统不会把位于其他可移动媒介(比如:光盘或网络驱动器)上的映射文件复制到内存中。
1.5页面保护属性
windows的数据执行保护(Data Execution Protection,后面简称DEP)特性提供了对一些恶意软件将代码写入到用于数据的内存区域(比如线程栈上)而让应用程序执行恶意代码的防护。如果启用了DEP,那么只有对那些真正需要执行的代码的内存区域,操作系统才会使用PAGE_EXECUTE_*保护属性。如果CPU试图执行某个页面中的代码,而该页又没有PAGE_EXECUTE_*保护属性,那么CPU会抛出访问违规异常。DEP白皮书:http://go.microsoft.com/fwlink/?linkid=28022 。
1.6 写时复制
PAGE_WRITECOPY和PAGE_EXECUTE_WRITECOPY属性目的是为了节省内存和页交换文件的使用。windows支持一种机制,允许两个或两个以上的进程共享同一块存储器,这样极大的提升了系统的性能。但如果某一个应用程序修改并写入一个存储页,那么会导致混乱。为了避免此,操作系统会给共享的存储页指定写时复制属性。当系统把一个exe或dll映射到一个地址空间的时候,系统会计算有多少个页面时可写的(代码页面被标记PAGE_EXECUTR_READ,数据页面被标记PAGE_READWRITE )然后系统会从页交换文件中分配存储空间来容纳这些可写页面。除非应用程序真的写入可写页面,否则不会用到页交换文件中的存储器。
当线程试图写入一个共享页面时,系统会介入并执行下面的操作。
1.7 数据对齐的重要性。
1.0 进程的虚拟地址空间
对于32位进程虚拟地址空间大小为4GB,64为16EB(exabytes,百亿字节),每个进程都有自己的私有地址空间,要想正常访问数据需要把物理存储器分配或映射到相应的地址空间。
说明:在windows中正在运行的线程看不到属于操作系统本身的内存,这意味着它不能无意间访问到操作系统的数据。
1.1 虚拟地址空间的划分
分区 | X86 32 windows | X86 32 3G 用户模式 | X64 64 windows | Ia64 64 windows |
空指针分配 | 0x00000000 | 0x00000000 | 0x00000000'00000000 | 0x00000000'00000000 |
0x0000FFFF | 0x0000FFFF | 0x00000000'0000FFFF | 0x00000000'0000FFFF | |
用户模式 | 0x00010000 | 0x00010000 | 0x00000000'00010000 | 0x00000000'00010000 |
0x7FFEFFFF | 0xBFFEFFFF | 0x000007FF'FFFEFFFF | 0x000006FB'FFFEFFFF | |
64k禁止访问 | 0x7FFF0000 | 0xBFFF0000 | 0x000007FF'FFFF0000 | 0x000006FB'FFFF0000 |
0x7FFFFFFF | 0xBFFFFFFF | 0x000007FF'FFFFFFFF | 0x000006FB'FFFFFFFF | |
内核模式 | 0x80000000 | 0xC0000000 | 0x00000800'00000000 | 0x000006FC'00000000 |
0xFFFFFFFF | 0xFFFFFFFF | 0xFFFFFFFF'FFFFFFFF |
a.空指针分配区域
这一分区是为了帮助程序员捕获对空指针的赋值,对这一分区的操作将会引发访问违规。
如:int* pnSomeInteger = (int*) malloc(sizeof(int));
*pnSomeInteger = 5;
如果分配函数不能发现足够的内存来满足请求,返回NULL。然而,这个代码没有检查那样一种可能-假设分配成功并且进行访问在地址00000000上的内
存。因为这个地址空间的区域是禁止访问的,内存访问违例发生并且进程被终止。(违例和终止)有助于开发者发现他们程序的bug。注意没有任何办法可以使我们分配到在这个地址范围的虚拟内存即使你使用
win32api。
b.用户模式分区
这个分区是进程的地址空间驻扎的地方。有用的地址范围和用户模式分区的大致地址依赖于处理器架构,显示在下表:
架构 | 可用用户模式分区地址范围 | 可用用户模式分区尺寸 |
x 86 (普通) | 0x00010000 è 0x7FFEFFFF | ~2 GB |
x 86 w/3 GB | 0x00010000 è 0xBFFFFFFF | ~3 GB |
x 64 | 0x00000000'00010000 è 0x000007FF'FFFEFFFF | ~8192 GB |
IA-64 | 0x00000000'00010000 è 0x000006FB'FFFEFFFF | ~7152 GB |
说明: 在windows中,所有的EXE和DLL都会载入到这里的不同地址(虽然可能性很小)。系统同时会把该进程可以访问的所有内存映射文件映射到这一分区。
进程可用地址空间的量少于总量的一半。内核模式分区需要整个地址空间的上半部分,系统需要这个空间为了内核代码,设备驱动代码,设备输入输出缓存缓冲区,非分页池分配,进程页面表,等待。事实上,微软已经将内核压缩到这2GB空
间中。在64位windows,内核终于得到它真正需要的空间。
c.在x86windows得到一个更大的用户模式区域
一些应用程序,诸如微软的sql服务程序,将会从用户模式大于2Gb获得益处为了增强效能和可测量性通过有更多的程序数据可以寻址。所以
x86windows版本提供升级用户模式分区到3G的方式。为了让所有进程使用大于2G用户模式分区和小于1G内核模式分区,你需要对windows中的配置数据(boot configuration data简称BCD)进行设定并且重启机器(详见MSDN)
一个解决方案允许应用程序工作在一个大的用户模式的地址空间环境。当系统将要运行一个应用程序,它检查看是否应用程序使用了/LARGEANNRESSAWARE开关。如果程序没有使用这个开关,操作系统会保留用户模式分区2GB以上到内核模式开始处。
注意所有的被内核需要的数据和代码被紧紧的压缩到2G分区。所以减少内核地址空间少于2G减少了线程、栈其他系统能够创造的资源的数量。除外,系统能够使
用最大仅仅64G内存,不象128G最大可用当内核默认的2G被使用的时候。)
用3GB开关:http://msdn.microsoft.com/en-us/library/ff556232.aspx
说明:操作系统在创造进程的地址空间时检查可执行文件的这个开关标志。对于动态连接库则被系统忽略。因而动态连接库必须正确编写。
d.在64位windows在得到2GB用户模式分区
e.内核模式分区
驻留在这一分区的任何东西为所有的进程所共有。该分区的所有代码和数据都被保护了起来,如果应用程序试图读取或改写这里的内存地址,会引发访问违规。
1.2 地址空间的区域
系统创建进程并赋予它地址空间时,可用地址空间中的大部分都市闲置的(free)或尚未分配的(unallocated),为了使用我们必须调用VirtualAlloc来预订(reserving).当应用程序预订地址空间的时候系统会确保区域的起始地址正好是分配粒度(allocation granularity)的整数倍,不同的CPU平台而有所不同,目前所有的CPU都使用64KB。
应用程序预订地址空间时会确保区域大小正好是系统页面大小的整数倍,页面是一个内存单元,系统通过它来管理内存。x86和x64使用的页面大小为4KB而IA-64为8KB.
说明:有时系统会以应用程序的名义来预订地址空间区域(如PEB,TEB)。虽然系统规定应用程序在预订地址空间区域时起始地址必须是分配粒度的整数倍,但系统自己却不存在同样的限制,非常有可能出现系统为PEB,TEB预订的区域地址并非64KB的整数倍。但是这些区域仍然必须是CPU页面大小的整数倍。
如果应用程序试图预订一块大小为10KB的地址空间区域,那么系统会自动取页面大小的整数倍,这意味着在x86和x64上会预订12KB的区域,在IA-64中16KB.
当应用程序不在需要预订区域时调用VirtualFree来释放该区域。
1.3 给区域调拨物理存储器
为了使用预订的地址空间区域,我们还必须分配物理存储器,并将其映射到所预订的区域。这个过程被称为调拨(committimg)物理存储器。物理存储器始终都以页面为单位来调拨(使用VirtualAlloc).当我们调拨物理存储器给区域时,并不需要给整个区域都调拨。当程序不需要时我们应该撤销调拨(decommitting)物理存储器,通过VirtualFree函数来完成。
1.4物理存储器和页交换文件
当应用程序调用VirtualAlloc函数来把物理存储器调拨给地址空间区域时,该空间实际上是从硬盘的页交换文件分配得到的。
当用户要求执行一个应用程序的时候。系统会打开该应用程序对应的EXE文件并计算出应用程序的代码和数据的大小。然后系统会预订一块地址空间,并注明与该区域相关联的物理存储器就是EXE文件本身。系统并没有从页交换文件中分配空间,而是将EXE文件的实际内容(或文件映像,即file image)用作程序预订的地址空间区域。这样一来,不但载入程序非常快,而且页交换文件也可以保持一个合理的大小。
当把一个程序位于硬盘上的文件映像(即一个EXE或DLL文件)用作地址空间区域对应的物理存储器时,我们称为这个文件映像为内存映射文件(memory mapped file)。当载入一个EXE或DLL时系统会自动预订地址空间区域并把文件映像映射到该区域。但系统也提供一组函数可以让开发人员把数据文件映射到地址空间。
windows可以使用多个页交换文件。如果多个页交换文件位于不同的物理硬盘上,那么系统就可以运行的更快,这是因为系统能同时写入多个硬盘。
说明:当windows从软盘载入EXE或DLL文件时,系统会把整个文件从软盘复制到内存中。此外,系统还会从页交换文件中分配足够的存储空间来存放文件映像。只有当系统需要把一个页面换出内存,而页面又包含该文件映像的一部分时,系统才会写入页交换文件。如果系统的内存负载很轻,那么文件总是从内存中直接运行。Microsoft必须让在软盘上执行的文件以这种方式运行,因为只有这样安装程序才能正常运行。通常,安装程序会从第一张软盘开始运行,然后再换下一张,由于系统已经将文件复制到内存中(并以页交换文件为后备存储器),因而安装不会遇到问题。除非映像文件是用/SWAPRUN:CD或SWAPRUN:NET开关链接的,否则系统不会把位于其他可移动媒介(比如:光盘或网络驱动器)上的映射文件复制到内存中。
1.5页面保护属性
windows的数据执行保护(Data Execution Protection,后面简称DEP)特性提供了对一些恶意软件将代码写入到用于数据的内存区域(比如线程栈上)而让应用程序执行恶意代码的防护。如果启用了DEP,那么只有对那些真正需要执行的代码的内存区域,操作系统才会使用PAGE_EXECUTE_*保护属性。如果CPU试图执行某个页面中的代码,而该页又没有PAGE_EXECUTE_*保护属性,那么CPU会抛出访问违规异常。DEP白皮书:http://go.microsoft.com/fwlink/?linkid=28022 。
1.6 写时复制
PAGE_WRITECOPY和PAGE_EXECUTE_WRITECOPY属性目的是为了节省内存和页交换文件的使用。windows支持一种机制,允许两个或两个以上的进程共享同一块存储器,这样极大的提升了系统的性能。但如果某一个应用程序修改并写入一个存储页,那么会导致混乱。为了避免此,操作系统会给共享的存储页指定写时复制属性。当系统把一个exe或dll映射到一个地址空间的时候,系统会计算有多少个页面时可写的(代码页面被标记PAGE_EXECUTR_READ,数据页面被标记PAGE_READWRITE )然后系统会从页交换文件中分配存储空间来容纳这些可写页面。除非应用程序真的写入可写页面,否则不会用到页交换文件中的存储器。
当线程试图写入一个共享页面时,系统会介入并执行下面的操作。
1.7 数据对齐的重要性。
相关文章推荐
- 高质量C++编程_第7章_内存管理(1)
- Linux之内存管理mm_struct
- iPhone/Mac Objective-C内存管理教程和原理剖析(二)口诀与范式
- 内存管理的概念
- iPhone/Mac Objective-C内存管理教程和原理剖析(一)基本原理
- 深入理解ngx_align_ptr宏及内存管理
- 内存管理 与 银行
- Linux内存管理之kmalloc 与 __get_free_page()
- Objective-C的内存管理(一)黄金法则的理解
- windows内存管理的机制以及优缺点
- JVM内存管理:深入垃圾收集器与内存分配策略
- 【C语言常识】内存管理详解
- ObjC 内存管理
- spark 内存管理详解 及 性能调优
- Android内存管理策略的优化
- Spark 内存管理概述
- iPad app应用开发系列文章之三 -- iOS的多核编程和内存管理
- Spark 内存管理之StaticMemoryManager
- 深入理解Linux内存管理机制(一)
- C++内存管理------>以对象管理资源(Effective C++)