13.1 Windows的虚拟地址空间安排
13.1.1虚拟地址空间的分区(即虚拟地址空间布局)
进程的地址空间划分
分区 |
x86 32位 Windows |
3GB用户模式下的x86 32位Windows |
X64 64位 Windows |
IA-64 64位 Windows |
空指针赋值区 |
0x0000 0000 0x0000 FFFF |
0x0000 0000 0x0000 FFFF |
0x00000000 00000000 0x00000000 0000FFFF |
0x00000000 00000000 0x00000000 0000FFFF |
用户模式分区 |
0x0001 0000 0x7FFE FFFF |
0x0001 0000 0xBFFE FFFF |
0x00000000 00010000 0x000007FF FFFEFFFF |
0x00000000 00010000 0x000006FB FFFEFFFF |
64KB禁入分区 |
0x7FFF 0000 0x7FFF FFFF |
0xBFFF 0000 0xBFFF FFFF |
0x000007FF FFFF0000 0x000007FF FFFFFFFF |
0x000006FB FFFF0000 0x000006FB FFFFFFFF |
内核模式 |
0x8000 0000 0xFFFF FFFF |
0xC000 0000 0xFFFF FFFF |
0x00000800 00000000 0xFFFFFFFF FFFFFFFF |
0x000006FC 00000000 0xFFFFFFFF FFFFFFFF |
(1)空指针赋值分区
①为帮助程序员捕获对空指针的赋值,当线程试图读取或写入这一分区的内存地址,就会引发访问违规
②没有任何办法可以让我们分配到位于这一地址区间的虚拟内存。
(2)用户模式分区
①进程地址空间的驻地。对于应用程序来说,大部分数据都保存在这一分区。
②32位下,默认为2GB大小。打开/3GB开关时,可扩大到3GB空间,但同时内核空间缩小为1GB)
【x86 Windows下获得更大的用户模式分区】——修改Windows启动配置数据(Boot Configuration Data,BCD)
①运行BCDEdit.exe
②bcdedit /set IncreaseUserVa 3072,就可以为进程保留3GB用户模式地址空间,IncreaseUserVa可接受的最小值为2048,即默认的2GB。取消的话:bcdedit /deletevalue IncreaseUserVa。
③为了让应用程序可以访问2GB以上的地址空间(特别地,早期的应用程序是不允许这样做的)。在链接时,可以打开/LARGEADDRESSAWARE链接开关。
【在64位Windows下得到2GB用户模式分区】将32位应用程序移植到64位环境下
①因大量使用32位指针开发程序,仅重新编译程序会导致指针截断错误和不正确的内存访问。但可以让应用程序在地址空间沙箱(Address space sandbox)中运行,这也是默认的情况,系统能够保证高33位都为0的64地址截断为32位,这样进程可用的地址空间就被限制在最底部的2GB中。
②当运行64位应用程序时,默认下系统会保留用户模式地址空间中在2GB以下(即最底部的2GB),这就是所谓的地址空间沙箱。这空间对于大多数的应用程序来说是足够的。
③为了让64位应用程序能够访问整个用户地址空间,必须指定/LARGEADDRESSAWARE链接器开关来链接应用程序。
(3)内核模式分区
操作系统代码的驻地。与线程调度、内存管理 、文件系统支持、网络支持以及设备驱动程序相关的代码都载入到这个分区中。该分区中的所有代码和数据都为所有进程共有,但这些代码和数据都是被保护起来的,如果试图在这分区的某个内存地址读取或写入数据时,会引发访问违规。
13.1.2 Windows内存安排(时间上的安排)
(1)每个应用程序都有自己的4GB寻址空间。该空间可存放操作系统、系统DLL和用户DLL代码,它们之中有各种函数供应用程序调用。再除去其他的一些空间,余下的是应用程序的代码、数据和可以分配的地址空间。
(2)不同应用程序的线性地址空间是隔离的。虽然它们在物理内存中同时存在,但在某个程序所属的时间片中,其他应用程序的代码和数据没有被映射到可寻址的线性地址中,所以是不可访问的。从编程的角度看,程序可供使用的4GB的寻址空间,而且这个空间是“私有的”
(3)DLL程序没有自己的“私有”的空间。它们总是被映射到其他应用程序的地址空间中,当做其他应用程序的一部分运行。原因很简单,如果它不和其他程序同属一个地址空间,应用程序就不能调用它。
(4)操作系统和系统DLL的代码需要供每个应用程序调用,所以在所有的时间片中都必须被映射;
(5)用户程序只在自己所属的时间片内被映射。用户DLL则有选择地被映射。如程序B和C都调用了xxx.dll,那么物理内存中xxx.dll(注意在内存中已经存在了!)的代码在图中的时间片2和n中被映射,其他时间片就不需要被映射。(当然物理内存中只需要一份xxx.dll的代码)。
13.2 地址空间中的区域
(1)预定地址空间中的一块区域(预订:VirtualAlloc、释放:VirtualFree)
①起始地址:分配粒度(一般是64K)的整数倍。(注意:分配粒度与CPU平台有关,同时系统自己预订的区域的起始地址不一定非得是64KB的整数倍,如系统为进程环境块(PEB)和线程环境块(TEB)预定的区域地址就可能不是64KB的整数倍,但区域的大小仍是系统页面大小的整数倍。应用程序自己预订的区域,)
②预定空间的区域的大小:系统页面大小的整数倍(x86和x64的页面大小为4KB,I64系统使用的页面大小为8KB)
(2)将预订区域提交物理存储器
①提交时,可以只提交区域的一部分。如预订64KB空间大小,但可以只提交第2、第4两个页面(同样是调用VirtualAlloc函数,但传入的是MEM_COMMIT类型的参数)。
②撤消提交:VirtualFree,并传入MEM_DECOMMIT
13.3 物理存储器和页交换文件
(1)虚拟内存的实现:当应用程序调用VirtualAlloc函数将预订的空间区域提交物理存储器(物理内存或页交换文件)时,该空间实际上仍然不是从物理内存而是页交换文件中分配得到的,以后当访问该空间时,会因数据并不存在于物理内存而发生访问“页面错误”,从而引发操作系统利用异常处理机制将虚拟地址空间真正映射到对应的物理内存中,如下图所示。
(2)内存映射文件:把硬盘上的文件映像(如一个.exe或DLL文件)作为虚拟内存的一部分(注意是文件映射,而不是页交换文件)。当用户要执行一个可执行文件时,系统会打开应用程序对应的.exe文件并计算出应用程序的代码和数据的大小。然后预订一块地址空间,并注明与该区域相关的存储场所是.exe文件本身,而不是页交换文件。这样做可以将.exe的实际内容用作程序预订的地址空间区域,不仅载入程序速度快,而且可避免将为每个程序文件的代码和数据复制到页交换文件而造成页交换文件过于庞大和臃肿。
13.4 页面保护属性
保护属性 |
描述 |
PAGE_NOACCESS |
不可访问。试图读取、写入或执行页面中的数据(代码)时将引发访问违规。 |
PAGE_READONLY |
只读。试图写入页面或执行页面中的代码将引发访问违规 |
PAGE_READWRITE |
读写属性。试图执行页面中的代码将引发访问违规 。 |
PAGE_EXECUTE |
可执行属性。试图读取或写入页面将引发访问违规。 |
PAGE_EXECUTE_READ |
可读、可执行。读图写入页面将引发访问违规。 |
PAGE_EXECUTE_READWRITE |
可读可写可执行。对页面的任何操作都不会引发访问违规 |
PAGE_WRITECOPY |
①写时复制。试图执行页面中的代码将引发访问违规。 ②试图写入页面将使系统为进程单独创建一份该页面私有副本(以页交换文件为后备存储器) |
PAGE_EXECUTE_WRITECOPY |
对页面执行任何操作都不会引发访问违规。试图写入页面将使系统为进程单独创建一份该页面私有副本(以页交换文件为后备存储器) |
★注意:如果Windows启用了数据执行保护(Data Execution Protection,DEP),当CPU试图执行某个页面中的代码,而该页面又没有PAGE_EXECUTE_*保护属性,那么CPU会抛出访问违规异常。(DEP开启方法:我的电脑→右键“属性”→高级系统设置→性能→设置→数据执行保护,选中“仅为基本Windows程序和服务启用DEP”)
13.4.1 写时复制
(1)写时复制属性的作用:节省内存和页交换文件的使用
Windows提供一种机制,允许两个或两个以上的进程共享一块存储器。如10个记事本进程正在运行,所有的进程会共享应用程序的代码页和数据页。当只读或执行时,这种共享存储页的方式极大地提高了性能。但当某个实例写入一个存储页时,就要求给共享的存储页指定写时复制属性,这样在映射地址空间时,系统会计算有多少可写页面,然后从页交换文件中分配空间来容纳这些可写页面,在程序真正写入的时候,就存储在页交换文件中。
(2)写入共享页面时,系统介入的操作
①系统在内存中找到一个空闲页面。注意,该空闲页的后备页面来自页交换文件。它是系统最初将模块映射到进程的地址空间时分配的。由于是第1次映射时就分配了所需的页交换文件空间。所以这步不可能失败。
②系统将要修改的页面内容复制到第1步找到的空闲页面,然后给这些空闲页面指定PAGE_READWRITE或PAGE_EXECUTE_READWRITE属性。(注意系统不会修改原始页面的保护属性和数据)
③然后系统更新进程的页面表,这样,原来的虚拟地址现在就对应到内存中一个新的页面了。以后进程就可以访问它自己的副本了。
(3)在预订地址空间或提交物理存储器时,不能使用PAGE_WRITECOPY或PAGE_EXECUTE_WRITECOPY保护属性,否则VirtualAlloc会失败,GetLastError将返回ERROR_INVALID_PARAMETER。
13.4.2 一些特殊的访问保护属性标志
保护属性 |
描述 |
PAGE_NOCACHE |
禁止对己提交的页面进行缓存。该标志的目的是为了让需要操控内存缓冲区的驱动程序开发人员使用。一般不建议用将这标志用于除此以外的其他用途。 |
PAGE_WRITECOMBINE |
允许把单个设备的多次写操作组合在一起,以提高性能。也是给驱动程序开发人员用的。 |
PAGE_GUARD |
使应用程序能够在页面中的任何一个字节被写入时得到通知。 |
13.5 实例分析
13.5.1 各区域分析
……
……
(1)基地址:
①从0x0000 0000开始,到0x7FFE 0000+ FFFF结束。
②几乎所有的非空闲区域的基地址都是64KB的整数倍(这是由系统地址空间的分配粒度决定的)。如果不是64KB的整数倍,这意味着该区域是由操作系统以进程名义分配的。
(2)区域类型
类型 |
描述 |
Free(空闲) |
区域的虚拟地址没有任何后备存储器。该地址空间尚未预订,应用程序可以从基地址开始预订,也可以从空闲区域内的任何地方开始预订区域 |
Private(私有) |
区域的虚拟地址以系统的页交换文件为后备存储器 |
Image(映像) |
一开始以映像文件(如exe或DLL)为后备存储器,但以后不一定以映像文件为后备存储器(如程序写入映像文件中一个全局变量,那么写时复制会改用页交换文件来作为后备存储器)(映射文件可理解为exe或dll文件) |
Mapped(己映射) |
一开始以内存映射文件为后备存储器,此后不一定以内存映像文件为后备存储器。(如内存映射文件可能会使用写时复制保护属性。任何写操作会使对应的页面改用页交换文件来作为后备存储器) |
★注意:对于每个区域整体而言,该区域的类型是推测出来的(除空闲外),详细见13.5.2节《区域内部》的内容。
(3)区域预订的字节数
①始终是CPU页面大小的整数倍(对于x86为4字节,即4096的倍数)
②为了节省磁盘空间,链接器会尽可能对对PE文件进行压缩,所以磁盘上的文件大小与映射到内存所需要的字节数是有差异的。
(4)预订区域内部的块的数量(block)
①块是一些连续的页面,这些页面具有相同的保护属性,并以相同类型的物理存储器为后备存储器。对闲置页面来说,由于不可能将存储器拨给他们,该值始终为0。
②每个区域最大能容纳的块的数量为:区域大小/页面大小,即当每个页面都是一个不同的块时,这里块的数量最多。
(5)区域的保护属性:
①E=execute,R=read,W=Write,C=copy on write。如果区域没有显示任保护属性,表示该区域没有任何访问保护。闲置区域没有与之相关联的保护属性。
②PAGE_GAUARD和PAGE_ONCACHE标志对地址空间没有意义,这些标志只有当用于物理存储时才有意义。
③如果同时给区域和物理存储器指定了保护属性,那么以后者为准。(见区域内部一节的分析)
13.5.2 区域内部——以0x767F000所在区域为例(本例中用来装载User32.dll的区域)
基地址 |
类型 |
大小 |
块数 |
保护属性 |
描述 |
… |
… |
… |
… |
… |
… |
767F0000 767F0000 767F1000 7685A000 7685B000 7685C000 |
映像 映像 映像 映射 映像 映像 |
647168 4096 430080 4096 4096 40960 |
5 |
ERWC -R—(只读) ER—(可执行,可读) -RW—(可读可写) -RWC- -R-— |
C:\Windows\system32\USER32.dll //提交了105个页面(430080/4096) |
7688E0000 |
空闲 |
8192 |
|||
… |
… |
… |
… |
… |
… |
(1)第1列显示的是具有相同状态和保护属性的一组页面的地址。如第1组只读,第2组可执行可读,第3组可读可写。
(2)第2列块的类型,即以何种类型的物理存储器为后备存储器。Private、Mapped、Image分别表示以页交换文件、内存映射文件和加载的Exe(或Dll)文件为后备存储器。但Free和Reserved表示该块没有后备物理存储器。
(3)第3列:块的大小。一个区域中所有的块都是连续的,不会存在任何的间隙。
(4)第4列:所预订区域内部中块的数量
(5)第5列:块的页保护属性:一个块的保护属性会优先于所属区域的保护属性。(注意:PAGE_GUARD、PAGE_NOCACHE、PAGE_WRITECOMBINE保护属性只能用于块(即物理存储器,不能用于区域)。(注意:区域可以理解为预订的地址空间,块可以理解为在这个预订的地址空间中进一步细分出来的更小的一片地址空间)。
13.6 数据对齐的重要性
(1)数据对齐:将数据的地址 % 数据大小 = 0时的数据是对齐的。
(2)x86CPU对错位数据的处理
①EFLAGS寄存器的AC标志位(AlignmentCheck)为0时,CPU自动执行必要的操作来访问错位数据)
②AC标志位为1时,如果试图访问错位数据,CPU会触发INT 17H中断。(对于x86版本的Windows从来不变为AC标志位(即永远为0),因此x86处理器上运行应用程序,绝对不会发生数据错位的异常,但IA-64CPU处理器不能自己处理数据错误的错误,因此当访问错位数据时,会抛出一个EXECPTION_DATATYPE_MISALIGNMENT异常,我们通用SetErrorMode函数并传为SEM_NOALIGNMENTFAULTEXCEPT标志,让系统自动修正数据错位的错误。(注意传入这个标志会影响进程中所有的线程,而且这个错误模式会被进程的子进程继承)
(3)编译器对错位数据的处理
①IA-64版本的VC/C++编译器支持__unaligned关键字
如DWORD dw = *(__unaligned DWORD*)pvDataBuffer;
②x86版本的VC/C++编译器:不支持__nnaligned关键字,所以这个关键字在x86版本的编译器下会报错。
③鉴于编译器对__unaligned有不同的支持,为代码的通用性,建议用UNALIGNED和UNLIGNED64宏来替换__unaligned。
#if defined(_M_MRX000) || defined(_M_ALPHA) || defined(_M_PPC) ||defined(_M_IA64) || defined(_M_AMD64) #define ALIGNMENT_MACHINE #define UNALIGNED __unaligned #if defined(_WIN64) #define UNALIGNED64 __unaligned #else #define UNALIGNED64 #endif #else #undef ALIGNMENT_MACHINE #define UNALIGNED #define UNALIGNED64 #endif
【AlignOf程序】内存对齐演示程序
#include <windows.h> #include <tchar.h> #include <locale.h> //在MSVC中,一般使用#progma pack来指定内存对齐: #pragma pack(show) //以警告信息的形式显示当前字节对齐的值(在编译输出框显示) //默认的8字节对齐 struct BYTE1{ char ch1; int i1; }; #pragma pack(push) #pragma pack(1) #pragma pack(show) struct BYTE2{ char ch2; int i2; }; #pragma pack(pop) //微软的__declspec(align(#)),其#的内容可以是预编译宏,但不能是编译期数值 struct __declspec(align(1)) BYTE3{ char ch3; int i3; }; VOID AlignTest(PVOID pvDataBuffer){ char *pc = (PCHAR)pvDataBuffer; pc++; //指向第2个字节 //未对齐方式访问:将第2-5个字节当成DWORD来看待,此时内存没对齐, //因为DWORD的起始地址而是4的倍数 DWORD dwUnAligned = *(DWORD*)(pc); _tprintf(_T("dwUnAligned=0x%08X\n"), dwUnAligned); //用对齐方式访问,效率更高 DWORD dwAligned = *(UNALIGNED DWORD*)pc;//*(DWORD*)pc; _tprintf(_T("dwAligned =0x%08X\n"), dwAligned); } int _tmain(){ char c[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; AlignTest((PVOID)c); //内存对齐 size_t sz1 = sizeof(BYTE1); size_t sz2 = sizeof(BYTE2); size_t sz3 = sizeof(BYTE2); _tprintf(_T("sizeof(BYTE1)==%d\n"), sz1); _tprintf(_T("sizeof(BYTE2)==%d\n"), sz2); _tprintf(_T("sizeof(BYTE3)==%d\n"), sz3); //MSVC使用__alignof获得结构体中最大成员变量的对齐大小,即结构体的对齐大小 sz1 = __alignof(BYTE1); //最大成员为i1,对齐大小应为4 sz2 = __alignof(BYTE2); //最大成员为ch2,对齐大小应为1 sz3 = __alignof(BYTE3); //最大成员为i3,对齐大小应为4 _tprintf(_T("__alignof(BYTE1)==%d\n"), sz1); _tprintf(_T("__alignof(BYTE2)==%d\n"), sz2); _tprintf(_T("__alignof(BYTE3)==%d\n"), sz3); return 0; }
/*********************************************************************************
64位 计算机 最大内存,Windows32位/64位系统最大支持多大内存及不支持的原因
想必大家都知道了,Windows XP/Win7/Win8/Win10系统除了有旗舰版、专业版、家庭版的版本区分之外,还有32位和64位的区别。区别在哪呢?其中之一就是系统支持的最大内存。来跟下载吧小编一起了解下Windows系统32位/64位系统支持多大内存。
32位/64位系统支持多大内存
32位:系统能够识别的内存最大在3.25-3.5GB左右,也就是说4GB的内存条有0.5GB左右用不了。2GB内存条或者2GB+1GB内存条用32位系统丝毫没有影响。
64位:对于64位系统的Windows 7,最大内存支持就要视不同的系统版本而定了。比如,Windows 7 Home Basic 64位最大仅支持8GB内存、Home Premium为192GB,此外,高端的Enterprise、Ultimate等则支持支持192GB的最大内存。
32位系统不支持大内存的原因
一、换算关系的差异
众所周知,电脑中二进制中的换算关系是 1GB=1024MB,实际生产时,硬件厂商厂商的换算单位是1GB=1000MB,当电脑厂商告诉您,您电脑中的内存是1GB的时候,其实您的内存是1000MB,系统识别时,用1000处以1024,也就显示您的内存是0.976GB,同理您安装了4G的内存,系统会显示您的内存是3.9GB。
二、32位系统架构限制
内存(RAM,也就是您的系统的寻址总空间)=内存条(物理内存,您的内存条是4GB的)+bios内存+缓存+显存+…,32位的电脑的寻址空间是2的32次方,也就是4G,系统将这4GB的地址分配给内存条,缓存,显存,硬件等。
系统分配寻址空间时是有优先级的, bios的内存,cpu的缓存,各个硬件的内存,显卡的显存等,他们的优先级比物理内存(也就是内存条)高,所以系统先将寻址空间分配给bios的内存、cpu的缓存、各个硬件的内存和显卡的显存,最后才将剩余的寻址空间分配给内存条。比如您的显卡有500MB,那么可以分配的内存也就只剩下4GB-0.5GB=3.5GB了。
相当于一条街上有1000户居民,但是门牌号码确实只有800个,于是还有200户居民没能获得门牌号码,但是这200户居民并未消失。
如果在32位电脑上装上4G内存条依照优先级关系,你只能使用3G多的的内存条空间,因为前面的几百MB空间是给了各个硬件(包括显存),4GB的内存条只有其中的3.31GB获得了寻址空间,此时您只可以看到获得了寻址空间的那一部分内存条(见下图.)
如果是2G的内存条,因为电脑本身寻址能里是4G,前面几百MB给了硬件,依旧剩余3G多的寻址空间, 2G的内存条可以完全获得2G的寻址空间,您也就能看到2G的内存条了
/****************************************************************
理论上:32位=2^32B = 4 * 2^30B = 4GB,这是 32 位下单进程内存上限
目前(2015年5月),Intel的32位架构下,可使用的地址线是36个,可使用的最大物理地址是2^36B,折合64GB,可用的地址空间是4GB。
64位架构下,地址线是46个,所以最大的物理地址是2^46B,折合64TB,可用地址空间也是这么大(目前为止):
为什么32位Windows中实际可用内存少于4G:
虽然物理地址有4G,但CPU如果要访问你的显卡上的显存,就必须把物理地址空间的一部分用于标识显存,这种技术被称为memory-mapped I/O,这样可以让CPU操作显存像操作内存一样。
在Windows的设备管理器里,查看显卡的属性页里的“资源”,可以看到内存映射的地址范围,实际上不管32位还是64位,都需要占用一部分地址空间,32位Windows只有4G的地址空间,被显存占去了一部分,所以可用的地址空间就少于4G了
实际上不仅仅是显存,你电脑上的PCI控制器,各种网卡、声卡、USB控制器等等都需要占用一定的物理内存,所以最终的后果就是可用的物理地址范围变得特别少,最少可能只有1.99GB,这种限制在32位Windows上是不可改变的。
所以,32位WIN7的可用内存才变得那么少,解决问题的唯一途径是换64位系统,或者Linux。
CPU | CPU Address Bus Size | Maximum RAM |
8086 | 20 bit | 1MB |
8088 | 20 bit | 1MB |
80286 | 24 bit | 16MB |
80386SX | 24 bit | 16MB |
80386DX | 32 bit | 4GB |
80486SX | 32 bit | 4GB |
80486DX | 32 bit | 4GB |
Pentium I | 32 bit | 4GB |
K6 | 32 bit | 4GB |
Duron | 32 bit | 4GB |
Athlon | 32 bit | 4GB |
Athlon XP | 32 bit | 4GB |
Celeron | 36 bit | 64GB |
Pentium Pro | 36 bit | 64GB |
Pentium II | 36 bit | 64GB |
Pentium III | 36 bit | 64GB |
Pentium 4 | 36 bit | 64GB |
Athlon | 40 bit | 1TB |
Athlon-64 | 40 bit | 1TB |
Athlon-64 FX | 40 bit | 1TB |
Opteron | 40 bit | 1TB |
Itanium | 44 bit | 16TB |
Itanium 2 | 44 bit | 16TB |
PAE确实可以让x86硬件支持到能够访问64G的地址空间,Windows的内核也支持使用多达64G的物理地址空间,但是微软偏偏要对各个版本的Windows的物理内存上限作出限制,下面是XP和Win7的物理内存上限
/*********************************************************
物理地址、虚拟地址、逻辑地址、线性地址、虚拟内存
1.物理地址
用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相对应。
在实地址模式(因为实模式没有分段或分页机制,Cpu不进行自动地址转换)下,程序员操作的就是物理地址,所谓的物理地址就是物理内存上的32位地址,即物理地址可以直接定位到物理内存上的位置,无论任何操作,最终都必须要得到物理地址才能在物理内存上进行操作.
2.虚拟地址 操作系统都提供了虚拟内存(virtual memory)管理抽象。进程使用虚拟内存中的地址,由操作系统协助相关硬件,把它“转换”成真正的物理地址。
有了虚拟内存,一个程序就可以使用比真实物理地址大得多的地址空间。所谓的虚拟地址,从广义上讲,不是物理的就是虚拟的,因为不是物理地址的地址是无法在物理内存上定位的,所以他们都可以被称为”虚拟地址”,从这个意义上讲,逻辑地址和线性地址都可以被称为虚拟地址;而从狭义上讲,虚拟地址指的是没有经过分页机制和分段机制转换的地址,也就是段寄存器和变址寄存器内容的组合,从这个意义上来说,虚拟地址就是类似于CS:SI这样形式的地址.
3.逻辑地址(Logical Address)
逻辑地址就是上层程序员可以操作的地址,和段相关的偏移地址部分,也就是变址寄存器中存储的32位偏移地址,而其他寄存器上的地址往往对于上层程序员来说是不可更改甚至是不可见的. 只有在实模式下,逻辑地址才和物理地址一致(因为实模式没有分段或分页机制,Cpu不进行自动地址转换);逻辑地址也就是在保护模式下程序执行代码段限长内的偏移地址(假定代码段、数据段如果完全一样).应用程序员仅需与逻辑地址打交道,而分段和分页机制对您来说是完全透明的,仅由系统编程人员涉及.应用程序员虽然自己可以直接操作内存,那也只能在操作系统给你分配的内存段操作.
4.线性地址
对狭义上的虚拟地址通过分段机制以后,可以得到段基址、段界限以及段偏移地址(即逻辑地址),段基址与段偏移地址的组合就是线性地址,线性地址可以在虚拟内存上完成定位。如果启用了分页机制,那么线性地址可以再经变换以产生一个物理地址.若没有启用分页机制,那么线性地址直接就是物理地址.对于程序员来说,他们并不关注MMU如何工作以及其得到的结果,他们只需要关心线性地址或者逻辑地址就可以完成全部工作了.
5.虚拟内存
是指计算机呈现出要比实际拥有的内存大得多的内存量.因此它允许程序员编制并运行比实际系统拥有的内存大得多的程序.这使得许多大型项目也能够在具有有限内存资源的系统上实现.
很多内存管理策略都需要同时将多个进程放在内存中,以便执行这些进程,这些策略都需要在进程执行之前将整个进程放在内存中。而虚拟内存技术允许执行进程不必完全放在内存中,这样我们就可以运行比物理内存大的程序,使得程序员不受内存存储的限制。
比如说异常处理、错误处理可能不经常执行或者就不执行,这些程序使得我们可以只需要加载需要执行的部分,从而减少了内存使用。
从而,构造一个大的虚拟内存空间,将其映射到较小的物理内存。这个大的虚拟内存空间存储我们进程的所有信息,而当执行进程时,我们只加载需要执行的部分,。这里就需要采用一定的技术,比如按需调页、页面置换、帧分配等,使得进程的执行可以在虚拟内存和物理内存之间进行协调,完成整个程序的执行。
优点:虚拟内存可以大于物理内存,一般为物理内存的1.5倍到3倍,从而可以运行比物理内存大的程序,进而使得更多的程序可以同时执行,提高了多道程序的程度,增加了CPU的使用率,并且使得进程之间的独立性得到了更好的体现。
6.内存的基本管理(页 段 段页)
(1)通常将逻辑内存进行分页,将逻辑内存分成很多小的页面,然后通过页表,映射到物理内存,而物理内存则划分为很多成为帧的块,从而和页对应起来,页和帧的对应关系主要是通过页表来保存的,页表中有很多条目,较为详细地保存了这些信息。进程都有自己的内存空间(虚拟内存),上面所说的虚拟内存技术就是指进程的虚拟内存空间存储了所有的进程信息,然后虚拟内存空间分成很多页,这些页并不是在进程执行时全部换入到物理内存,而是按照需要进行换入,这需要一定的算法管理。
虚拟内存(虚拟存储器)是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,
虚拟存储器是由硬件和操作系统自动实现存储信息调度和管理的。它的工作过程包括6个步骤:
(1)CPU访问主存的逻辑地址分解成组号a和组内地址b,并对组号a进行地址变换,即将逻辑组号a作为索引,查地址变换表,以确定该组信息是否存放在主存内。 (2)如该组号已在主存内,则转而执行④;如果该组号不在主存内,则检查主存中是否有空闲区,如果没有,便将某个暂时不用的组调出送往辅存,以便将这组信息调入主存。 (3)从辅存读出所要的组,并送到主存空闲区,然后将那个空闲的物理组号a和逻辑组号a登录在地址变换表中。 (4)从地址变换表读出与逻辑组号a对应的物理组号a。 (5)从物理组号a和组内字节地址b得到物理地址。 (6)根据物理地址从主存中存取必要的信息。 调度方式有分页式、段式、段页式3种。
页式调度是将逻辑和物理地址空间都分成固定大小的页。主存按页顺序编号,而每个独立编址的程序空间有自己的页号顺序,通过调度辅存中程序的各页可以离散装入主存中不同的页面位置,并可据表一一对应检索。页式调度的优点是没有外碎片只有内碎片,页表对程序员来说是透明的,地址变换快,调入操作简单;缺点是各页不是程序的独立模块,不便于实现程序和数据的保护。
段式调度是按程序的逻辑结构划分地址空间,段的长度不等的,并且允许伸长,它的优点是消除了内碎片有外碎片,易于实现存储保护,便于程序动态装配;缺点是调入操作复杂。
段页式调度中把物理空间分成页,程序按模块分段,每个段再分成与物理空间页同样小的页面。段页式调度综合了段式和页式的优点。其缺点是增加了硬件成本,软件也较复杂。大型通用计算机系统多数采用段页式调度。
/***********************************************
地址总线宽度决定了CPU可以访问的物理地址空间,简单地说就是CPU到底能够使用多大容量的内存。
数据总线负责整个系统的数据流量的大小,而数据总线宽度则决定了CPU与二级高速缓存、内存以及输入/输出设备之间一次数据传输的信息量。
--------------------------------------
按总线的功能(传递信息的内容)分类,计算机中有三种类型的总线,即传送数据信息的数据总线、传送地址信息的地址总线和传送各种控制信息的控制总线。
1.数据总线
数据总线是CPU与存储器、CPU与I/O接口设备之间传送数据信息(各种指令数据信息)的总线,这些信号通过数据总线往返于CPU与存储器、CPU与I/O接口设备之间,因此,数据总线上的信息是双向传输的。
2.地址总线
地址总线上传送的是CPU向存储器、I/O接口设备发出的地址信息,寻址能力是CPU特有的功能,地址总线上传送的地址信息仅由CPU发出,因此,地址总线上的信息是单向传输的。
3.控制总线
控制总线传送的是各种控制信号,有CPU至存储器、I/O接口设备的控制信号,有I/O接口送向CPU的应答信号、请求信号,因此,控制总线是上的信息是双向传输的。控制信号包括时序信号、状态信号和命令信号(如读写信号、忙信号、中断信号)等。
机器字长:CPU一次能处理数据的位数,通常与CPU的寄存器位数有关。
存储字长:存储器中一个存储单元(存储地址)所存储的二进制代码的位数,即存储器中的MDR的位数。
指令字长:计算机指令字的位数。
数据字长:计算机数据存储所占用的位数
计算机存储基本单位:
位(bit):是计算机中最小的数据单位,存放一位二进制数,即0或1。它也是存储器存储信息的最小单位,通常用“b”来表示。
字节(Byte):字节是计算机中表示存储容量的最常用的基本单位。一个字节由8位二进制数组成,通常用“B”表示。一个字符占一个字节,一个汉字占两个字节。其它常见的存储单位有:
1KB (Kilobyte 千字节)=1024B
1MB (Megabyte 兆字节 简称“兆”)=1024KB
1GB (Gigabyte 吉字节 又称“千兆”)=1024MB
1TB (Trillionbyte 万亿字节 太字节)=1024GB
字节的位数永远是8位,而字的位数却随着CPU处理能力的改变而改变。
在16位CPU中,一个字表示16位(两个字节)
在32位CPU中,一个字表示32位(四个字节)
在64位CPU中,一个字表示64位(八个字节)
寻址范围:
最大寻址范围:
寻找的地址是内存单元的地址。例如:一个地址总线由6根地址线组成,那么他的地址线的最大寻址范围是2的6次方,也就是64个内存单元。超出了这个范围的内存单元则访问不到,需要增加地址线才可以访问的到。
例题:假设CPU有20根地址线,32根数据线,试问按字节和字寻址,寻址范围分别是多少?
按字节寻址:20根地址线也就是
按字寻址:🦑有32根数据线,所以一个字要四个字节表示;四个字节需要两个二进制表示00表示第一个字节,01表示第二个字节,10表示第三个字节,11表示第四个字节。因此按字寻址:
/*********************************************************
首先要区分两个词:
寻址空间应该反映的是内存的容量的大小。
寻址范围应该是一个反映地址取值的数值范围。
来看看问题:
寻址范围的概念怎么理解?例如地址线20根,数据线16根,按字寻址为什么是1MB,而不是2MB?按字寻址为什么是512KB,而不是1MB?
实际上题目中的这种描述是很模糊的,因为1MB也好,2MB也好描述的都是描述寻址空间的,而下面的题目问的却都是寻址范围。在21版上第5页末尾明明已经说明寻址范围不带B和bit等单位,在下一页却依然将寻址范围表述成 2 20 2^{20}2
20
=1MB,不得不吐槽一下这本书实在很不严谨。
1.几个存储相关的概念
首先来了解几个基本概念,比特位和字节这两个都好理解,但是字长这一块时间长了就有些模糊了,这也是理解不到位的主要原因。
字位 (bit):计算机存储信息的最小单位,即二进制位。
字节(Byte):计量存储容量的一种计量单位,通常情况下1Byte=8bits。
字长(Word):计算机同一时间可以处理的二进制数位数叫字长,我们平时说计算机是多少位的,那么相应的字长就是多少个字位,例如对于32位的计算机,它一次就能处理32位数据,它的字长就是32bits=4Bytes。
2.地址总线和数据总线的概念
总线是计算机中传输数据的公共通道, 按照其功能大体上可以分为地址总线和数据总线。
数据总线是双向三态形式的总线,它既可以把CPU的数据传送到存储器或I/O等其它部件,也可以将其它部件的数据传送到CPU。需要指出的是,数据的含义是广义的,它可以是真正的数据,也可以指令代码或状态信息,有时甚至是一个控制信息,因此,在实际工作中,数据总线上传送的并不一定仅仅是真正意义上的数据。
地址总线是专门用来传送地址的,且地址只能从CPU传向外部存储器或I/O端口,这与数据总线不同。地址总线的位数决定了CPU可直接寻址的内存空间大小。
3.回归问题
由上面的内容不难分析出,一根地址线可以看成一个bit位,可以表示出0和1这两个数值,可以用这连个数值代表两个地址。比如0代表地址0,1代表地址1,对于按字节寻址,一个地址对应的空间大小就是一个字节,所以两个地址可以寻址的范围就是2,可寻址空间就是两个字节。
于是如果地址总线有n条,那么实际可以用来表示内存空间的就是n个bit位,n个bit位可以表示的数据范围是 0 00~2 n − 1 {2^n-1}2
n
−1 共2 n 2^n2
n
个数,那么可寻址范围就是 2 n 2^n2
n
,按字节寻址可寻址空间就为 2 n 2^n2
n
Bytes 。
则对于有20根地址总线来说,它的可寻址范围就是:
2 20 = 2 10 K = 1 M 2^{20}=2^{10}K=1M
2
20
=2
10
K=1M
按字节寻址可寻址空间就是:
2 20 B y t e s = 2 10 K B = 1 M B 2^{20}Bytes=2^{10}KB=1MB
2
20
Bytes=2
10
KB=1MB
这里要注意单位,也就是按多大的空间来寻址,那么当按字长来寻址的时候,字长的大小不是固定的,它由计算机位数决定的,而计算机位数又跟数据总线条数有关,所以需要用到数据总线的相关信息。由字长的大小不难得出,对于有16根数据总线的来说,它的字长就是:
16 b i t s = 2 B y t e s 16bits=2Bytes
16bits=2Bytes
也就是说一个字长就有两个字节。上面说过按照字节寻址范围是1M,因为是按字节寻址,每个寻址单元已经是最小的了,所以不需要字内寻址,如果按字寻址,对于题目中的字长两字节,则需要字内寻址,即区分字内这两个字节的地址)。
对于按字寻址,由于需要区分字内字节地址,所以不能把20根地址线全拿出来按字寻址,也要分部分来进行字内寻址。那么到底分出几根呢?
对于字长为2字节的字内寻址来说,里面只有两个字节,所以一个bit位(“0″和”1″两种状态)即可区分出它们。由此不难得到,对于字长为4个字节的字内寻址,要区分出4种状态,那么我们需要2个bit位来实现(“00”,“01”,“10”,“11”)。以此可以类推。。。
回到题目中来,按照字来寻址的时候,由于要分出一根地址线来进行字内寻址,所有只有19根地址线可以用来进行按字寻址,所以按字寻址的可寻址范围就是:
2 19 = 2 9 K = 512 K 2^{19}=2^9K=512K
2
19
=2
9
K=512K
4.总结
按字节寻址的时候,可寻址范围只跟地址总线条数有关,一般对于n条地址总线,可寻址范围为 2 n 2^n2
n
,可寻址空间为 2 n 2^n2
n
Bytes。
按字长寻址的时候,字长大小由数据总线的条数决定,当一个字里包含多于1个字节的时候需要区分这些字节所以要进行字内寻址,当然也是通过地址总线来实现,这个时候需要分出地址总线来进行字内寻址。
今天的文章windows内存是什么意思_关于windows7窗口的概念分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/84051.html