龙空技术网

1.3 内存管理模式

爱达人编程达人 249

前言:

目前大家对“vmnet8怎么启用”都比较珍视,同学们都需要剖析一些“vmnet8怎么启用”的相关资讯。那么小编也在网上搜集了一些关于“vmnet8怎么启用””的相关内容,希望各位老铁们能喜欢,同学们一起来了解一下吧!

本节必须掌握的知识点:

4GB虚拟空间

虚拟内存

多任务切换

1.3.1 4GB虚拟空间

■Win16操作系统

Windows1.0版本为16位操作系统,支持16位处理器实模式,最大寻址空间为1MB。Win16操作系统的内存管理非常简单,采用分段内存管理模式,最小段为16个字节,最大段为64KB,整个1MB内存空间都是透明的,没有任何访问限制。逻辑地址为“段值:偏移”,段值和偏移都是16位。将段值左移4位+偏移得到物理地址。在这种模式下,CPU的寄存器的宽度为16位,因此C语言的int数据类型的宽度也是16位。

■Win32操作系统

当体系结构从16位到32位的时候,Windows API及其语法发生了最大改变。Windows1.0到3.1版本一直采用16位的分段内存管理模式。为了保证兼容性,INTEL的386之后的32位处理器也都支持这一模式。从Windows NT及Windows 95开始,利用32位intel 386、486以及奔腾处理器,Windows开始支持32位的flat平坦内存管理模式。C语言的int数据类型也因此扩展成32位宽。

Windows32位应用程序按照32位最大寻址范围4GB虚拟空间编写源程序。由于Win32操作系统为保护模式,普通应用程序只具有R3权限,只可以访问低2GB虚拟内存空间(高2GB空间为操作系统内核),在源程序中只需要使用32位偏移地址。

在32位保护模式中,仍然采用分段和分页模式管理内存空间。32位段基址为起始地址,源程序中的32位偏移地址为段内偏移。想要将源程序中的32位偏移地址转换为物理地址需要经过两个步骤:

●步骤一:将32位源程序中的逻辑地址转换为32位线性地址。

转换方法为:32位段基址+32位偏移地址=32位线性地址。

新的问题来了,在32位处理器中的段寄存器只有16位,且段寄存器数量只有6个。由于段寄存器无法存放32位段基址。为此,Windows操作系统设计了段选择子和段描述符表,段选择子为16位,作为段描述符表的索引存入段寄存器。段描述符表的每个表项为一个64位值,其中32位为段基址,20位为段界限域(定义段大小),12位段属性(定义段权限)。描述符表项的索引为13位,作为段选择子的高13位。段选择子的第0位和第1位存放RPL(段权限请求R0~R3)。段选择子第2位为0时,表示访问全局段描述符表并查找段基址,第2位为1时,表示访问局部段描述符表并查找段基址。找到对应的段基址后,再加上32位偏移地址就得到了32位线性地址。

那么如何找到段描述符表呢?为此,32位处理器增加了GDTR全局段描述符表寄存器、LDTR局部段描述表寄存器、中断描述符表寄存器IDTR和任务TR寄存器。GDTR寄存器为48位寄存器,存储全局段描述符表的32位地址和16位段界限。指令LGDT和SGDT分别用于加载和保存GDTR寄存器的内容。操作系统中只有一个GDT全局段描述符表。局部描述符表寄存器LDTR和任务寄存器TR为16位寄存器,存储局部描述符表段和任务段选择子,这是因为局部描述符表和任务段本身是作为全局描述符表的表项存在的,Windows操作系统可以同时存在多个局部段描述表和TSS任务段。如果需要访问局部段描述符表或TSS任务段可以使用LDT指令或LTR指令将段选择子存入LDTR和TR寄存器。

看到这里,读者可能会产生一些畏惧的情绪,Win32位系统寻址尽然如此复杂。如果你仅是一名Windows应用程序员,完全没必要担心。在应用程序的源程序中只需要使用32位的偏移地址就可以了。至于段基址和段权限这些内容都是使用操作系统默认值,应用程序的段基址通常为0,而段的大小是以页(4KB)为单位分配,在编译时由编译器更具源代码中分段的大小确定。至于段属性只能是R3特权级。接下来我们做一个实验。

