![Windows 服务限制导致启动时服务崩溃](https://linux22.com/image/523180/Windows%20%E6%9C%8D%E5%8A%A1%E9%99%90%E5%88%B6%E5%AF%BC%E8%87%B4%E5%90%AF%E5%8A%A8%E6%97%B6%E6%9C%8D%E5%8A%A1%E5%B4%A9%E6%BA%83.png)
我们用 C# 开发了一个自定义 Windows 服务,作为大型企业应用程序的一部分。我们的 QA 部门测试了该服务的多个版本。
QA 实验室在一台 Windows 2003 测试机上安装了该服务的多个副本(超过 20 个)。每个副本都在自己的文件夹中,并具有唯一的服务名称,但每个可执行文件的名称都相同(例如 OurWindowsService.exe)。每个服务都使用相同的 Windows 凭据(域用户)。
此服务的目的是处理 MSMQ 消息。排队的消息可执行各种重要操作。
由于某种原因,它们一次只能运行 5 个服务。当我们启动第 6 个服务时,该服务在启动时崩溃。
例如,我可以启动 #1、#2、#3、#4 和 #5。当我启动 #6 时,它会崩溃。但是,如果我停止 #1 并启动 #6,#6 会正常运行,而现在 #1 无法启动。
当服务崩溃时,Windows 事件日志中会出现以下错误:
错误应用程序 OurWindowsService.exe,版本 5.40.1.1,错误模块 kernel32.dll,版本 5.2.3790.4480,错误地址 0x0000bef7。
我能够使用 WinDbg 生成事后转储文件。转储文件显示崩溃发生在尝试延迟加载 SHLWAPI.dll 时:
0:000> kb100
ChildEBP RetAddr Args to Child
0012ece4 79037966 c06d007e 00000000 00000001 KERNEL32!RaiseException+0x53
0012ed4c 790099ba 00000008 0012ed08 7c82860c mscoree!__delayLoadHelper2+0x139
0012ed98 790075b1 001550c8 0012edac 0012fb34 mscoree!_tailMerge_**SHLWAPI_dll**+0xd
0012edb0 79007623 001550c8 0012edf8 0012edf4 mscoree!XMLGetVersionWithSupported+0x22
0012ee00 790069a4 aa06f1b0 00000000 000001fe mscoree!RuntimeRequest::GetRuntimeVersion+0x56
0012f478 790077aa 00000001 7903fb4c 0012fb34 mscoree!RuntimeRequest::ComputeVersionString+0x5bd
0012f89c 79007802 00000001 0012f8b4 7903fb4c mscoree!RuntimeRequest::FindVersionedRuntime+0x11c
0012f8b8 79007b19 00000001 00000000 aa06fa6c mscoree!RuntimeRequest::RequestRuntimeDll+0x2c
0012ffa4 79007c02 00000001 0012ffbc 00000000 mscoree!GetInstallation+0x72
0012ffc0 77e6f23b 00000000 00000000 7ffdf000 mscoree!_CorExeMain+0x12
0012fff0 00000000 79007bf0 00000000 78746341 KERNEL32!BaseProcessStart+0x23
我相信传递给 Kernel32.RaiseException 的错误代码 c06d007e 意味着未找到模块,但我不确定。
大家觉得这听起来很熟悉吗?我们是否对某个文件名上的服务实例数量有所限制?MSMQ 是否不喜欢超过 5 个监听服务?
答案1
如果你的服务正在为每个实例加载消息队列驱动程序 (Mqac.sys) 的副本,则你很可能系统视图空间内存池耗尽。每个 MSMQ 实例都使用该池的 4MB,默认情况下池大小仅为 16MB。
这是否真的发生实际上取决于您的服务如何(如果有的话)使用 MSMQ 系统,以及它是否直接引用它(加载其库)或使用其他方法(例如套接字)与其通信。
如果事实证明您正在加载多个 MSMQ 实例,则可以通过增加系统视图空间池来缓解问题。这可以通过以下方式完成:
- 打开注册表项 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management。
- 创建一个名为 SystemViewSize 的新 DWORD 值。
- 计算并指定该值如下:
- 使用以下公式:(16 +(已加载的消息队列资源数 x 4))。
- 例如,具有三个消息队列资源的群集的值等于 28。
- 重启
答案2
这次崩溃意味着您的计算机上没有找到 SHLWAPI.dll。分析您的调用堆栈,“XMLGetVersionWithSupported”在某些情况下调用了 SHLWAPI.dll 导出的 API,正如您的描述,当您运行服务时,该 API 被调用了超过 5 次。