Windows核心编程:内存体系结构

作者:shenzi

链接:http://blog.csdn.net/shenzi

Windows核心编程:内存体系结构

1.进程的虚拟地址空间
    每 个进程都有自己的虚拟地址空间。对32位进程来说,这个地址空间的大小为4GB,这是因为32位指针可以表示从0x00000000到 0xFFFFFFFF之间的任一值,指针在这个范围内可以有4 294 967 296个值,它们覆盖了进城的4GB地址空间。对62位进程来说,它们可以覆盖16EB地址空间,这个地址空间实在太大了!!!
     每个进程有自己的私有地址空间,但是要记住这只是虚拟地址空间,不是物理存储器。为了能过正常读/写数据,我们需要把物理存储器分配或映射到相应的虚拟地址空间,否则将导致访问违规。
2.虚拟地址空间的分区

    每个进程的虚拟地址空间被划分成许多分区(partition)。由于地址空间的分区依赖于操作系统的底层实现,因此会随着Windows内核的不同而略有变化。表1列出了各平台上对进程地址空间的分区。

表1:进程地址空间是如何划分的

分区

x 86 32位Windows

3 GB用户模式下的 x 86

32位Windows

x 64 64位Windows

IA-64 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

64-KB

禁入分区

0x7FFF0000

0xBFFF0000

0x000007FF'FFFF0000

0x000006FB'FFFF0000

0x7FFFFFFF

0xBFFFFFFF

0x000007FF'FFFFFFFF

0x000006FB'FFFFFFFF

内核模式分区


0x80000000

0xC0000000

0x00000800'00000000

0x000006FC'00000000

0xFFFFFFFF

0xFFFFFFFF

0xFFFFFFFF'FFFFFFFF

  0xFFFFFFFF'FFFFFFFF

空指针赋值分区:
    保留该区域的目的是为了帮助程序员捕获对空指针的赋值。如果进程中的线程试图读取或写入位于这一分区内的内存地址,就会引发访问违规。没有任何办法可以让我们分配到位于这一地址区间内的虚拟内存。
用户模式分区:
    这一分区是进程地址空间的驻地。对所有应用程序来说,进程的大部分数据都保存在这一分区。
内核模式分区:
    系 统需要用这一空间来存放内核代码。设备驱动程序代码、设备输入/输出高速缓存、非分页缓冲池分配表、进程页面表,等等。驻留在这一分区内的任何东西为所有 进程共有。该分区中的所有代码和数据都被完全的保护起来。如果一个应用程序试图读取或写入位于这一分区中的内存地址,会引发访问违规。在默认情况下,访问 违规会导致系统先向用户显示一个消息框,然后结束该应用程序。
3.地址空间中的区域
    当系统创建一个进程并赋予它地址空间时,可用地址空间中的大部分都是闲置的(free)或尚未分配的(unallocated)
为了使用这部分地址空间,我们必须调用VirtualAlloc来分配其中的区域(region)分配区域的操作被称为预定(reserving)
    当应用程序预定地址空间区域时,系统会确保区域的起始地址正好是分配粒度 (allocation granularity)的整数倍。分配粒度会根据不同的CPU平台而有所不同。目前,所有的CPU平台都使用相同的分配粒度,大小为64KB—也就是系统会把分配请求取整到64KB的整数倍。
     当应用程序预定地址空间的一块区域时,系统会确保区域的起始地址正好是系统页面 大小的整数倍。页面是一个内存单元,系统通过它来管理内存。与分配粒度相似,页面大小会根据不同的CPU而有所不同。x86和x64系统使用的页面大小为4KB,IA-64系统使用的页面大小为8KB。
     (即当我们预定地址空间时,分配粒度影响我们的起始地址,因为起始地址必须整除分配粒度。页面大小影响我们预定的大小,大于等于我们预定的大小,因为要是页面大小的整数倍。)
    虽然系统规定应用程序在预定地址空间区域时起始地址必须是分配粒度的整数倍,但系统自己却不存在这样的限制。非常有可能出现的情况是,系统预定的区域的起始地址并非64KB的整数倍,但是预定的区域仍然必须是CPU页面大小的整数倍。
    当程序不再需要访问所预定的地址空间区域时,应该释放该区域。通过调用VirtualFree函数来完成。
