为什么窗口在内核中处理滚动条?

“全部”Windows版本的新1位漏洞利用内核代码中的一个处理滚动条的错误。 这让我思考。 为什么窗口在内核而不是用户模式下处理滚动条? 历史原因? 其他的操作系统是否也这样做?

TL; DR:微软牺牲了性能的安全。


Windows上的滚动条有点特别。 大多数滚动条不是真正的窗口,而是作为“父”窗口上的装饰来实现。 这导致我们更一般的问题; 为什么Windows在内核模式下实现了Windows?

让我们看看替代品:

  1. 用户模式下的每个进程。
  2. 用户模式下的单一“主”进程。

方案1在处理自己的窗口方面有很大的优势; 没有上下文切换/内核转换。 当然,问题是来自不同进程的窗口存在于同一个屏幕上,当用户切换到不同的窗口时,有人必须负责决定哪个窗口处于活动状态,并协调更改。 这个人将不得不成为一个特殊的系统进程或内核,因为这个信息不能是每个进程,它必须存储在全球的某个地方。 这种双重信息设计将变得复杂,因为每个进程信息不能被全局窗口管理器信任。 我相信这个理论设计还有很多其他的缺点,但我不打算在这里花更多的时间。

Windows NT 3实现了替代方案2的一个变体。窗口管理器在NT 4中被移入内核模式,主要是出于性能原因 :

…窗口管理器(USER)和图形设备接口(GDI)已从Win32子系统移到Windows NT执行程序。 Win32用户模式设备驱动程序,包括图形显示和打印机驱动程序,也被移到执行官手中。 这些更改旨在简化图形处理,减少内存要求并提高性能。

…在同一份文件中还有更多的技术细节和理由:

当第一次设计Windows NT时,Win32环境子系统被设计为支持在MS-DOS,POSIX和OS / 2中的应用程序的环境子系统的对等体。 但是,应用程序和其他子系统需要在Win32子系统中使用图形,窗口和消息功能。 为了避免重复这些功能,Win32子系统被用作所有子系统的图形功能的服务器。

这个设计在Windows NT 3.5和3.51上运行得很好,但它低估了图形调用的音量和频率。 在单独的进程中具有与消息传递和窗口控制一样基本的功能会从客户端/服务器消息传递,数据收集和管理多个线程中产生大量的内存开销。 它还需要多个上下文切换,这会消耗CPU周期以及内存。 每秒图形支持调用的音量降低了系统的性能。 很显然,在Windows NT 4.0中重新设计这个方面可以回收这些浪费的系统资源并提高性能。

其他子系统现在不是那么重要,但性能问题仍然存在。

如果我们看一个像IsWindowVisible这样简单的函数,那么当窗口管理器处于内核模式时,没有太多的开销:函数将在用户模式下执行一些指令,然后将CPU切换到环0,其中整个操作验证传入的窗口句柄,如果有效,检索可见属性)在内核模式下执行。 然后切换回用户模式,就是这样。

如果窗口管理器驻留在另一个进程中,那么至少会使内核转换量增加一倍,并且必须以某种方式将函数输入和输出传递给窗口管理器进程,并且必须以某种方式使窗口管理器进程在您等待时执行为结果。 NT 3通过使用共享内存, LPC和称为配对线程的隐蔽功能的组合。