桌面堆概述(一)

桌面堆可能不是你花很多时间考虑的事情,这是件好事。但是,有时您可能会遇到由于桌面堆耗尽而导致的问题,然后了解此资源会有所帮助。让我先说一下,在Vista中,内核地址空间的情况发生了显著的变化,而我今天所说的大部分内容并不适用于Vista。

奠定基础:会话空间

要理解桌面堆,首先需要了解会话空间。windows2000、windowsxp和windowsserver2003在内核模式下有一个有限但可配置的内存区域,称为会话空间。会话表示单个用户的登录环境。每个进程都属于一个会话。在没有安装终端服务的Windows 2000计算机上,只有一个会话,并且会话空间不存在。在Windows XP和Windows Server 2003上,会话空间始终存在。称为会话空间的地址范围是一个虚拟地址范围。此地址范围映射到分配给当前会话的页。以这种方式,给定会话中的所有进程将会话空间映射到相同的页面,但另一个会话中的进程将会话空间映射到不同的页面集。
会话空间分为四个区域:会话image空间、会话结构、会话视图空间和会话分页池。会话映像空间加载Win32k.sys修改数据的会话专用副本、Win32k.sys代码和未修改数据的单个全局副本,并映射各种其他会话驱动程序,如视频驱动程序、TS远程协议驱动程序,会话结构保存各种内存管理(MM)控制结构,包括会话的会话工作集列表(WSL)信息。会话分页池允许会话特定的分页池分配。Windows XP使用常规分页池,因为远程桌面连接的数量是有限的。另一方面,如果安装了终端服务(应用服务器模式),WindowsServer2003从会话分页池而不是常规分页池进行分配。会话视图空间包含会话的映射视图,包括桌面堆。
Session Space布局:
 

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

 

会话、窗口工作站和桌面

您可能已经猜到桌面堆与桌面有关。让我们花点时间来讨论桌面,以及它们与会话和窗口站的关系。所有Win32进程都需要一个桌面对象来运行。桌面有一个逻辑显示面,包含窗口、菜单和挂钩。每个桌面都属于一个窗口工作站。窗口工作站是一个包含剪贴板、一组全局原子和一组桌面对象的对象。每个会话只允许一个窗口站与用户交互。这个窗口站名为“Winsta0”。每个窗口站都属于一个会话。会话0是运行服务的会话,通常表示控制台(Vista之前)。任何其他会话(会话1、会话2等)通常是远程桌面/终端服务器会话,或通过快速用户切换连接到控制台的会话。总而言之,会话包含一个或多个窗口工作站,窗口工作站包含一个或多个桌面。
你可以把上面描述的关系想象成一棵树。以下是典型系统上的桌面树示例:

- 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值引用窗口对象

哪里出了问题

通常情况下,这种方法“管用”,用户和应用程序开发人员都不需要担心桌面堆的使用情况。但是,有两种主要情况下,可能会发生与桌面堆相关的故障:
1、给定会话的会话视图空间可以充分利用,因此不可能创建新的桌面堆。
2、现有的桌面堆分配可以充分利用,因此使用该桌面的线程不可能使用更多的桌面堆。
你怎么知道你是否遇到了这些问题?进程无法以状态STATUS_DLL_INIT_FAILED (0xC0000142)user32.DLL中的错误启动是常见的症状。由于user32.dll需要桌面堆才能正常工作,因此在进程启动时未能初始化user32.dll可能表示桌面堆已耗尽。您可能观察到的另一个症状是无法创建新窗口。根据应用程序的不同,可以用不同的方式处理任何此类故障。请注意,如果您遇到上面的第一个问题,这些症状通常只存在于一个会话中。如果您看到的是问题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

-------------------------------------------------------------

正如您在上面的示例中看到的,每个桌面堆大小都是指定的,后面是使用率的百分比。如果任何一个桌面堆变得太满,则该桌面内的分配将失败。如果所有桌面的累计堆大小接近会话视图空间的总大小,则无法在该会话中创建新的桌面。上面描述的两个失败场景都取决于两个因素:会话视图空间的总大小和每个桌面堆分配的大小。这两种尺寸都是可配置的。

