開發與維運

Windows Networking 2: 為什麼我的Apache服務停不了。

作者:陳鴿

Windows Networking 2: 為什麼我的Apache服務停不了。

這是Windows Networking 排查的第二篇,上次涉及了Windows NDIS的架構以及對該架構下網絡問題排查的方法。這次,我們通過一個case,來講講NDIS之上,Windows TCPIP 協議棧的實現。

問題

Windows Server 2008 R2 SP1 上,使用Apache部署了服務。當我們嘗試停止Apache服務是,該服務一直pending在stopping狀態。任何操作都無法恢復,直至重啟機器。

方案

在遇到這個問題之前,我們都沒有聽說過有已知問題會導致Apache服務無法停止,怎麼看都是應用的問題。但為避免萬一,我們還是經驗性的建議,

  1. 卸載不需要的三方軟件,尤其是添加了Filter driver或者WFP callout driver的安全軟件。
  2. 禁用網卡的高級功能,尤其是TCP Chimney 和 RSS。參考:https://blogs.technet.microsoft.com/onthewire/2014/01/21/tcp-offloadingchimney-rsswhat-is-it-and-should-i-disable-it/
  3. 確認Windows 補丁版本,建議安裝最新補丁。
    一一嘗試之後,確認問題依舊存在。同時我們確認補丁已經升級到最新。至此,我們常規手段已經無法解決問題,只好通過重現問題並抓取Memory dump做進一步分析。

Memory Dump 分析

從Dump來看,我們明確 Apache的服務進程httpd.exe不退出的原因是Afd.sys驅動在等待一個完成信號。

image.png

由於無法釋放afd資源,導致應用程序一直等待,即便強制kill,也會留下zombie進程,只能通過重啟解決。

而我們知道,在Windows中AFD資源又是與TCP資源強關聯的,只有當對應的TCP資源被釋放之後,才會由tcpip.sys調用afd.sys的callback routine,完成釋放以及觸發信號量的工作。

image.png

因此,更重要的一個問題是TCP資源為什麼沒有得到釋放。因此,我們直接檢查TCP資源的Reference,

image.png

在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,例如,

image.png

由於我們明確了系統中沒有任何網絡相關的三方軟件,那麼基本上問題就認定為操作系統對於TCP資源的leak。此時,我們基本上就需要跟微軟開case,進一步分析操作系統問題。

後記

正當我們準備摩拳擦掌跟微軟進一步分析操作系統問題的時候,我們在微軟網站上偶爾的發現7月10日發佈的最新補丁可能會引發w3svc無法停止的問題,儘管不是Apache,但是問題的描述跟我們發現的本質問題一致,

https://support.microsoft.com/en-us/help/4338818/windows-7-update-kb4338818

image.png

微軟在之後緊急release了新的update 4345459來修復該問題。

https://support.microsoft.com/en-us/help/4345459/stop-error-0xd1-after-a-race-condition-occurs-in-windows-7-service-pac

在我們的這個case中,應用該補丁之後,問題解決。

Leave a Reply

Your email address will not be published. Required fields are marked *