Windows内存体系结构
1、进程的虚拟地址空间
每个进程都有自己的虚拟地址空间。虚拟地址空间为用户编程时用的地址,用户程序地址的集合为逻辑地址空间。它的编制是从0开始的。32位进程的地址空间是4GB,因为32位指针可以表示从Ox00000000到OxFFFFFFFF之间的任一值。指针在这个范围内副高可进程的4GB地址空间。每个进程都有自己的地址空间,当线程运行的时候,只能访问自己进程的内存。
如何将比大于物理内存容量的用户程序装入内存?这就涉及到虚拟存储器。将物理存储器分配或映射到相应的地址空间。
1)虚拟地址空间的分区
(1)每个进程的虚拟地址空间划分为许多分区。主要有空指针赋值分区、用户模式分区、内核模式分区、64KB禁入分区。
(2)在空指针赋值分区帮助程序员捕获对空指针的赋值。如果进程中的线程试图读取或写入这一分区内的内存地址引发违规。在32位中,空指针赋值分区为Ox00000000到Ox0000FFFF,其实我们一般使用的NULL指为0,但是可以用其它的地址表示是空指针呢?答案是肯定的。可是为什么会有这么大的空间呢,这就是跟操作系统的分配粒度有关系,为了内存对齐,它是以64KB作为分配粒度的。是没有办法让我们分配位于这一地址区间内的虚拟地址的。
2)用户模式分区
(1)进程是无法通过指针来读取、写入或任何方式,访问驻留在这一分区中其他进程的数据的。可以通过设置对用户模式分区进行增加。当系统即将运行一个应用程序的时候会检查应用程序在链接时是否使用了/LARGEADDRESSAWARE链接器开关,如果是就是相当于应用程序在声明时会充分利用大用户模式地址空间。
3)内核模式分区
(1)在内核模式分区中存放这内核代码、设备驱动程序代码、设备输入/输出高速缓存、非分页缓冲池分配表、进程页面表等
2、地址空间中的区域
(1)当系统创建一个线程并赋予它地址空间时,可用地址大部分是空闲的。为了使用这部分的地址空间要使用VirtualAlloc来分配其中的区域,系统会确保区域的起始地址是分配粒度的整数倍,一般为64KB。当应用程序预订地址空间中的一块区域时,系统会确保区域的大小正好是系统页面大小的整数倍。
(2)我们必须分配物理存储器,将存储器映射到所预订的区域。物理存储器都是以页面为单位进行调拨,内存的大小对于我们有些程序来说,实在是太小了,于是页交换文件产生了。将内存中的一部分保存到页交换文件中,并在应用程序需要的时候将页交换文件中的对应部分载入内存。如此可增加应用程序可用内存的总量。
当使用函数把物理存储器调拨给地址空间区域时,该空间实际上是从硬盘上的页交换文件分配得到的。系统中页交换文件的大小决定应用程序可用内存总量的最重要因素。机器实际装备的内存总量对它的影响相对较小。也就等于说页交换文件增加了内存。
当线程访问所属进程地址空间中的一块数据时,会出现这样的情况,在内存中找不到数据,那么这个时候会到页交换文件中进行查找,如果在的话,查看内存中有无空闲的页面,然后将数据载入页面中。若没有闲置的页面,当页面中的数据已经被修改了,将页面放入页交换文件中。在进行载入数据的时候会进行定位。使用重定位寄存器实现。
3)那么还有一种情况是,不在页交换文件中维护的物理存储器。当许多程序同时运行,页交换文件势必会变得非常的大,这个时候系统还必须将在硬盘上的代码和数据放在页交换文件已调拨的物理存储器中。所谓的调拨就是将存储器映射到所预订的区域。
我们会提出这样的疑问,如此,当载入一个程序并让它运行会花费很长的时间。当我们运行一个.exe文件时,应用程序会计算出程序代码和数据的大小,然后在系统预定一个地址空间,并指明与这个地址空间对应的物理存储器就是.exe本身。页交换文件并没有给这个程序映射另外的物理存储。当把一个程序为与硬盘上的文件映像用作地址空间区域对应的物理存储器时,我们称这个文件映像为内存映射文件。
3、内存区域的类型
地址空间的映射包括区域的基地址、区域的类型,有4种类型:闲置、私有、映像、已映射。闲置也就是区域的虚拟地址没有任何的后备存储器。私有则是以系统的页交换文件作为后备存储器。映像;以映像文件为后备存储器。已映射:虚拟地址一开始以内存映射文件为后备存储器。内存映射文件:系统保留地址空间,且将物理存储器交给此区域。内存文件映射的物理存储器来自于已经存在磁盘上的文件。在进行操作之前必须首先对文件进行映射。可以将文件映射到多个进程地址空间,实现数据的共享。它是一个文件到一块内存的映射。这里对于映像文件与内存映射文件的区别不是很清楚???
4、数据对齐
1)原因:现在的大多数机器是并行进行访问的。比如4个芯片同时从地址为0处开始访问。若有的要从1开始,或许可以进行把读到的数据进行左移8个位置,那么势必要增加总线,接口变得负责。而且大多数的数据访问都可以做到数据对齐。
2)如果数据不对齐,那么就会进行多次访问。为什么呢??因为若不对齐,比如,访问地址1, 2, 3, 4的4字节数据,因为对于32位机子,一次能够读取4个字节,也就是并行的读取这4个字节中的数据,于是先读取地址0-3的数据,再读取地址4-7的数据,再把地址1-5的数据取出来放到目的寄存器中。 这就需要两次进行访问已经对齐的内存,这就增加了CPU内部的复杂度,影响到应用程序的性能。
3)对齐的方式
(1)自身的对齐值对于char型数据, 其自身对齐值为1; 对于short型为2; 对于int, float, double类型, 其自身对齐值为4单位字节.
(2)结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值.
(3)指定对齐值:#pragma pack (value)时的指定对齐值value. 在Visual C++中可以通过/Zp选项获#pragma设置。综合起来,基本类型的对齐模数是这个类型的大小和设置的对齐限制的较小者。
(4).数据成员, 结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值.
所以好的定义类的成员的方式是将同类型的放在一起。减少占用内存的空间。
struct A//内存所占的字节大小为. { char c; int i;//int 的对齐模式为,那么对齐模式为 char e; }; struct B//内存所占的字节大小为. { char c; char e; int i;//对于结构体,联合体和数组,对齐模数是它的成员的对齐模数的最大值 //它的size值为而不是,因为k,e都是连续存放的。 }; int main() { A a; cout<<"数据结构A的大小为:"<<endl; cout<<sizeof(a)<<endl; B b; cout<<"数据结构B的大小为:"<<endl; cout<<sizeof(b)<<endl; } |
小结
1、数据对齐
2、内存映射文件与映像文件的区别是???还望指教丫