Por que ps * very * ocasionalmente falharia em encontrar um processo válido?

7

Eu me deparei com um problema estranho em que um comando ps -o args -p <pid> muito ocasionalmente falha em localizar o processo em questão, mesmo que ele esteja sendo executado no servidor em questão. Os processos em questão são scripts wrapper de longa duração usados para iniciar alguns aplicativos Java.

As ocorrências "in the wild" do problema sempre parecem acontecer no início da manhã, então há algumas evidências de que isso está relacionado à carga de disco no servidor em questão, porque elas são muito carregadas, mas por executando o ps em questão em um loop apertado, posso eventualmente replicar o problema - uma vez a cada poucas centenas de execuções, recebo um erro.

Ao executar o seguinte script bash, consegui gerar saída strace para uma falha e uma execução bem-sucedida:

while [ $? == 0 ] ; do strace -o fail.out ps -o args -p <pid> >/dev/null ; done ; strace -o good.out ps -o args -p <pid>

Comparando a saída de fail.out e good.out , posso ver que a chamada do sistema getdents na execução que falha de alguma forma retorna um número muito menor que a contagem real de processos no sistema (na ordem de ~ 500 comparado com ~ 1100)

grep getdents good.out
  getdents(5, /* 1174 entries */, 32768)  = 32760
  getdents(5, /* 31 entries */, 32768)    = 992
  getdents(5, /* 0 entries */, 32768)     = 0

grep getdents fail.out
  getdents(5, /* 673 entries */, 32768)   = 16728
  getdents(5, /* 0 entries */, 32768)     = 0

... e essa lista mais curta não inclui o pid real em questão, por isso não foi encontrado.

You can ignore this section, the ENOTTY errors are explained by dave_thompson's comment below, and are unrelated

Additionally, the failed run gets some ENOTTY errors that don't appear in the successful run. Near the beginning of the output I see

ioctl(1, TIOCGWINSZ, 0x7fffe19db310) = -1 ENOTTY (Inappropriate ioctl for device) ioctl(1, TCGETS, 0x7fffe19db280) = -1 ENOTTY (Inappropriate ioctl for device)

And at the end I see a single

ioctl(1, TCGETS, 0x7fffe19db0d0) = -1 ENOTTY (Inappropriate ioctl for device)

The failed ioctl at the end happens right before the ps returns, but it occurs after the ps has already printed an empty results set, so I'm not sure if they're related. I do know that they're consistent in all of the failed strace outputs I have, but don't appear in the successful ones.

Eu não tenho absolutamente nenhuma idéia do porquê getdents ocasionalmente não encontraria a lista completa de processos, e agora eu cheguei ao ponto em que eu vou apenas colocar um band-aid na coisa toda, mudando o controle script que verifica o script wrapper em questão para chamar o ps uma segunda vez se o primeiro falhar, mas eu estaria interessado em saber se alguém tem alguma ideia do que está acontecendo aqui.

O sistema em questão está executando o Kernel 4.16.13-1.el7.elrepo.x86_64 no CentOS 7 e o procps-ng versão 3.3.10-17.el7_5.2.x86_64

    
por James 07.11.2018 / 17:06

1 resposta

6

Considere ler as informações necessárias diretamente do sistema de arquivos /proc em vez de usar uma ferramenta como ps . Você encontrará as informações que está procurando ("args") dentro do arquivo /proc/$pid/cmdline , separadas apenas por bytes NUL em vez de espaços.

Você pode usar este sed one-liner para obter os argumentos do processo $pid :

sed -e 's/\x00\?$/\n/' -e 's/\x00/ /g' "/proc/$pid/cmdline"

Este comando é equivalente a:

ps -o args= -p "$pid"

(Usar args= em ps omitirá o cabeçalho.)

O comando sed primeiro procurará pelo último byte NUL à direita e o substituirá por uma nova linha, e depois substituirá todos os outros bytes NUL (separando argumentos individuais) por espaços, finalmente produzindo o mesmo formato que você está vendo ps .

Com relação aos processos de catalogação no sistema, ps faz isso listando os diretórios em /proc , mas há condições de corrida inerentes a esse procedimento, já que os processos estão iniciando e saindo enquanto ps está em execução. não é realmente um instantâneo, mas uma aproximação. Em particular, é possível que ps mostre processos que já foram finalizados no momento em que mostra seus resultados, ou omite processos que foram iniciados enquanto estava em execução (mas não foram retornados pelo kernel enquanto listavam o conteúdo de /proc .)

Eu sempre assumi que se um processo está lá antes que ps comece e ainda esteja lá depois que terminar, então não será omitido por ele, eu < em> assumido o kernel garantiria que estes seriam sempre incluídos, mesmo que haja muitos processos sendo criados e destruídos. O que você está descrevendo implica que não é o caso. Eu ainda estou cético sobre isso, mas dado que existem condições de corrida conhecidas em como ps funciona, eu acho que é pelo menos plausível que listar PIDs de /proc pode perder um existente devido a essas condições de corrida.

Seria possível verificar isso verificando a origem do kernel do Linux, mas eu não fiz isso (ainda), então realmente não posso dizer com certeza se existe uma condição de corrida desse tipo que perderia uma corrida longa processo, como você descreve.

A outra parte é a maneira como ps funciona. Mesmo que você esteja passando um único PID com o argumento -p , ele ainda está listando todos os PIDs existentes, mesmo que você esteja interessado apenas nesse único. Poderia ter um atalho nesse caso e pular listando as entradas em /proc e indo diretamente para /proc/$pid .

Eu não posso dizer por que isso foi implementado dessa maneira. Talvez porque a maioria das opções ps sejam "filtros" nos processos, então implementar -p da mesma maneira foi mais fácil, usar um atalho para ir direto para /proc/$pid pode envolver um caminho de código separado ou duplicação de código ... Outra hipótese é que alguns casos, incluindo -p mais opções adicionais, acabariam exigindo listagem, por isso talvez seja complexo determinar quais casos exatos permitiriam usar o atalho e quais não permitiriam.

O que nos leva à solução alternativa, indo diretamente para /proc/$pid , sem listar o conjunto completo de PIDs do sistema, evitando todas as raças conhecidas e simplesmente obtendo as informações que você precisa diretamente da fonte.

É um pouco feio, mas o problema descrito realmente existe, deve ser uma maneira confiável de buscar essas informações.

    
por 08.11.2018 / 09:53

Tags