■实验一:查看32位应用程序的内存分配

打开DtDebug调试器,随便拖入一个32位应用程序,如图1-1所示。

【注】本机环境为X64处理器,Windows 10操作系统。

图1-1 观察段寄存器的值

●观察段寄存器的值

图中ES附加段寄存器的值为002BH,CS段寄存器的值为0023H,SS段寄存器的值为002BH,DS寄存器的值为002BH。这4个段寄存器的段选择子中的权限请求都是R3(低两位的值为3),表明当前进程的特权级为R3。FS段寄存器指向线程环境块,在32位程序中GS段寄存器通常用来指向TLS本地线程存储。

【注意】不同版本的Windows操作系统中默认的段寄存器的值可能会不同,但是其特权级一定是相同的。

●观察内存分配

图1-2 查看内存窗口

如图1-2所示,点击工具栏内存窗口“M”,在内存中可以清晰的看到当前进程HelloWord.exe的内存分配情况。在调试器中只显示低2GB空间的内存分配。Address一栏显示32位线性地址(每个段的基址),Size一栏显示内存分配大小(以4KB页为单位)。Owner一栏显示进程名称,Section一栏显示节区名(段名)。在低2GB空间的高地址处加载dll系统动态链接库。0x7FFF0000~0x7FFFFFFF和0~0xFFFF为禁止访问区域,其余空白空间为进程环境块、线程环境块、堆区和栈区。

如果读者想要更深入的学习Windows系统内核,请留意我们关于操作系统内核的课程。

●步骤二:将32位线性地址转换为物理地址

在转换之前首先需要考虑的是物理地址是多少位。如果物理内存小于4GB,那么物理地址使用32位就可以了。如果物理内存大于4GB,可以考虑使用36位,可以寻址的范围就是64GB了。剩下的问题就是如何将32位线性地址转换为32位或36位或者更多位的物理地址了。我们以小于等于4GB物理内存为例,对应的物理地址是32位,以4KB页为单位分配内存。我们可以按照10-10-12分页机制创建一个PageTable地址映射表。如图1-3所示,32位线性地址中的高10位为页目录表索引,32位线性地址中间10位为页表索引,32位线性地址低12位为偏移地址。创建进程初始化时创建了地址映射表,页目录表的物理基址存储在CR3寄存器中。

图1-3 10-10-12分页机制

转换公式:页目录表基址+PDI*4+PTI*4+12位偏移。

每个进程在初始化时都会创建一个CR3(准确的说是一个CR3的值,CR3本身是个寄存器,一个核,只有一套寄存器), CR3指向一个物理页就是页目录表,一共1024个表项(也就是1024个页表),每一项32位,共4096字节(4KB)。每一个页表中也有1024个表项,每一项都是一个32位的物理页基址。每一个页表是4KB大小,一共1024个页表(页目录表本身为其中一个页表),因此地址映射表一共占用4MB大小的空间,并且存储在高2GB空间中。我们将高2GB空间虚拟地址0xC0300000和CR3中存储的页目录表的物理页地址对应,将虚拟地址0xC0000000与页表的第0页对应,因此只需要使用虚拟地址0xC0300000或0xC0000000就可以直接访问地址映射表了。通过查表的方法将32位线性地址转换为32位物理地址。

以此类推,如果物理内存大于4GB,可以使用2-9-9-12分页机制建立地址映射表。也可以是其它类似的分页机制,只要可以正确访问物理地址就可以了。

注意

只有真正发生读写操作的时候,才需要查找地址映射表,访问物理内存。

■Win64操作系统:

Windows 64位操作系统理论上可以访问的最大内存为264,但实际只使用了低48位,可以访问的248TB内存空间。Windows 64位操作系统寻址变得简单了,DS、CS、SS段寄存器不再需要,没有了分段机制,只保留了分页机制。Windows 64位系统采用9-9-9-9-12分页机制,如图1-4所示:

Win64操作系统分页机制原理和Win32操作系统分页机制原理相同,此处不再赘述。

