奠定基础:会话空间
会话空间分为四个区域:会话image空间、会话结构、会话视图空间和会话分页池。会话映像空间加载Win32k.sys修改数据的会话专用副本、Win32k.sys代码和未修改数据的单个全局副本,并映射各种其他会话驱动程序,如视频驱动程序、TS远程协议驱动程序,会话结构保存各种内存管理(MM)控制结构,包括会话的会话工作集列表(WSL)信息。会话分页池允许会话特定的分页池分配。Windows XP使用常规分页池,因为远程桌面连接的数量是有限的。另一方面,如果安装了终端服务(应用服务器模式),WindowsServer2003从会话分页池而不是常规分页池进行分配。会话视图空间包含会话的映射视图,包括桌面堆。
Session Image Space: win32k.sys, session drivers |
Session Structure: MM structures and session WSL |
Session View Space: session mapped views, including desktop heap |
Session Paged Pool |
会话、窗口工作站和桌面
你可以把上面描述的关系想象成一棵树。以下是典型系统上的桌面树示例:
- Session 0
| |
| ---- WinSta0 (interactive window station)
| | |
| | ---- Default (desktop)
| | |
| | ---- Disconnect (desktop)
| | |
| | ---- Winlogon (desktop)
| |
| ---- Service-0x0-3e7$ (non-interactive window station)
| | |
| | ---- Default (desktop)
| |
| ---- Service-0x0-3e4$ (non-interactive window station)
| | |
| | ---- Default (desktop)
| |
| ---- SAWinSta (non-interactive window station)
| | |
| | ---- SADesktop (desktop)
| |
- Session 1
| |
| ---- WinSta0 (interactive window station)
| | |
| | ---- Default (desktop)
| | |
| | ---- Disconnect (desktop)
| | |
| | ---- Winlogon (desktop)
| |
- Session 2
|
---- WinSta0 (interactive window station)
|
---- Default (desktop)
|
---- Disconnect (desktop)
|
---- Winlogon (desktop)
在上面的树中,SADesktop的完整路径可以表示为“Session 0sawintaSADesktop”。
桌面堆-它是什么,它是用来做什么的?
每个桌面对象都有一个与之关联的桌面堆。桌面堆存储某些用户界面对象,如窗口、菜单和挂钩。当应用程序需要用户界面对象时,调用user32.dll中的函数来分配这些对象。如果应用程序不依赖于user32.dll,则它不会使用桌面堆。让我们看一个应用程序如何使用桌面堆的简单示例。
1、应用程序需要创建一个窗口,因此它在user32.dll中调用CreateWindowEx。
2、User32.dll使系统调用进入内核模式,并以win32k.sys结束。
3、Win32k.sys从桌面堆分配窗口对象
4、窗口的句柄(HWND)返回给调用方
5、同一会话中的应用程序和其他进程可以通过其HWND值引用窗口对象
哪里出了问题
2、现有的桌面堆分配可以充分利用,因此使用该桌面的线程不可能使用更多的桌面堆。
诊断问题
那么,您如何确定桌面堆耗尽是您的问题呢?这可以用多种方法来解决,但我现在要讨论最简单的方法。Dheapmon是一个命令行工具,它将转储给定会话中所有桌面的桌面堆使用情况。一旦安装了dheapmon,一定要在您认为桌面堆用完的会话中运行它。例如,如果服务启动失败,则需要从会话0运行dheapmon,而不是从终端服务器会话运行dheapmon。
Dheapmon输出如下所示:
Desktop Heap Information Monitor Tool (Version 7.0.2727.0) Copyright (c) 2003-2004 Microsoft Corp. ------------------------------------------------------------- Session ID: 0 Total Desktop: ( 5824 KB - 8 desktops) WinStationDesktop Heap Size(KB) Used Rate(%) ------------------------------------------------------------- WinSta0Default 3072 5.7 WinSta0Disconnect 64 4.0 WinSta0Winlogon 128 8.7 Service-0x0-3e7$Default 512 15.1 Service-0x0-3e4$Default 512 5.1 Service-0x0-3e5$Default 512 1.1 SAWinStaSADesktop 512 0.4 __X78B95_89_IW\__A8D9S1_42_ID 512 0.4 -------------------------------------------------------------
正如您在上面的示例中看到的,每个桌面堆大小都是指定的,后面是使用率的百分比。如果任何一个桌面堆变得太满,则该桌面内的分配将失败。如果所有桌面的累计堆大小接近会话视图空间的总大小,则无法在该会话中创建新的桌面。上面描述的两个失败场景都取决于两个因素:会话视图空间的总大小和每个桌面堆分配的大小。这两种尺寸都是可配置的。
配置会话视图空间的大小
OS |
Size if no registry value configured |
Default registry value |
Windows 2000 * |
20 MB |
none |
Windows XP |
20 MB |
48 MB |
Windows Server 2003 |
20 MB |
48 MB |
对于32位操作系统,会话视图空间和会话分页池的大小之和理论上最大值略低于500 MB。最大值取决于RAM和其他各种注册表值。实际上,对于大多数配置,最大值约为450 MB。当上述值增加时,将导致非分页池、系统PTE、系统缓存或分页池的任何组合的虚拟地址空间减少。
配置单个桌面堆的大小
·桌面属于交互式窗口站,是“断开连接”或“Winlogon”桌面,因此其堆大小分别固定为64KB或128KB(对于32位x86)
·桌面堆属于交互式窗口站,不是上述桌面之一。此桌面的堆大小是可配置的。
·桌面堆属于非交互式窗口站。这个桌面的堆大小也是可配置的。
每个桌面堆分配的大小由以下注册表值控制:
第一个SharedSection值(1024)是所有桌面共用的共享堆大小。此内存不是桌面堆分配,不应修改该值以解决桌面堆问题。
第二个SharedSection值(3072)是与交互式窗口站关联的每个桌面的桌面堆大小,但“Disconnect”和“Winlogon”桌面除外。
第三个SharedSection值(512)是与“非交互式”窗口站关联的每个桌面的桌面堆大小。如果不存在此值,则非交互式窗口工作站的桌面堆大小将与为交互式窗口工作站指定的大小相同(第二个SharedSection值)。
考虑上面描述的两个桌面堆耗尽场景。如果遇到第一种情况(会话视图空间耗尽),并且大多数桌面堆是非交互式的,那么可以减少第三个SharedSection,以便创建更多(更小)非交互式桌面堆。当然,如果使用非交互式堆的进程需要完整的512 KB,则这可能不是一个选项。如果遇到第二种情况(单个桌面堆分配已满),则可以增加第二个或第三个SharedSection值,以允许每个桌面堆大于3072或512 KB。一个潜在的问题是可以创建的总桌面堆更少。
会话0中的窗口工作站和桌面到底是什么?
·默认桌面-桌面堆大小可配置如下所述
·断开桌面连接-在32位系统上,桌面堆大小为64k
·Winlogon桌面-在32位系统上,桌面堆大小为128k
注意,WinSta0中可能还有更多的桌面,因为任何进程都可以调用CreateDesktop并创建新的桌面。
让我们继续讨论与非交互式窗口站相关联的桌面:这些通常与服务相关。系统将创建一个窗口工作站,在该工作站中启动在LocalSystem帐户下运行的服务进程。这个窗口站名为service-0x0-3e7$。它以LocalSystem帐户的LUID命名,包含一个名为Default的桌面。但是,作为LocalSystem interactive运行的服务进程在Winsta0中启动,以便它们可以在会话0中与用户交互(但仍在LocalSystem上下文中运行)。
在显式用户或服务帐户下启动的任何服务进程都有一个窗口站和由服务控制管理器为其创建的桌面,除非其LUID的窗口站已经存在。这些窗口站是非交互式窗口站。窗口站名称基于LUID,对于每次登录都是唯一的。如果一个实体(而不是系统)多次登录,则每次登录都会创建一个新的窗口站。示例窗口站名称是“service-0x0-22e1$”。
一个常见的桌面堆问题发生在具有大量服务的系统上。这可以是大量独特的服务,也可以是一个(设计拙劣的,IMHO)自己安装多次的服务。如果服务都在LocalSystem帐户下运行,那么会话0Service-0x0-3e7$Default的桌面堆可能会耗尽。如果这些服务都在另一个多次登录的用户帐户下运行,每次获取一个新的LUID,就会为服务的每个实例创建一个新的桌面堆,会话视图空间最终将耗尽。
根据您现在对服务进程如何使用窗口站和桌面的了解,您可以使用这些知识来避免桌面堆问题。例如,如果会话0Service-0x0-3e7$Default desktop的桌面堆不足,则可以通过更改运行该服务的用户帐户将某些服务移动到新的窗口工作站和桌面。