配置会话视图空间的大小

可以使用SessionViewSize注册表值配置会话视图空间大小。这是一个REG_DWORD,大小以兆字节为单位指定。下面列出的3GB/x86位值不是特定于x86的系统。需要重新启动才能使此更改生效。该值应在以下项下指定:
HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlSession ManagerMemory Management
 

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

没有安装终端服务,会话空间就不存在,桌面堆分配是从固定的48MB区域中为系统映射视图进行的。如果没有安装热修复程序318942,会话视图空间的大小固定为20 MB。
对于32位操作系统,会话视图空间和会话分页池的大小之和理论上最大值略低于500 MB。最大值取决于RAM和其他各种注册表值。实际上,对于大多数配置,最大值约为450 MB。当上述值增加时,将导致非分页池、系统PTE、系统缓存或分页池的任何组合的虚拟地址空间减少。

配置单个桌面堆的大小

配置单个桌面堆的大小要复杂一些。就桌面堆大小而言,有三种可能:
·桌面属于交互式窗口站,是“断开连接”或“Winlogon”桌面,因此其堆大小分别固定为64KB或128KB(对于32位x86)
·桌面堆属于交互式窗口站,不是上述桌面之一。此桌面的堆大小是可配置的。
·桌面堆属于非交互式窗口站。这个桌面的堆大小也是可配置的。
每个桌面堆分配的大小由以下注册表值控制:
HKEY_LOCAL_MACHINESystemCurrentControlSetControlSession ManagerSubSystemsWindows
此注册表值的默认数据如下所示(全部在一行上):
%SystemRoot%system32csrss.exe ObjectDirectory=Windows SharedSection=1024,3072,512 Windows=On SubSystemType=Windows  ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3  ServerDll=winsrv:ConServerDllInitialization,2 ProfileControl=Off  MaxRequestThreads=16
 
“SharedSection=”后面的数值控制如何分配桌面堆。这些SharedSection值以千字节为单位指定。
第一个SharedSection值(1024)是所有桌面共用的共享堆大小。此内存不是桌面堆分配,不应修改该值以解决桌面堆问题。
第二个SharedSection值(3072)是与交互式窗口站关联的每个桌面的桌面堆大小,但“Disconnect”和“Winlogon”桌面除外。
第三个SharedSection值(512)是与“非交互式”窗口站关联的每个桌面的桌面堆大小。如果不存在此值,则非交互式窗口工作站的桌面堆大小将与为交互式窗口工作站指定的大小相同(第二个SharedSection值)。
考虑上面描述的两个桌面堆耗尽场景。如果遇到第一种情况(会话视图空间耗尽),并且大多数桌面堆是非交互式的,那么可以减少第三个SharedSection,以便创建更多(更小)非交互式桌面堆。当然,如果使用非交互式堆的进程需要完整的512 KB,则这可能不是一个选项。如果遇到第二种情况(单个桌面堆分配已满),则可以增加第二个或第三个SharedSection值,以允许每个桌面堆大于3072或512 KB。一个潜在的问题是可以创建的总桌面堆更少。

会话0中的窗口工作站和桌面到底是什么?

既然我们知道了如何调整会话视图空间和各种桌面的大小,那么有必要讨论一下为什么有那么多窗口工作站和桌面,特别是在会话0中。首先,您会发现每个WinSta0(交互式窗口站)至少有3个桌面,并且每个桌面都使用不同数量的桌面堆。我之前曾提到过这一点,但回顾一下,每个交互式窗口站的三个桌面是:
·默认桌面-桌面堆大小可配置如下所述
·断开桌面连接-在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的桌面堆不足,则可以通过更改运行该服务的用户帐户将某些服务移动到新的窗口工作站和桌面。
原文地址:https://www.cnblogs.com/yilang/p/13545384.html