Ano passado, tive que investigar um deadlock em uma máquina Windows Server 2012. Qualquer tentativa de iniciar ou parar um serviço nessa máquina congelava e não voltava mais. Eu não conhecia o recurso “Analyze Wait Chain” no Gerenciador de Tarefas (novo desde o Windows 7), mas acontece que ele economiza muito tempo. Esse recurso utiliza a API de depuração Wait Chain Traversal e é, na verdade, o recurso favorito do Matt Pietrek no Windows 7. Simplesmente usando o Gerenciador de Tarefas, pude ver quantos programas estavam esperando pelo serviço “Service e Controller app”, que é o SCM (o Service and Controller Manager):
O services.exe com PID 588 é o serviço SCM “Service and Controller app”. Apenas um entre todos que estavam bloqueando as threads no SCM estavam esperando pela thread 1072, e a thread 1072 estava esperando pelo serviço LSA. Tirei um dump do PID 588, e essas threads bloqueadas tinham esta pequena pilha:
ntdll!ZwWaitForSingleObject ntdll!RtlpWaitOnCriticalSection ntdll!RtlpEnterCriticalSectionContended services!ScStartServiceAndDependencies services!RStartServiceW rpcrt4!Invoke ...
Não sou um especialista em Windows, mas, apenas chutando os nomes das funções, posso dizer que essa é uma thread servindo uma requisição RPC para iniciar um serviço e aguardar uma seção crítica. Sabendo pela sequência de espera do Gerenciador de Tarefas que a thread bloqueada é a 1072 (poderíamos usar outras maneiras para encontrar o dono da seção crítica, mas por que se preocupar com isso?), a pilha da thread 1072 exibe que está esperando no LSA pelo logon de um usuário:
... rpcrt4!NdrClientCall3 sspicli!SspirLogonUser sspicli!SspipLogonUser sspicli!LsaLogonUser sspicli!L32pLogonUser sspicli!LogonUserExExW services!ScLogonService services!ScLogonAndStartImage services!ScStartService services!ScStartMarkedServices services!ScStartServiceAndDependencies services!RStartServiceW rpcrt4!Invoke ...
Tirando um dump do processo Isass.exe mostra que há apenas uma thread ativa:
... rpcrt4!NdrClientCall2 sechost!RStartServiceW sechost!StartServiceW dnsapi!StartDnsServiceOnDemand dnsapi!Query_PrivateExW dnsapi!Query_Shim dnsapi!DnsQuery_UTF8 netlogon!NetpSrvOpen netlogon!NetpDcGetDcNext netlogon!NetpDcGetNameSiteIp netlogon!NetpDcGetNameIp netlogon!NetpDcGetName netlogon!DsIGetDcName netlogon!DsrGetDcNameEx2 kerberos!KerbGetKdcBinding kerberos!KerbMakeSocketCallEx kerberos!KerbMakeSocketCall kerberos!KerbGetAuthenticationTicketEx kerberos!KerbGetTicketGrantingTicket kerberos!LsaApLogonUserEx2 lsasrv!NegLogonUserEx2Worker lsasrv!NegLogonUserEx2 lsasrv!LsapCallAuthPackageForLogon lsasrv!LsapAuApiDispatchLogonUser lsasrv!SspiExLogonUser sspisrv!SspirLogonUser rpcrt4!Invoke ...
Então aí está, o LSA está tentando iniciar um serviço de cliente DNS (também chamado de dnscache) para responder a uma requisição de logon de um usuário. Essa chamada volta ao SCM e bloqueia a seção crítica mantida pela thread 1072. Essa é uma lista de espera circular, caracterizando um deadlock. Nosso servidor estava travado nesse estado por vários dias antes da investigação.
Ao analisar o problema, fica claro que qualquer serviço executado sob uma conta de domínio pode passar por esse problema durante a inicialização da máquina. A conta de domínio é necessária porque é, na verdade, um domínio que aciona o LSA para buscar o DNS para encontrar o controle de domínio para o domínio da conta do serviço.
Eu não tinha visto esse problema antes, então provavelmente deve ser raro. Muitas coisas precisam acontecer para que ele surja. Entretanto, caso aconteçam, é difícil de investigar, ele não desaparece, corrige a si mesmo e misteriosamente some depois de um reboot. Minha recomendação é para tornar qualquer serviço rodando como uma conta de domínio, dependente do serviço dnscache. Dessa forma, o SCM se assegurará de que o serviço dnscache é iniciado antes de pedir ao LSA para criar um token de logon para o serviço que deseja iniciar, eliminando portanto a possibilidade de um deadlock. A desvantagem é que parar o serviço dnscache também irá parar o seu serviço, o que pode ser considerado desvantajoso em algumas situações.
***
Artigo traduzido pela Redação iMasters, com autorização do autor. Publicado originalmente em http://rusanu.com/2013/08/27/services-running-under-domain-account-should-add-dnscache-service-as-dependency/