关闭

第13章windows内存体系架构上

标签: windows操作系统内存
213人阅读 评论(0) 收藏 举报

操作系统的内存体系架构是理解操作系统如何运作的重要内容,系统如何管理内存的深入理解能够帮助我们更好地理解应用程序之间如何共享数据,系统在内存中哪里存储信息,我们编写的程序如何更加高效的运行。

1、进程的虚拟地址空间

  每一个进程都有一个自己的虚拟地址空间,对于32bit进程的地址空间是4GB,32bit指针可以在0x0000 0000到0xFFFF FFFF之间取值,也就是说该指针可以取4294967296之间的值。对于64bit进程,它的地址空间是16EB,64-bit pointer能够从0x00000000 00000000到0xFFFFFFFF FFFFFFFF,64位指针可以有18446744073709551616个值。
 因为每一个进程都有自己的私有地址空间,因此属于该进程的线程运行时能够存取只属于该进程的内存空间。同时属于其他进程的内存空间被隐藏,对该线程来说不可存取。
 Note:In Windows, the memory belonging to the operating system itself is also hidden from the running thread, which means that the thread cannot accidentally access the operating system's data. 在windows中属于操作系统本身的内存空间对于运行的线程也是隐藏的,这意味着线程不能够存取操作系统数据。
  现在讨论的是虚拟地址空间不是物理存储空间,物理存储需要被映射到地址空间在你能够成功存取数据之前。

2、虚拟地址空间的分区

  每一个进程的虚拟地址空间被划分成各个区,地址空间的分区根据操作系统的基本实现方法来进行的,不同的Windows内核,其分区也略有不同。
 
进程地址空间的分区
分区    x86 32-Bit Windows x86 32-Bit Windows with 3GB User-Mode x64 64-Bit Windows IA-64 64-Bit Windows

  从图中可以得到,32bit的Windows内核和64-bit的Windows内核有几乎相近的内存分区,不同的地方在于分区大小和位置。
  1. 空指针赋值分区:从0x0000 0000到0x0000FFFF地址空间的分区被设定为帮助程序员捕获NULL指针的赋值,如果你的进程中的一个线程试图读写数据到这个内存分区(内存空间),则读写发生异常。如果你的进程中的线程试图读取该分区的地址空间的数据,或者将数据写入该分区的地址空间,那么C P U就会引发一个访问违规。保护这个分区是极其有用的,它可以帮助你发现 N U L L指针的分配情况。
               C / C + +程序中常常不进行严格的错误检查。例如,下面这个代码就没有进行任何错误检查:
                
int* pnSomeInteger = (int*) malloc(sizeof(int));
*pnSomeInteger=5;

   如果malloc不能找到足够的内存来满足需要,它就返回NULL。但是,该代码并不检查这种可能性,它认为地址的分配已经取得成功,并且开始访问0x00000000地址的内 存。由于这个分区的地址空间是禁止进入的,因此就会发生内存访问违规现象,同时该进程将终止运行。这个特性有助于编程员发现应用程序中的错误。
        2.用户模式分区:该分区是在用户地址空间中,用户模式分区的范围和大小取决于CPU架构。

一个进程不能读取、写入、或者以任何方式访问驻留在该分区中的另一个进程的数据,对于所有的应用程序,该分区维护着进程的绝大部分数据。在Windows中,所有的.exe和动态链接库DLL模块被加载到该分区,系统还可以在这个内存分区中映射进程可以访问的所有内存映射文件,所有进程共享DLL均位于相同的虚拟地址中。
     3.内核分区:该分区是操作系统代码驻留区,线程调度、内存管理、文件系统支持、网络支持和所有的设备驱动都被加载到这个分区,驻留在该分区的一切东西都被所有进程共享,但这些代码和数据都是被保护起来的,如果试图在这分区的某个内存地址读取或写入数据时,会引发访问违规。