图1-4 9-9-9-9-12分页机制

1.3.2 虚拟内存

上一小节我们讲述了4GB虚拟空间,这一小节我们讲解虚拟内存。很多读者无法正确区分虚拟内存和虚拟空间。

■虚拟空间

4GB虚拟空间是我们编写32位Windows应用程序的模板,我们在源程序中按照32位逻辑地址编写应用程序,程序可以访问的虚拟地址空间的大小为4GB。

■虚拟内存

●分页机制

虚拟内存是和页交换机制密切相关的。早期的32位计算机的物理内存很小,可能只有512KB,后来随之集成电路的发展,物理内存逐渐扩大到1GB、2GB、4GB、8GB,今天32GB的物理内存已经是比较常用的了。32位应用程序按照4GB模板编写,随着应用程序的功能越来越复杂,程序的体积必然越来越大。而且Win32系统支持多任务,可以同时加载多个进程,这必然涉及到一个非常严峻的问题,物理内存不足。为了解决这个问题,Windows操作系统采用了分页机制和页交换机制。由于地址映射表的存在,我们可以通过映射机制(映射就是查表的意思)将虚拟地址转换为物理地址。Windows操作系统以4KB页为单位分配虚拟4GB空间,如图1-2所示,HelloWord.exe进程初始化时一共分配了4个页,其中PE头分配了一页,代码段.text分配了一页,.rdata节区分配了一页,数据段.data分配了一页。与此对应,物理内存足够分配4个对等的物理页,并将其填写到地址映射表中,与虚拟线性地址想对应。

●页交换机制

假设HelloWord.exe进程体积大小为4GB,再加上系统DLL、堆栈、进程环境块和线程环境块占用的内存空间,在低2GB虚拟空间中无法正常加载整个进程,只能先加载部分页。再假设物理内存只有512MB大小,在地址映射表中实际映射的物理页必然小于实际需要的物理页。为了保证已加载的页通过地址映射表可以正常访问物理内存,当前已加载的部分页(必定小于512MB)。当前已加载页执行完之后,后续的页由于未加载,引发缺页异常,Windows操作系统的异常处理程序将已执行过的页存储到物理磁盘上的页交换文件中,然后在装载将要访问的页面,并重新建立物理地址映射后继续执行。我们把这个过程称之为页交换机制。我们将磁盘上的交换文件和物理内存一并称之为虚拟内存,虚拟内存的大小=页交换文件的大小+实际物理内存的大小。实际情况是,操作系统同时加载了多个类型HelloWord.exe进程,实际需要的内存要大的多。接下来我们动手做一个实验为证。

■实验二:查看虚拟内存和物理内存

打开任务管理器,如图1-5所示:

图1-5 任务管理器查看内存

图中显示当前机器物理内存为16GB,正在使用中的物理内存为5.8GB,剩余可以使用的物理内存为10GB。注意下方,已经提交的内存为17.6GB,已申请使用的内存为22.9GB,这里的内存已经超出物理内存16GB的大小,必定是指虚拟内存。

此外,我们还可以通过另外一种方式查看当前Windows系统的虚拟内存大小。在控制台窗口输入命令:systeminfo,查看当前机器信息如下所示:

C:\Users\16400>systeminfo

主机名: DESKTOP-ENIJKEB

OS 名称: Microsoft Windows 10 教育版

OS 版本: 10.0.16299 暂缺 Build 16299

OS 制造商: Microsoft Corporation

OS 配置: 独立工作站

OS 构件类型: Multiprocessor Free

注册的所有人: 暂缺

注册的组织: 暂缺

产品 ID: 00328-10000-00001-AA821

初始安装日期: 2018/12/22, 22:50:35

系统启动时间: 2023/11/21, 7:01:29

系统制造商: MSI

系统型号: MS-7A59

系统类型: x64-based PC

处理器: 安装了 1 个处理器。

[01]: Intel64 Family 6 Model 158 Stepping 9 GenuineIntel ~4200 Mhz

BIOS 版本: American Megatrends Inc. A.50, 2017/3/31

