Windows是功能最全面,最灵活的操作系统之一,它可以在完全不同的体系结构上运行,并且具有不同的版本。 今天,它支持x86,x64,ARM和ARM64体系结构。 Windows
一次支持Itanium,PowerPC,DEC Alpha和MIPS。 此外,Windows支持在各种条件下运行的各种SKU。 从数据中心,笔记本电脑,Xbox和电话到嵌入式版本的物联网,例如在ATM中。
最令人惊奇的方面是,取决于所有这些体系结构和
SKU ,Windows内核实际上保持不变。 内核根据其工作的体系结构和处理器动态扩展,以充分利用设备。 当然,内核具有与特定体系结构关联的一定数量的代码,但是那里只有少量的代码,这使得Windows可以在各种体系结构上运行。
在本文中,我将讨论Windows内核的关键部分的演进过程,以使其能够从
Surface RT 2012上运行的低功耗NVidia Tegra芯片透明地扩展到Azure数据中心内的巨型设备。
Windows任务管理器在Windows DataCenter预发行版计算机上运行,具有896个内核,支持1792个逻辑处理器和2 TB内存
单核演进
在讨论Windows内核的细节之前,让我们先讨论一下
重构 。 重构在增加各种SKU和平台(例如,客户端,服务器和电话)上OS组件的重用中起着关键作用。 重构的基本思想是允许您在不同的SKU上重用相同的DLL,支持专门针对所需SKU进行的小修改,而无需重命名DLL,也不会中断应用程序的工作。
Windows重构的核心技术是一种鲜为人知的技术,称为
API集 。 API套件是一种机制,允许OS分离DLL及其使用位置。 例如,尽管所有API的实现都是在另一个DLL中编写的,但API集允许win32的应用程序继续使用kernel32.dll。 这些实现DLL在SKU之间也可能有所不同。 您可以通过在传统的Windows DLL(例如kernel32.dll)上运行依赖关系遍历来查看正在使用的API集。
完成了有关Windows结构的讨论之后,使系统可以最大程度地实现代码重用和共享,让我们继续按照调度程序启动内核的技术深度,这是扩展OS的关键。
内核组件
实际上,Windows NT是一个
微内核 ,从某种意义上说,它具有自己的核心内核(KE),具有有限的功能集,它使用可执行层(执行层,Ex)执行所有高级策略。 EX仍然是内核模式,因此它不完全是一个微内核。 内核负责调度线程,在处理器之间进行同步,处理硬件级别的异常以及实现低级别的硬件相关功能。 EX层包含各种子系统,这些子系统提供了通常被视为核心的一组功能-IO,对象管理器,内存管理器,进程子系统等。
为了更好地理解组件的大小,这里是内核源代码树的几个关键目录(包括注释)中的代码行数的大致分解。 该表尚未包含与内核相关的所有内容。
内核子系统 | 代码行 |
---|
内存管理器 | 501,000 |
登记处 | 211,000 |
威力 | 238,000 |
行政人员 | 157,000 |
安全性 | 135,000 |
内核 | 339,000 |
处理子系统 | 116,000 |
有关Windows体系结构的更多信息,请参见
Windows Internals系列丛书。
策划人
以这种方式做好了准备,让我们来谈一谈调度程序,它的演变以及Windows内核如何通过具有如此多的处理器来扩展到如此多种不同的体系结构。
线程是执行程序代码的基本单元,而Windows调度程序正是计划该线程的工作。 在决定启动哪个线程时,调度程序将使用它们的优先级,并且理论上,优先级最高的线程应该在系统上启动,即使这意味着优先级较低的线程将没有时间了。
工作了量子时间(线程可以工作的最短时间)后,该线程的动态优先级就会降低,因此高优先级的线程无法永远工作,这是其他所有人的灵魂。 当另一个线程醒来工作时,它会根据导致等待的事件的重要性来计算优先级(例如,前端用户界面的优先级大大提高,而完成I / O操作的优先级却没有提高)。 因此,线程在保持交互性的同时具有较高的优先级。 当它主要与计算相关联(受CPU限制)时,其优先级降低,并且在其他具有高优先级的线程拥有处理器时间后,它们将返回到计算中。 此外,内核会任意增加在一定时间内未接收到处理器时间的现成线程的优先级,以防止它们的计算不足并纠正优先级倒置。
Windows Scheduler最初有一个就绪队列,它从中选择了下一个优先级最高的线程来运行。 但是,随着开始支持越来越多的处理器,唯一的队列变成了瓶颈,并且调度程序更改了Windows Server 2003发行区域的工作,并为每个处理器组织了一个就绪队列。 当切换为支持一个处理器的多个请求时,它们并没有执行单个全局锁来保护所有队列,而是允许调度程序根据本地最优决策。 这意味着在系统中的任何时刻,都有一个具有最高优先级的线程,但这并不一定意味着列表中的N个最高优先级线程(其中N是处理器的数量)在系统中工作。 在Windows开始切换到笔记本电脑和平板电脑等低功耗CPU之前,这种方法一直奏效。 当具有最高优先级的线程在此类系统上不起作用时(例如,用户界面的前端线程),这会导致明显的界面故障。 因此,在Windows 8.1中,调度程序已转移到混合模型,每个处理器都有与该处理器关联的线程的队列,而所有处理器都有一个现成进程的共享队列。 由于调度程序体系结构中的其他更改(例如,重构调度程序数据库锁),这不会显着影响性能。
Windows 7引入了诸如动态公平份额计划程序(Dynamic Fair Share Scheduler,DFSS)之类的东西。 这主要涉及终端服务器。 此功能试图解决CPU负载高的一个终端会话可能影响其他终端会话中的线程的问题。 由于调度程序没有考虑会话,只是使用优先级来分配流,因此不同会话中的用户可能会扼杀他们的流,从而影响其他会话中用户的工作。 这也给具有大量线程的会话(和用户)带来了不公平的优势,因为具有大量线程的会话有更多机会获得处理器时间。 尝试将一个规则添加到调度程序中,根据该规则,每个会话在处理器时间方面被认为与其他会话处于平等地位。 Linux的完全诚实的调度程序(
完全公平的调度程序 )具有类似的功能。 在Windows 8中,此概念被概括为一个调度程序组,并添加到了调度程序中,因此,每个会话都属于一个独立的组。 除了线程的优先级之外,调度程序还将调度程序组用作第二级索引,从而确定下一个要启动的线程。 在终端服务器中,所有调度程序组具有相同的权重,因此,无论调度程序组中线程的数量或优先级如何,所有会话都将接收相同数量的处理器时间。 此外,此类组还用于更精确地控制过程。 在Windows 8中,已增强Job对象以支持
处理器时间管理 。 使用特殊的API,您可以决定进程可以使用多少处理器时间(无论是软限制还是硬限制),并在进程达到这些限制时接收通知。 这类似于Linux上
cgroup中的资源管理。
从Windows 7开始,Windows Server引入
了对单台计算机上
64个以上逻辑处理器的支持 。 为了增加对这么多处理器的支持,系统中引入了一个新类别,即“处理器组”。 组是不超过64个逻辑处理器的不变集合,调度程序将其视为计算单元。 引导时的内核确定哪个处理器属于哪个组,对于处理器内核少于64个的机器,几乎无法注意到这种方法。 一个进程可以分为几组(例如,SQL Server的一个实例),一次只能在同一组内执行一个线程。
但是在CPU核心数量超过64个的计算机上,Windows开始显示出新的瓶颈,阻止了诸如SQL Server之类的苛刻应用程序随处理器核心数量的增加而线性扩展。 因此,即使增加了新的内核和内存,速度测量也没有显着提高。 与此相关的主要问题之一是关于调度员基地阻塞的争议。 锁定调度程序数据库可保护对必须计划工作的对象的访问。 这些对象包括线程,计时器,输入/输出端口,其他需要等待的内核对象(事件,信号量,互斥体)。 在解决此类问题的压力下,在Windows 7中,已进行工作以消除对调度程序数据库的阻塞,并用更精确的调整(例如逐对象锁定)替换它。 这使得性能测试(例如SQL
TPC-C)在某些配置上与以前的架构相比,可以证明速度提高了290%。 由于单个功能的更改,这是Windows历史上最大的性能提升之一。
Windows 10通过引入CPU集带来了另一项创新。 CPU集允许进程对系统进行分区,以便进程可以分布在多个处理器组中,从而防止其他进程使用它们。 Windows内核甚至不允许设备中断使用集合中包含的处理器。 这样可以确保即使设备也无法在发布给应用程序组的处理器上执行其代码。 看起来像是技术含量较低的虚拟机。 显然,这是一个强大的功能,因此内置了许多安全措施,因此应用程序开发人员在使用API时不会犯重大错误。 在游戏模式下使用CPU集的功能。
最后,我们开始支持
Windows 10中出现的 ARM64。 ARM体系结构支持本质上是异构的
big.LITTLE体系结构-“大”内核速度快,消耗大量能量,“小”内核速度慢,消耗较少。 这样的想法是,可以在小型内核上执行微不足道的任务,从而节省了电池。 为了支持big.LITTLE架构并增加在ARM上运行Windows 10时的电池寿命,考虑到应用程序与big.LITTLE架构一起使用的愿望,在调度程序中添加了异构布局支持。
希望,我的意思是Windows试图为应用程序提供优质的服务,跟踪在前台运行的线程(或缺少处理器时间的线程),并保证它们在“大型”内核上执行。 所有后台任务,服务和其他辅助线程都在小型内核上运行。 同样在程序中,您可以
强行注意线程
的低重要性,以便使其在小型内核上运行。
代表他人工作[代表工作]:在Windows中,前台的许多工作是由在后台工作的其他服务执行的。 例如,在Outlook中搜索时,搜索本身是由Indexer后台服务执行的。 如果我们仅在小型内核上运行所有服务,则前台应用程序的质量和速度将受到影响。 为了防止在这种工作方案下big.LITTLE体系结构变慢,Windows会监视对其他进程的应用程序调用,以代表它们执行工作。 在这种情况下,我们将与服务相关的线程的优先级赋予优先级,并强制其在大型内核上运行。
让我结束有关Windows内核的第一篇文章,它概述了调度程序。 有关操作系统内部工作原理具有类似技术细节的文章将在后面介绍。