3、地址空间中的区域

 当一个进程被创建和赋予它地址空间时,该可用地址空间 的主体是空闲的,即未分配的。若要使用该地址空间的各个部分,你必须通过调用VirtualAlloc来分配区域,分配区域的行为被称为预留。   每当你预留地址空间中的一个区域时,系统要确保该区域从一个分配粒度的边界开始。对于不同的CPU平台来说,分配粒度是各不相同。目前来说,所有的CPU平台都使用64KB这个相同粒度。
   当你预留地址空间的一个区域时,系统还要确保该区域的大小是系统的页面大小的倍数。页面是系统在管理内存时使用的一个内存单元。不同CPU其页面大小也不相同,x86使用的页面是4KB。
(1)预定地址空间中的一块区域(预订:VirtualAlloc、释放:VirtualFree)
  ①起始地址:分配粒度(一般是64K)的整数倍。(注意:分配粒度与CPU平台有关,同时系统自己预订的区域的起始地址不一定非得是64KB的整数倍,如系统为进程环境块(PEB)和线程环境块(TEB)预定的区域地址就可能不是64KB的整数倍,但区域的大小仍是系统页面大小的整数倍。应用程序自己预订的区域,)
  ②预定空间的区域的大小:系统页面大小的整数倍(x86和x64的页面大小为4KB,I64系统使用的页面大小为8KB)
(2)将预订区域提交物理存储器
  ①提交时,可以只提交区域的一部分。如预订64KB空间大小,但可以只提交第2、第4两个页面(同样是调用VirtualAlloc函数,但传入的是MEM_COMMIT类型的参数)。
  ②撤消提交:VirtualFree,并传入MEM_DECOMMIT
 如果你想预留一个10KB的地址空间区域,系统将自动对你的请求进行四舍五入,使保留的地址空间区域的大小是页面大小的倍数。这意味着,在x86平台上,系统将保留一个12KB的区域,在Alpha平台上,系统将保留一个16KB的区域。

  将预定区域提交给物理存储器,为了使用地址空间的预留区域,你必须分配物理存储,然后映射该存储区到预留区域,该过程称为提交物理存储器,物理存储被提交以页的倍数形式。为了提交物理存储器给预留区域,你可以调用VirtualAlloc。
                                                                 
Windows的内存安排



(1)每个应用程序都有自己的4GB寻址空间。该空间可存放操作系统、系统DLL和用户DLL代码,它们之中有各种函数供应用程序调用。再除去其他的一些空间,余下的是应用程序的代码、数据和可以分配的地址空间。

(2)不同应用程序的线性地址空间是隔离的。虽然它们在物理内存中同时存在,但在某个程序所属的时间片中,其他应用程序的代码和数据没有被映射到可寻址的线性地址中,所以是不可访问的。从编程的角度看,程序可供使用的4GB的寻址空间,而且这个空间是“私有的”

(3)DLL程序没有自己的“私有”的空间。它们总是被映射到其他应用程序的地址空间中,当做其他应用程序的一部分运行。原因很简单,如果它不和其他程序同属一个地址空间,应用程序就不能调用它。

(4)操作系统和系统DLL的代码需要供每个应用程序调用,所以在所有的时间片中都必须被映射;

(5)用户程序只在自己所属的时间片内被映射。用户DLL则有选择地被映射。如程序B和C都调用了xxx.dll,那么物理内存中xxx.dll(注意在内存中已经存在了!)的代码在图中的时间片2和n中被映射,其他时间片就不需要被映射。(当然物理内存中只需要一份xxx.dll的代码)。




















0
0

猜你在找
【套餐】Hadoop生态系统零基础入门
【套餐】嵌入式Linux C编程基础
【套餐】2017软考系统集成项目——任铄
【套餐】Android 5.x顶级视频课程——李宁
【套餐】深度学习入门视频课程——唐宇迪
【直播】广义线性模型及其应用——李科
【直播】从0到1 区块链的概念到实践
【直播】计算机视觉原理及实战——屈教授
【直播】机器学习之凸优化——马博士
【直播】机器学习&数据挖掘7周实训--韦玮
查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:11020次
    • 积分:250
    • 等级:
    • 排名:千里之外
    • 原创:13篇
    • 转载:10篇
    • 译文:0篇
    • 评论:0条
    文章分类