Windows 目录: C:\Windows

系统目录: C:\Windows\system32

启动设备: \Device\HarddiskVolume1

系统区域设置: zh-cn;中文(中国)

输入法区域设置: zh-cn;中文(中国)

时区: (UTC+08:00) 北京,重庆,香港特别行政区,乌鲁木齐

物理内存总量: 16,271 MB

可用的物理内存: 9,627 MB

虚拟内存: 最大值: 23,393 MB

虚拟内存: 可用: 4,847 MB

虚拟内存: 使用中: 18,546 MB

页面文件位置: C:\pagefile.sys

域: WORKGROUP

登录服务器: \\DESKTOP-ENIJKEB

修补程序: 安装了 13 个修补程序。

[01]: KB4534129

[02]: KB4134661

[03]: KB4295110

[04]: KB4462930

[05]: KB4471331

[06]: KB4477136

[07]: KB4480979

[08]: KB4486153

[09]: KB4486155

[10]: KB4541731

[11]: KB4561600

[12]: KB4562560

[13]: KB4561602

网卡: 安装了 3 个 NIC。

[01]: VMware Virtual Ethernet Adapter for VMnet1

连接名: VMware Network Adapter VMnet1

启用 DHCP: 是

DHCP 服务器: 192.168.194.254

IP 地址

[01]: 192.168.194.1

[02]: fe80::6c0e:7577:e534:10d6

[02]: VMware Virtual Ethernet Adapter for VMnet8

连接名: VMware Network Adapter VMnet8

启用 DHCP: 是

DHCP 服务器: 192.168.76.254

IP 地址

[01]: 192.168.76.1

[02]: fe80::5d7b:2e73:7d56:c7f6

[03]: Intel(R) Ethernet Connection (2) I219-V

连接名: 以太网

启用 DHCP: 否

IP 地址

[01]: 192.168.2.105

[02]: fe80::4d62:17e6:4fc3:95a4

Hyper-V 要求: 虚拟机监视器模式扩展: 是

固件中已启用虚拟化: 是

二级地址转换: 是

数据执行保护可用: 是

【注意】页交换文件位置: C:\pagefile.sys

1.3.3 多任务切换

■DOS系统任务切换

回顾一下简单直白的DOS操作系统。DOS操作系统为单任务系统,处理器和操作系统本身并不支持多任务切换。聪明的程序员自己想办法实现了DOS系统的任务切换。当一个任务(进程)退出时,将其一部分程序驻留在内存中,然后通过热键激活驻留程序实现任务切换。还可以通过另外一种方式,设置一个时钟,到了指定时间执行特定的任务。这两种方式从本质上都是通过中断(键盘中断和时钟中断)实现任务切换。

■Windows系统任务切换

我们再看Windows操作系统是如何实现任务切换的。先明确一下什么是任务。在单核处理器时代,一个进程只有一个线程,任务等同于进程或线程。到了多核处理器时代,一个进程可以同时拥有多个线程,此时,任务特指线程,任务切换就是线程切换。

●Windows任务切换可以分为几种情形:

情形一:线程时间片到期,切换线程就绪队列中的新线程。

情形二:有了更高优先级的线程进入就绪队列,终止当前线程,切换到优先级高线程。

情形三:线程结束,自然退出。

情形四:强制结束当前线程。

线程切换时,需要将线程信息保存到线程环境块中,以备再次轮循执行。

●在应用程序中如何实现任务切换呢?32位处理器提供了几种任务切换的方法,包括调用门、中断门、陷阱门和中断门。而实际情况是,Windows操作系统只使用了陷阱门和中断门,部分使用了任务门,完全未使用调用门。不过作为程序员,可以利用处理器提供的所有方法实现任务切换。

作为Windows应用程序的开发者不必担心,任务切换的实现都是隐藏在现有的API函数调用中,只需掌握其调用方法就可以了。如果你是一个有追求的开发者,可以进一步深入学习,这里作者推荐《Windows核心编程》这本书经典的教科书,也可以参考编程达人关于保护模式的Windows内核相关的系列教材。

标签: #vmnet8怎么启用