Windows 95的系统结构
Windows 95在许多方面类似于Windows3.1,尤其是虚拟机 VM(Virtual Machine)。基于Windows的应用都运作在系统的VM状态。旧的16位应用程序共用单一的地址空间,而新的32位应用程序则不共用地址空间,每个应用程序都有自己的私有地址空间。Windows 95通过操纵页表来达到这一目的。因此,一个应用程序看不见另一个应用程序,除非它们明确共享内存储器。Windows 95也支持虚拟8086模式或保护模式下MS-DOS程序的运作。
虚拟机管理器VMM(Virtual Machine Manager)是Windows 95系统的核心,它除了为虚拟设备驱动程序提供服务外,还提供低级内存储器管理和调度服务。
Windows 95的文件系统是新提供的主要部件,是完全重新设计的子系统,能同时支持多种文件系统。而在Windows3.1中,MS DOS的文件系统支持本地磁盘,虽然也支持CD-ROM和网络文件系统,但性能不佳。
Windows 95中,除了一些特殊设备需要旧的设备驱动程序外,其整个档案系统都是保护模式下的32位代码。此文件系统不仅支持本地磁盘和CD-ROM,而且还通过可安装档案系统接口(IFS),并可支持一个或多个网络接口,因此,利用Windows 95,用户可以在保护模式下建成一个很好的系统。此系统可以连接硬盘、软盘、CD-ROM、Bernoulli盒、基于 WindowsNT的服务器、Netware网络等。
Windows 95的人机界面服务机制与Windows3.1类似,但做了一些改进,以方便用户。系统服务中的KERNEL,USER,GDI仍用以前 Windows NT版本的名字,主要改变是,它们都已变为32位的,性能更好。如果用户开发应用程序,则要运作Win32 API,调用的其他服务也都是32位的,对内存储器管理的需求也不同以前,其用户的程序是全32位的。
Windows 95系统的基本结构如图 1所示。

二、系统保护环
Windows 95充分利用386处理器的能力,支援两个特权级。它用0和3两个特权级管理微处理器,也可称为两个环。环0中的部件是操作系统的底层,如包括对低级内存储器管理的支持,环0里的软件在整个系统中功能最强,包括了几乎所有微处理器的指令,并能存取关键的数据结构,如页表等。因此环0里的软件最可靠。
Windows 95允许用户安装新的虚拟设备驱动程序(VXD),这些VXD可以支持后加的硬件或提供系统范围内的软件眼务。VXD都在环0里运作,因此,如果VXD有一点差错,就会使整个系统崩溃。而如果要开发一种软件,使其能和失败的VXD分离,那将是非常困难的。
Mictosoft公司的开发人员开发了一种可调试内核(ebug xernel),使程序员在安装VXD之前能够先检查VXD对VMM服务的所有需求,这样就会避免在安装以后出现问题,这个可调试内核包含在Windows 95的DDK中。
有些操作系统试图利用Intel 386处理器提供的附加特权级,而 Wndows 95没有这样做,它只利用了两级。对于绝大多数应用,这种“双环模式”(two-ring model)工作得相当出色。
在Intel处理器中,环的过度(特权级的改变)会增加程序的运作时间,以 lute 486为例,如果没有环的过度,一个子程序调用另一个段中的代码需要20个时钟周期,有环的过度则需要69个时钟周期。这是因为当处理器的特权级变化时,处理器控制会发生变化,寄存器要重装。因此,较少的环过度意味着性能的提高,这也是Wndows 95 把其圆形系统的多数代码放在环3的主要原因。
Intel 386及其以上处理器提供了 4GB虚拟存储空间,Window 95都可以利用。在虚拟地址空间里,不同的系统部件和应用程序,占用的区域都有固定的界限,其结构如2所示。