4.给区域调拨物理存储器
    
为了使用所预定的地址空间区域,我们还必须分配物理存储器,并将存储器映射到所预定的区域。这个过程称为调拨(committing)物理存储器。物理存储器始终都以页面为单位来调拨。我们通过调用VirtualAlloc函数来讲物理存储器调拨给所预定的区域。
     当程序不再需要访问所预定区域中已调拨的物理存储器时,应该释放物理存储器。这个过程称为撤销调拨(decommitting)物理存储器,通过调用VirtualFree函数来完成。
    (
VirtualAlloc, VirtualFree具体使用在以后章节具体讨论
     说明:预定只是在虚拟地址空间中预定区域,这时并未为该区域指定物理地址空间。通过调拨操作,我们将之前预定的虚拟地址空间绑定到特定的物理地址空间。
5、物理存储器和页交换文件   
     在老式的操作系统中,物理存储器被认为是极其中的内存的总量。当今的操作系统能让磁盘空间看起来像内存一样。磁盘上的文件一般被称为页交换文件 (paging file),其中包含虚拟内存,可供任何进程使用。
     为了能够使用虚拟内存,操作系统需要CPU的大力协助。当线程试图访问存储器中的一个字节时,CPU必须知道该字节是在内存中还是在磁盘上。如果一台机器 准备了1GB的内存,硬盘上还有1GB的也交换文件,那么有用程序会认为可用内存的总量为2GB。当然,这台机器实际上并没有准备2GB的内存。实际上, 是操作系统与CPU分工协作,把内存中的一部分保存到也交换文件中,并在应用程序需要的时候再将页交换文件中的对应部分载入内存。因此,使用也交换文件可 以增大应用程序可用内存的总量。最好把物理存储器看成是保存在磁盘上的也交换文件中的数据。
     当一个线程试图访问所属进程的地址空间中的一块数据时,有可能会出现两种情况。图1显示了经简化后的流程:
    

图1:把虚拟地址转换为物理存储器地址

     系统需要在内存和也交换文件之间复制页面的频率越高,硬盘颠簸(thrash)得越厉害,系统运行得也越慢。
     要让计算机跑的快,最好是增加内存。
不在页交换文件 中维护的物理存储器:
    
如 果每次运行一个程序时,系统都必须为该进程的代码和数据预定地址空间区域,为这些区域调拨物理存储器,然后把硬盘上的程序文件中的代码和数据复制到页交换 文件中已调拨的物理存储器中去。那么载入一个程序并让它运行起来会花费很长的时间。事实上,系统并不会执行刚才所说的这些操作。当用户要求执行一个应用程序时,系统会打开该应用程序对应的.exe文件并计算出应用程序的代码和数据的大小。然后系统会预定一块地址空间,并注明与该区域相关联的物理存储器就是.exe文件本身。 是的,系统并没有从页交换文件中分配空间,而是将.exe文件的实际内容用作程序预定的地址空间区域。这样一来,不但载入程序非常快,而且页交换文件也可以保持一个合理的大小。
    
当把一个程序位于磁盘上的文件映像(即一个.exe或DLL文件)用作地址空间区域对应的物理存储器时,我们称这个文件映像为内存映像文件 (memory mapped file)。当载入一个.exe或DLL时,系统会自动预定地址空间区域并把文件映像映射到该区域。
6.页面保护属性
    
我们可以给每个已分配的物理存储页指定不同的页面保护属性。表2列出了所有页面保护属性:

表2:内存页面保护属性
保护属性

 

描述

 

PAGE_NOACCESS

试图读取页面、写入页面或执行页面中的代码将引发访问违规。

PAGE_READONLY

试图写入页面或执行页面中的代码将引发访问违规。

PAGE_READWRITE

试图执行页面中的代码将引发访问违规。

PAGE_EXECUTE

试图读取页面或写入页面将引发访问违规。

PAGE_EXECUTE_READ

试图写入页面将引发访问违规。

PAGE_EXECUTE_READWRITE

对页面执行任何操作都不会引发访问违规

PAGE_WRITECOPY

试图执行页面中的代码将引发访问违规。试图写入页面将使系统为进程

单独创建一份该页面的私有副本(以页交换文件为后备存储器)

PAGE_EXECUTE_WRITECOPY

对页面执行任何操作都不会引发访问违规。试图写入页面将使系统为

进程单独创建一份该页面的私有副本( 以页交换文件为后备存储器

写时复制:
    
PAGE_WRITECOPY PAGE_EXECUTE_WRITECOPY 存在的目的是为了节省内存和页交换文件的使用。 Windows 支持一种机制,允许两个或两个以上的进程共享同一块存储器。让所有的应用程序共享系统的存储页极大地提升了系统的性能,但另一方面,这也要求所有的应用程 序实例只能读取其中的数据或是执行其中的代码。如果某个应用程序实例修改并写入一个存储页,那么这等于是修改了其它实例正在使用的存储页,最终将导致混 乱。
     为了避免此类混乱的发生,操作系统会给共享的存储页指定写时复制属性 。当系统把一个.exe或.dll映射到一个地址空间的时候,系统会计算有多少页面是可写的。(通常,包含代码的页面被标记为
PAGE_EXECUTE_WRITECOPY, 而包含数据的页面被标记为 PAGE_WRITECOPY
     具体通过两个图说明:


图2:修改数据前
图3:修改数据后

7.地址空间映射实例

表3:运行在32位x86版本的Windows上的一个地址空间映射的实例

基地址

类型

大小

块数

保护属性

描述

00000000

Free

65536

     

00010000

Mapped

65536

1

-RW-

 

00020000

Private

4096

1

-RW-

 

00021000

Free

61440

     

00030000

Private

1048576

3

-RW-

Thread Stack

00130000

Mapped

16384

1

-R--

 

00134000

Free

49152

     

00140000

Mapped

12288

1

-R--

 

00143000

Free

53248

     

00150000

Mapped

819200

4

-R--

 

00218000

Free

32768

     

00220000

Mapped

1060864

1

-R--

 

00323000

Free

53248

     

00330000

Private

4096

1

-RW-

 

00331000

Free

61440

     

00340000

Mapped

20480

1

-RWC

/Device/HarddiskVolume1/Windows/System32/en-US/user32.dll.mui

00345000

Free

45056

     

00350000

Mapped

8192

1

-R--

 

00352000

Free

57344

     

00360000

Mapped

4096

1

-RW-

 

00361000

Free

61440

     

00370000

Mapped

8192

1

-R--

 

00372000

Free

450560

     

003E0000

Private

65536

2

-RW-

 

003F0000

Free

65536

     

00400000

Image

126976

7

ERWC

C:/Apps/14 VMMap.exe

0041F000

Free

4096

     

00420000

Mapped

720896

1

-R--

 

004D0000

Free

458752

     

00540000

Private

65536

2

-RW-

 

00550000

Free

196608

     

00580000

Private

65536

2

-RW-

 

00590000

Free

196608

     

005C0000

Private

65536

2

-RW-

 

005D0000

Free

262144

     

00610000

Private

1048576

2

-RW-

 

00710000

Mapped

3661824

1

-R--

/Device/HarddiskVolume1/Windows/System32/locale.nls

00A8E000

Free

8192

     

00A90000

Mapped

3145728

2

-R--

 

00D90000

Mapped

3661824

1

-R--

/Device/HarddiskVolume1/Windows/System32/locale.nls

0110E000

Free

8192

     

01110000

Private

1048576

2

-RW-

 

01210000

Private

524288

2

-RW-

 

01290000

Free

65536

     

012A0000

Private

262144

2

-RW-

 

012E0000

Free

1179648

     

01400000

Mapped

2097152

1

-R--

 

01600000

Mapped

4194304

1

-R--

 

01A00000

Free

1900544

     

01BD0000

Private

65536

2

-RW-

 

01BE0000

Mapped

4194304

1

-R--

 

01FE0000

Free

235012096

     

739B0000

Image

634880

9

ERWC

C:/Windows/WinSxS/x86_microsoft.vc80.crt_1fc8b3b9a1e18e3b_8.0.50727.

312_none_10b2ee7b9bffc2c7/MSVCR80.dll

73A4B000

Free

24072192

     

75140000

Image

1654784

7

ERWC

C:/Windows/WinSxS/x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.6000.16386_none_5d07289e07e1d100/ comctl32.dll

752D4000

Free

1490944

     

75440000

Image

258048

5

ERWC

C:/Windows/system32/uxtheme.dll

7547F000

Free

15208448

     

76300000

Image

28672

4

ERWC

C:/Windows/system32/PSAPI.dll

76307000

Free

626688

     

763A0000

Image

512000

7

ERWC

C:/Windows/system32/USP10.dll

7641D000

Free

12288

     

76420000

Image

307200

5

ERWC

C:/Windows/system32/GDI32.dll

7646B000

Free

20480

     

76470000

Image

36864

4

ERWC

C:/Windows/system32/LPK.dll

76479000

Free

552960

     

76500000

Image

348160

4

ERWC

C:/Windows/system32/SHLWAPI.dll

76555000

Free

1880064

     

76720000

Image

696320

7

ERWC

C:/Windows/system32/msvcrt.dll

767CA000

Free

24576

     

767D0000

Image

122880

4

ERWC

C:/Windows/system32/IMM32.dll

767EE000

Free

8192

     

767F0000

Image

647168

5

ERWC

C:/Windows/system32/USER32.dll

7688E000

Free

8192

     

76890000

Image

815104

4

ERWC

C:/Windows/system32/MSCTF.dll

76957000

Free

36864

     

76960000

Image

573440

4

ERWC

C:/Windows/system32/OLEAUT32.dll

769EC000

Free

868352

     

76AC0000

Image

798720

4

ERWC

C:/Windows/system32/RPCRT4.dll

76B83000

Free

2215936

     

76DA0000

Image

884736

5

ERWC

C:/Windows/system32/kernel32.dll

76E78000

Free

32768

     

76E80000

Image

1327104

5

ERWC

C:/Windows/system32/ole32.dll

76FC4000

Free

11649024

     

77AE0000

Image

1171456

9

ERWC

C:/Windows/system32/ntdll.dll

77BFE000

Free

8192

     

77C00000

Image

782336

7

ERWC

C:/Windows/system32/ADVAPI32.dll

77CBF000

Free

128126976

     

7F6F0000

Mapped

1048576

2

-R--

 

7F7F0000

Free

8126464

     

7FFB0000

Mapped

143360

1

-R--

 

7FFD3000

Free

4096

     

7FFD4000

Private

4096

1

-RW-

 

7FFD5000

Free

40960

     

7FFDF000

Private

4096

1

-RW-

 

7FFE0000

Private

65536

2

-R--

 

8.数据对齐的重要性
     相 对于操作系统的内存体系结构,数据对齐更多的是CPU体系结构的一部分。只有当访问已对齐的数据时,CPU的执行效率才最高。把数据的地址模除数据的大 小,如果结果为0,那么数据就是对齐的。如果CPU要访问的数据没有对齐,那么会有两种可能。第一种可能是CPU会引发一个异常,另一种可能是CPU会通 过多次访问已对齐的内存,来取得整个错位数据。
     x86系统,一旦程序试图访问错位数据,CPU会自动执行必要的操作来访问错位数据。为了得到最佳的应用程序性能,我们在编写代码时应该尽量让数据对齐。因为与访问已对齐的数据相比,系统在最好的情况下页需要花两倍的时间来访问错位数据,而且情况可能会更糟。



你可能感兴趣的:(windows编程)