作者:陈鸽
Windows Networking 2: 为什么我的Apache服务停不了。
这是Windows Networking 排查的第二篇,上次涉及了Windows NDIS的架构以及对该架构下网络问题排查的方法。这次,我们通过一个case,来讲讲NDIS之上,Windows TCPIP 协议栈的实现。
问题
Windows Server 2008 R2 SP1 上,使用Apache部署了服务。当我们尝试停止Apache服务是,该服务一直pending在stopping状态。任何操作都无法恢复,直至重启机器。
方案
在遇到这个问题之前,我们都没有听说过有已知问题会导致Apache服务无法停止,怎么看都是应用的问题。但为避免万一,我们还是经验性的建议,
- 卸载不需要的三方软件,尤其是添加了Filter driver或者WFP callout driver的安全软件。
- 禁用网卡的高级功能,尤其是TCP Chimney 和 RSS。参考:https://blogs.technet.microsoft.com/onthewire/2014/01/21/tcp-offloadingchimney-rsswhat-is-it-and-should-i-disable-it/
- 确认Windows 补丁版本,建议安装最新补丁。
一一尝试之后,确认问题依旧存在。同时我们确认补丁已经升级到最新。至此,我们常规手段已经无法解决问题,只好通过重现问题并抓取Memory dump做进一步分析。
Memory Dump 分析
从Dump来看,我们明确 Apache的服务进程httpd.exe不退出的原因是Afd.sys驱动在等待一个完成信号。
由于无法释放afd资源,导致应用程序一直等待,即便强制kill,也会留下zombie进程,只能通过重启解决。
而我们知道,在Windows中AFD资源又是与TCP资源强关联的,只有当对应的TCP资源被释放之后,才会由tcpip.sys调用afd.sys的callback routine,完成释放以及触发信号量的工作。
因此,更重要的一个问题是TCP资源为什么没有得到释放。因此,我们直接检查TCP资源的Reference,
在Windows系统中,基本上实现了资源的对象管理。对于TCP的一个端口来说,它也是一个对象。对任何对象的操作之前,系统都会尝试AddReference,避免该对象在使用过程中被释放导致Memory Access Violation。在用完该对象之后,系统会调用DeReference的操作,把减少对应的引用计数。一旦当一个对象的Reference count为0,那么它就会被对应的routine释放。对于一个TCP监听端口来说,释放资源就是tcpip!TcpDereferenceListener,例如,
# Child-SP RetAddr Call Site
00 fffff880`05b727b0 fffff880`0160a01e tcpip!TcpDereferenceListener+0xe
01 fffff880`05b727e0 fffff880`0160a039 tcpip!TcpCloseListener+0x6e
02 fffff880`05b72830 fffff880`02b2fa30 tcpip!TcpTlListenerCloseEndpoint+0x9
03 fffff880`05b72860 fffff880`02b2fef2 afd!AfdCleanupCore+0x410
04 fffff880`05b729e0 fffff800`01937aaf afd!AfdDispatch+0x42
05 fffff880`05b72a30 fffff800`01935a2e nt!IopCloseFile+0x11f
06 fffff880`05b72ac0 fffff800`0193565f nt!ObpDecrementHandleCount+0x8e
07 fffff880`05b72b40 fffff800`01935964 nt!ObpCloseHandleTableEntry+0xaf
08 fffff880`05b72bd0 fffff800`016fd9d3 nt!ObpCloseHandle+0x94
09 fffff880`05b72c20 00000000`7774999a nt!KiSystemServiceCopyEnd+0x13
在我们这个问题中,很明显目前这个80端口对应的TCP资源有超过0x36个引用,除开TcpCreateListener,其它的0x35个引用可能是leak,也可能是其他驱动或者软件在操作这个结构所添加的引用。比如说,netstat 在枚举端口信息的时候会增加端口的reference,例如,
由于我们明确了系统中没有任何网络相关的三方软件,那么基本上问题就认定为操作系统对于TCP资源的leak。此时,我们基本上就需要跟微软开case,进一步分析操作系统问题。
后记
正当我们准备摩拳擦掌跟微软进一步分析操作系统问题的时候,我们在微软网站上偶尔的发现7月10日发布的最新补丁可能会引发w3svc无法停止的问题,尽管不是Apache,但是问题的描述跟我们发现的本质问题一致,
https://support.microsoft.com/en-us/help/4338818/windows-7-update-kb4338818
微软在之后紧急release了新的update 4345459来修复该问题。
在我们的这个case中,应用该补丁之后,问题解决。