Windews 95的VMM的任务之一就是把4GB的虚拟地址空间映射到内存储器上。下面作一说明:
虚拟地址空间中,最低的IMB用作当前执行的 MS-DOS VM,每个 VM在 IGB,ZGB内也有一段空间,允许系统本身寻址不活动的 VM段存储空间,一旦 MS DOS VM运作起来,就会定位在最低的IMB空间里。
32位 Windows应用程序优先装入的空间为 4MB~1·5GB,标准开发工具也缺省地用这个空间,这主要和 WindOWS NT相匹配。当然,用户也可以选择较低的地址空间,这时,用户需要做起额外的工作。
系统为每个32位应用标记的低16KB空间不可获得,这主要是为了捕获程式的错误。许多程式常犯的错误是使用空指针,在Wndows 95中,空地址将产生内存储器错误,这对开发者及时发现指针错误是有益的。
三、虚拟机管理器
如前所述,虚拟机管理器VMM是Windows 95系统的核心,VMM的效率对整个系统的性能有看重要影啊。系统中许多复杂的部件都在这里。虚拟机管理器的代码包含以下几项特性:
在每个过程的私有地址空间里,32位的Windows应用程序都是抢先调度的;
每个32位的应用程序有一个私有的消息队列;
VXD能够动态装载和定位,减少系统的操作集;
许多系统资源都是按32位内存储器模式开发的,大大提高了系统能力。
Windows95有两种基本的VM:一是系统VM,KERNEL,USER,GDI部件和Windows的应用程序都在这里;M是 MS-DOS VM,运作一个MS-DOS应用程序,这个程序既能运作在虚拟8086模式,也能运作在保护模式。
四、支持MS-DOS应用程序
Windows支持MS-DOS应用程序,这就意味着当用户工作基于DOS的应用时,不必离开Windows95。Windows95软件也提供最新的MS-DOS 6.X版的代码和数据。
Windows 95支持单一的 MS-DOS应用模式。这种模式对MS-DOS应用提供绝对的兼容性。虽然开发人员做了很大的努力,使更多的MS-DOS程序能在MS-DOSVM下运作,但这种单一的MS-DOS应用模式为那些不能在Windows下运作的MS-DOS程序提供真正的兼容性,这就是说,此模式对以前的DOS版本向下兼容。
Windows 95因 MS DOS之关系,与W4indows3.1的最大区别是:基于Windows的应用完全不需要MS DOS代码支持。Windows已有许多的版本(如Win-dows3.1,Windows for Workgroup 3.1,Windows3·11等),每种都支持越来越多的MS DOS INT软件服务。而且,基于WindowS的应用程序在进出虚拟86模式时,对MS DOS代码的需要也在减少。
但这些版本的档案系统都没有什么变化,直到 Windows for Workgroup3,11版。只有Windows 95,最后打破了实模式MS DOS的所有束缚,几乎无不例外,甚至现存的16位Windows应用程序,也沿着保护模式的路径,通过新的档案管理系统进人磁盘。
五、虚拟机调度方式
Windows 95中的过程调度和虚拟机管理关系十分密切。在Windows 95中,线程是系统调度程序要处理的主要对象,也是调度的基本单元,假如用户熟悉Window NT,就会习惯处理线程。
下面是一些线程的特征:
(1)在过程里是一个可执行路径;
(2)能够被任何32位的Windows程序或运作在 Windows 95里的 VXD创建;
(3)有自己私有的堆叠存储器和执行价前后关系;
(4)固定的过程分享存储器;
(5)一个过程可以创建许多并发的线程。
线程有时也被称为“简单过程”(lighl weisht Process),因为创建和管理一个线程的操作比较简单。特别需要指出的是,一个线程可以分享其他过程的所有代码和全局数据,这意味着创建一个新的线程只需要很少的存储空间。
当Windows 95加载程序,以及创建与之联系的数据结构时,系统就把这个过程当做单一的线程——主线程来建立。许多程序在执行时只有一个线程。但只有32位的Windows应用程序和 Windows 95下的VXD可获得线程服务;MS DOS VM和16位的Windows应用程序不能调用线程的API。
一个MS DOS VM只提供一个线程,即一个MS DOS VM既是过程又是线程。16位的Windows应用程序也只有一个线程,这就为旧的应用程序保留了协同式多任务模式。任何VXD和 32位的 Windows 用程序都能创建附加线程,Windows 95能够抢先式调用这些线程。
Windows 95中的线程基于不同的程序来实现,如 MS-DOS应用程序、16位的Windows应用程序、32位的Windows应用程序等。但系统为每一个线程提供了相同的数据结构。因此,系统调度程序和系统内其他32位代码都可以用这些数据结构,并能顺利地在16位和32位应用程序中实现。
六、调试程序
Windows 95的VMM实际上有两个调试程序,即主调度程序和时间片调度程序。前者负责并保证最高优先级的线程一直在执行;后者负责动态调整线程的优先级,以便提供合理的多任务。
那么调度的过程是如何呢?首先,主调度程序先检查系统中的每一个线程,然后选择出优先级最高的线程来执行。为了与Windows NT兼容,优先级从0到 31,共32级,数字越大优先级越高。
同时,为了与以前的Windows版本兼容,设备驱动程序的优先级能设置成比这32级还高。比最高优先级低的线程,主调度程序不予考虑。这里,最高优先级未必是31,假如只有两个线程,优先级一个是20,一个是16,那么最高优先级就是20;如果此时又来一个优先级是ZI的线程,那么21就成为最高优先级。
时间片调度程序增加了一个对前台线程和后台线程优先级升高的能力。为了阻止高优先级的线程独占CPU,时间限制器会周期地升高那些当前未运作的非停止的优先级线程,这就避免了后台线程被挂起来。在缺省情况下,线程的优先级每隔20ms重新计算一次。
除了纯粹的定量求优先级的值外,时间调度程序根据当前VM的状态来决定时间片的分配。假如一个VM有当前的执行焦点(典型)情况是它的视窗是显示的活动视窗,那么,它就是前台VM。当重新计算当前优先级时,调度程序就使前台的优先级提高,而其他VM作为后台考虑,继续没有升高的优先级。
七、系统虚拟机内的调度
所有基于Windows应用程序的线程都运作在系统VM下,它是支持多任务的唯一VM。系统VM中,一个支持16位应用、的子系统,多个是支持32位的WindOWS应用的。在系统VM下,通常包括多个有效一的,具有相同优先级的线程,为了处理这种情况,调度程序采用Round-Robin调度策略,以确保每个线程公平分配时间片。
一旦系统VM中的线程用完了给它的时间片,调度程序就把它放到了这种优先级相同的线程尾部。假如选择的线程失败地用完了分给它的时间片,那么,调度程序就把处理器交给下一个优先级相同的线程,并允许失败的线程利用时间片的剩余部分。
八、调度程序控制
调度程序控制有两种不同的影响,一种是它本身的一套内部算法,试图为每个线程提供一种平滑的多任务环境。“平滑”的目标是给线程提供一个合理的处理器时间,既要使它能很好地完成工作,又不能太长,以免其他线程被锁住的时间太长。另一个对调度程序的影响是 VXD能够直接调用的一套系统服务。
为了达到这一目的,调度程序内部采用了三种线程优先级的动态升高,定时的损耗优先级的继承。
九、线程的应用
Wndows的设计者面临的问题之一是如何更好地处理失败的运作程序。要开发完全没有错误的软件是不可能的,因此,Win- dOWS必须能够处理应用程序中的错误。其处理过程包括两步:一是恰当地处理失败的程序,即允许用户关闭应用而不掉失数据;二是做好后续的事,除了打开档案外,应用程序都会利用和处理系统提供的资源,如内存储器段、笔、刷等。假如系统不能释放这些资源占用的内存储器,那么可以获得的资源就会减少。
大多数应用程序共有的错误都是寻址时引起的错误。这类错误是由于应用程序试图用无效的指针指向某些对象引起的。在Windows 3·1下就产生 GP错误,用户会看到一个对话框,它提供了产生错误的程序模块的细节以及关闭错误程序的选项。
Window。95处理这类问题有两种方式,GP错误处理程序在系统里以分开的线程运作,因此就不用程序前后关系来处理错误,反映错误的对话框和程序的终止由已知状态的线程来管理,系统用线程标识符标识
每个已分配的资源,因此,假如一个线程非正常终止,系统就会通过资源表找到它,然后使其返回不用状态。
通过这种清理,系统中所存的过程挂钩、视窗、全局内存储器、选表、类、游标、图标、笔、刷子、区域、设备上下文、字体、位图、调色板等都可以重新利用。假如 32位的线程失败,那么这种清理会立即见效,而对于16位的 Windows应用程序则不能立即见效。必须等应用程序退出以后才能见效。线程机制的另一个用途是,当系统沉寂时,调度后台程序运作。
例如,有一个线程,其作用是把要修改的内容输出到交换档案中。当没有程序运作时,这个线程就被唤醒运作,确保交换档案匹配当前执行程序的内存储器映像。这一点有时是十分有用的。
十、支持多消息队列
Windows程序都是事件驱动的,这种特性要求系统能提供一种由应用程序传递消息的方法。消息可由设备驱动程序、应用程序和系统发出。系统把所有硬件的初始化消息放在一个被称为原始输人队列(RawInput Queue)的数据结构中。
Windows 95支持多消息队列,这种设计的改进来自 Windows NT。因为有效的消息流对好的响应时间和平滑多任务是极其重要的,而这种支持多消息队列的设计技术是关键。它能够保证系统在一个应用程序失败时不至于死锁。这种多消息队列技术称做 “异步输人”(Input Desynchronlzatlon)。
在Windows 95下,经原始输人队列添加消息十分简单。系统中还有一个运作的线程,它有规律地把这消息移出队列转到各个私有应用消息队列。这种队列有两种:所有16位应用程序的单一队列和所有32位应用程序中的线程的私有队列。
必须指出,系统本身或其他过程产生的消息可直接移人私有队列。假如系统很忙,就会在内部开一个小的缓冲区存放消息。大多数情况下不必这样做。当一个32位过程第一次运作时,它就有一个单一的消息队列
与其主线程联系。
假如这个过程又创建了另一个线程,系统就不会马上创建另一个消息队列,只有与之相关的消息队列第一次调用它时才创建消息队列。如果一个线程不需要消息队列,那么系统就不会在其浪费时间。
十一、API层
在Wndows 95中,应用程序代码和系统代码之间的路径与Windows3.1中的相同。系统大量地使用DDL在应用程序与Windo。s3.1子系统之间提供必须的路径。但Windows 95比Wndows3.l有两种主要结构的改进:一是档案系统的保护模式的代码路径;二是直接支持32位应用程序。
Win 32 API是 Microsoft公司的战略性系统接口,它第一次出现在Windows NT中,并把其子集Win 3ZAPI引人到Windows 3.1中。正是由于Win 32 API的强大功能及远大前途,Windows 95也包含了Win 32。Microsoft公司希望每个应用程序都是32位的,而目前许多应用程序都是16位的,因此,Windows 95的特性必须支持16位应用程序。
从Intel微处理器的结构显然看出,基于Win 32的应用程序及支持它的系统代码与现存的16位环境有着根本的区别。尤其是,寻址方式的变化意味着不能很容易地混合使用16位和32位代码。
对于 Windows 95,这就意味着要有新的编译器、汇编器和连接程序来开发32位应用程序。系统自身至少必须提供32位版本的包含 KERNEL、USER和 GDI的 WindOWS子系统来支持新的 Wi。32 API。这些代码必须小、速度快、易于测试,还要有好的文档。
十二、16/32位代码混合
代码混合技术在以前的 Windows版本、OS/2和 Windows NT中都已使用过,Windows 95中也用了这种技术,并解决了以下问题:
(1)32位代码使用32位线性寻址,而16位代码使用 16位段选择器加 16位偏移量来寻址。要使代码混合使用,必须在两种寻址方式之间有一种转换。解决这个问题的方法,包含一种称为“贴瓦”*ilin护的技术,即系统分配一个新的16位段选择子,它描述的存储器能覆盖此时存储器包含的参数。
(2)在C语言中,基于Win 32的应用中整型是32位,而在16位的应用中是16位的。当调用16位代码时,32位的整数参数必须转换成16位,返回时扩展成32位。如果参数在寄存器中,这种转换就容易些。但许多Windows函数把参数放在堆叠中。
(3)返回32位值(如指针)的16位代码要用DX:AX这对寄存器,而32位代码希望返回值放人EAX寄存器中。
(4)32位代码用386的SS:ESP寄存器对为堆叠寻址,而 16位代码用 SS:SP寄存器对。这就必须进行反覆的寄存器交换,可能还要进行参数拷贝。
解决以上问题的“设备”称为thunk程序,从32位的代码调用16位代码,或反过来,都需要thunk,无论何时调用API都需要先执行thunk。
如果thunk速度很慢,应用程序的性能就会降低。因此实现thunk的关键是,占用内存储器最小,执行时间要尽可能短,所以,hunk用汇编语言编写。在不同代码转换期间,系统通过建立一个新的堆叠结构来处理
堆叠管理出口。混合代码调用时,在把参数压人新的堆叠时,要转换格式。
必须指出,在设计thunk时,Microsoft的一些工作已经文档化为其产品的一部分。Windows NT用了“genetic thunk”方法,其细节在Win32 SDR中。Windows 3.1中的Win32用了“universalthunk”方法,它已成为Win 32子系统的组成部分,在Windows 95中,为了提高执行速度,使用“interation”方法来设计thunk。这种速度的提高,一是由于在 thunk层尽可能用 32位代码;二是由于在 thunk处理器部分编码十分细致,特别是减少了选择子装人的数目,因为在Intel处理器中装人选择子是十分费时的。