可移植性
windows被设计成可在多种硬件平台上运行。windows NT最初的版本支持x86和MIPS架构。对于DEC(被康柏收购,后与惠普合并)公司的Alpha AXP平台,尽管它是一个64位处理器,windows NT运行在32位模式,windows也尝试过支持。windows 2000开发期间,曾有一个原生的64位版本但后来没有被发布。在支持第四代处理器架构上,Motorola PowerPC也曾被加入到windows NT 3.51中。因为市场需求的改变,MIPS和PowerPC架构在windows 2000的研发中被丢弃了。随后,因为windows 2000仅仅支持x86平台,康柏也不再支持其旗下的Alpha AXP架构了。然后windows XP和windows server 2003开始支持3种64位的处理器家族:Intel Itanium IA-64,AMD64,和适用于x86平台的intel64位扩展技术(兼容AMD64架构,二者有细微差别)。后面两族处理器称作64位扩展系统,本书称其x64。(windows 是如何在64位操作系统系统上可以运行32位应用程序的会在第三章中解释)
windows在不同硬件架构和平台之间实现可移植性主要靠如下两个方法:
- windows是分层设计的,系统底层有关具体处理器和平台的模块是独立分开的,这样系统的上层就可以屏蔽这些硬件的差异。两个保障系统可移植性的关键组件是kernel(Ntoskrnl.exe中)和硬件抽象层(HAL,于Hal.dll)。这两个组件在本章后面都有详细叙述。基于具体的硬件的函数(比如线程会话切换和门调度)在kernel中实现。因不同硬件(例如主板)而不同的函数位于HAL中。唯一剩余的组件,也是特定于硬件体系结构的,即内存管理,代码数量相当多,不过放眼整个系统又仅仅是个小数目而已。
- windows的绝大多数代码都是C写的,部分掺杂C++。汇编被用在系统需要直接操纵硬件的部分(比如中断门句柄)或者非常注意性能的部分(如会话切换)。汇编语言不仅存在于kernel和HAL,在系统核心的其他部分也有(如实现互斥锁的一个模块),另外还有windows子系统内核模式的部分,甚至一些用户模式的库中也有汇编,如Ntdll.dll中进程创建的代码。(本章后面会解释)
对称多处理
多任务是操作系统把一个处理器在多个线程之间共享的技术。当一个电脑拥有多于一个处理器的时候,它就可以同时进行多个线程了。也就是说,单一处理器的系统仅仅是看起来像能同时处理多个线程,多个处理器的系统就是名副其实了,每个处理器处理一个线程。
本章开头说过,windows的设计目标之一就是能够在多处理器平台上运行。windows是一个对称多处理(SMP)系统,没有主处理器——系统和用户线程一样可以运行在任意一个处理器上。同时,所有的处理器共享同一个内存空间。如果系统选择其中一个处理器运行系统内核代码,另外的处理器运行用户代码,那样的模式叫非对称多处理(ASMP)系统。二者对比于图2-2。
windows也支持3种现代多处理器系统类型:多核,超线程,和NUMA(非统一内存架构)。这些在后面的段落中简要提及。(完全了解这些系统调度的细节,请参阅第五章“进程和线程”)
图2-2
超线程是intel提出的一项技术,它在cpu的每个物理核上模拟出两个逻辑处理器。每个逻辑处理器有他自己的cpu状态,但共享执行引擎和内建cache。这允许其中一个逻辑cpu停滞的时候(如cache未命中或者指令分支预测错误)另一个逻辑cpu运算。这种调度策略旨在最佳发挥超线程设备——调度选择一个忙的逻辑cpu同一物理cpu上的另一个闲置逻辑cpu好过选择一个闲置的物理cpu。更多线程调度的细节参看第五章。
在NUMA系统上,处理器被分为称作节点的组。每组都有其自身的处理器和内存,它们通过高速缓存互联总线连接到整个系统中。运行在NUMA系统上的windows依然是个SMP系统,所有的处理器要访问全部内存,这是背景。实际上访问节点自身的内存要快过访问其他节点的内存。对于处于同一节点可以访问同一内存的处理器之间调度线程,以此来改善性能。并且尽量在节点内部满足内存请求,但如果必要还是会从外节点分配内存的。
当然,windows也原生支持多核系统——因为这样的系统有真正的物理核,windows上原本SMP代码将他们看做离散的多处理器,除了某些需要区分同一cpu上不同核和远程核的计算和识别任务。
windows在原本的设计中并没有限制具体的处理器数量,同时用发放许可证的办法来区分不同版本的windows。为了方便和效率,windows会在一个掩码中追踪记录处理器(总数量,闲置数,使用数,等等细节),这个可以被cpu直接操作的掩码又称作关联掩码,其位数和机器位数(32位或64位一致)。因此windows系统这样又限制了cpu的数量,因为这个关联掩码不可任意增长。为了保持兼容性,满足支持大处理器的需求,windows实现了一个高级结构叫处理器群。整个处理器群可以被一个关联掩码定义,内核和应用程序均可指定由哪个组群执行。兼容程序可查询支持的组群数量(最近被限制在4个),遍历每个组群的关联掩码。同时,传统的应用程序依旧可以在一个族内运行无碍。更多信息关于windows如何分配处理器族(也有关NUMA)的细节参阅第五章。
上面提到,可支持的处理器数量取决于windows版本(见本章后面图2-2)。这个数字被保存在系统许可文件(\Windows\ServiceProfiles\NetworkService\AppData\Roaming\Microsoft\SoftwareProtectionPlatform\tokens.dat)中,作为一个公共变量“Kernel-RegisteredProcessors”。(注意,篡改这个数据违反软件许可证,要使用更多数量的cpu要改的不仅仅是这一个数据。)
可扩展性
多处理器系统的一个关键问题就是可扩展性。SMP系统为了保持运行的正确必须遵守严格的规定。资源争夺和其他的性能问题比单一处理器的系统更复杂,而且在系统设计中必须更重视。windows为此采取了多个策略:
- 同时可让系统代码运行在一个或几个处理器上的能力
- 一个处理器可运行多个线程,而每个线程又可被多个处理器执行
- 内核,驱动和服务进程的高精度同步(如自旋锁,队列自旋锁,见第三章),让更多组件同时在多个处理器上协同运行。
- 诸如I/O端口(第二部分第八章“I/O系统”)这样的编程机制使得多线程服务进程可以很好地运行在多处理器系统上。
windows系统的这些兼容性已经经过了长时间的演变优化。比如windows server 2003在多处理器上调度多线程提出的per-CPU调度队列,还有windows 7和windows server 2008 R2 在调度数据库中摒弃了全局锁。这种逐步的提高也发生在其他方面,比如内存管理。更多细节有关多处理器同步详见第三章。