一、进程的虚拟地址空间
n 每个进程都被赋予虚拟地址空间。32位进程是4GB(0x00000000~0xFFFFFFFF),64位为16EB(0x0000000000000000~0xFFFFFFFFFFFFFFFF)。
n 运行的线程只能访问它自己的进程的内存,属于其他进程的内存则隐藏,不能被访问。在Win2000,OS的内存也隐藏不能被访问,而Win98却可以。
n 但这是虚拟地址空间(可以访问的范围),不是物理地址空间,在访问前,必须赋予物理存储器或者将物理存储器映射到各个部分的地址空间。
二、虚拟地址空间如何分区:
n 虚拟地址空间划分成各个区,其划分是根据OS来实现的。不同的Windows内核分区也不同。
n NuLL 指针分配的分区(适用于Win2000和Win98)。线程试图读取、写入该分区的地址空间,CPU会引发访问违规。
n MS-DOS及16位Windows 应用程序兼容分区(4MB)(仅适用于Windows 98)。维护MS-DOS应用程序和16位Windows 应用程序兼容性。32位应用程序不应该读写。在Win2000中,16位程序运行在它们自己的空间,32位程序不会对他们产生影响。
n 用户方式分区(适用于Win2000和Win98)。维护进程大部分数据的地方,其他进程不能访问该分区的数据。Win2000中所有的.exe和DLL均加载这个分区。Win98中,主要的Win32系统DLL(Kernel32.dll, AdvApi32.dll, User32.dll, GDI32.dll)加载在共享内存映射文件分区,.exe和其他DLL均加载这个分区。(在x86的Win2000中获取3GB用户方式分区和在64位Win2000中获取2GB用户方式分区,操作系统创建进程时,检查LARGEADDRESSAWARE标志)。
n 64 KB禁止进入的分区(仅适用于Windows 2000)。位于用户方式分区上面的64KB分区是禁止进入的,任何访问均导致访问违规。
n 共享的MMF分区(仅适用于Windows 98)。这个1GB分区是系统存放32位进程共享数据的地方。如:Kernel32.dll, AdvApi32.dll, User32.dll, GDI32.dll和内存映射文件等。
n 内核方式分区(适用于Windows 2000和Windows 98):存放操作系统代码的地方。用于加载线程调度、内存管理、文件系统支持、网络支持和所有设备驱动程序的代码。此分区中的一切均可被所有进程共享。Win2000中受保护的,试图访问该分区中的内存地址,会产生访问违规(系统有错误提示);但Win98不受保护。
三、址空间中的区域:
当进程被创建并赋予地址空间时,其空间主体是空闲的(未分配)。需用VirtualAlloc分配其中的各个区域(从分配粒度的边界开始,64KB;大小是系统页面大小的倍数,4KB或8KB)才可使用(reserving)。使用VirtualFree释放。
四、提交地址空间区域中的物理存储器:
调用VirtualAlloc提交,使用VirtualFree释放。
五、物理存储器与页文件:
老OS,物理存储器为RAM;现在的OS,磁盘上的文件为页文件,包含可供所有进程使用的虚拟内存。OS与CPU协调,将RAM的各部分保存到页文件中,当运行的程序需要时,再将页文件加载到RAM。所以,增加RAM可以使计算机运行更快;增加页文件,就可以同时运行更多的程序。
六、保护属性:
已经分配的物理存储器的各个页面可以被赋予不同的保护属性(PAGE_READONLY, PAGE_EXECUTE, ……)。
n Copy-On-Write 访问(PAGE_WRITECOPY,PAGE_EXECUTE_WRITECOPY)。只被OS使用,不应该在VirtualAlloc调用时使用。
n 特殊的访问保护属性的标志。
1. PAGE_NOCACHE:停用已提交的页面的高速缓存。一般供设备驱动程序使用。
2. PAGE_WRITECOMBINE:一般供设备驱动程序使用(允许把单个设备的多次写入合并在一起,提高性能)。
3. PAGE_GUARD:见C16。
七、数据对齐的重要性
a) 数据对齐是CPU结构的一部分,而不是OS内存结构的一部分。如:WORD值应该从被2整除的地址开始、DWORD值应该从被4整除的地址开始……
b) CPU访问正确对齐的数据时,运行效率最高。所以在设计数据结构时要注意对齐,也可以理解Windows在很多结构中使用reserved字段的原因。
c) CPU读取未对齐的数据时:产生异常或多次对齐内存访问(运行速度降低)。
d) x86 CPU能自动对齐;
e) 而Alpha CPU不能自动对齐,Win2000可以设置(通过注册表、AXPAlig实用程序或调用SetErrorMode函数,编译器的特殊关键字_unaligned)是默默的纠正还是产生异常(一般用于调试程序)。