Obtém o valor de retorno do processo pelo PID

2

Se eu tiver o PID de um processo que foi eliminado, como posso obter o status de retorno do processo?

Estou particularmente interessado em saber se o status de retorno foi 143 ( kill ) ou 137 ( kill -9 ), pois não sei o que está matando o processo. É um script Python sendo executado a partir de um script PHP em um servidor para o qual não tenho acesso root. O script Python é executado por alguns minutos, mas o PHP está limitado a 30 segundos. O PHP tem facilidade para obter o status de retorno com exec() , mas isso não funcionará se o script PHP expirar!

    
por dotancohen 22.08.2013 / 10:28

5 respostas

2

Isso é quase certamente determinado por (o padrão) php.ini ou configuração equivalente:

max_execution_time = 30

TLDR: se você está certo de que o PHP está realmente saindo primeiro, e algo está matando seu script, então você pode usar o daemon para envolver e monitorar o processo Python (no modo não-reinicializante, ou seja, sem --respawn ), ou adicione handers de sinal para o script Python.

Em geral, se você conseguir executar um shell como o ID do usuário do script, será possível strace ou truss do processo, por exemplo, no linux você pode fazer isso de forma razoavelmente eficaz:

 $ sleep 60 &
 [1] 10873
 $ strace -e trace=signal,process  -p $!
 Process 10873 attached - interrupt to quit
 +++ killed by SIGKILL +++
 Process 10873 detached

O processo sleep foi finalizado com kill -9 de outro terminal. kill -9 não seria comum, já que um processo não seria capaz de interceptar isso e sair corretamente.

Para monitorar com daemon , use uma variação em:

daemon -i --errlog=/tmp/mypy.log -- /usr/bin/python [...]

Isso registrará qualquer terminação relacionada ao sinal. Adicione --dbglog e --debug=2 se você quiser registrar todas as saídas, independentemente disso.

Como observado em outros lugares, se o processo já foi encerrado, ele desapareceu. Apenas o pai (ou init) teria conseguido obter seu código de saída e, a menos que estivesse registrado (possivelmente usando contabilidade ou auditoria de processo), o código de saída é perdido.

Internamente para manipulação de tempo limite em plataformas * nix, o PHP configura SIGALARM ou SIGPROF com o tempo limite especificado. Esse manipulador de sinal simplesmente chama a função zend_error() interna. No entanto, ele também chama qualquer chamadas de retorno registradas e gerentes de erros , que podem ser úteis para você.

Se o manipulador de erro padrão entrar em ação, o código de saída do PHP será, acredito, 255, já que o tempo limite é E_ERROR .

Observe também que a convenção de um código de saída como 128 + N, onde N é o número do sinal, é devido ao comportamento de certos shells (incluindo bash ) e muitas APIs. O código de saída do processo real após um sinal é dependente do sistema. É provavelmente apenas o número do sinal, este é geralmente o caso no Linux. O grupo wait() de chamadas do sistema fornece melhores detalhes da saída do processo. O PHP segue a convenção shell, por ex. Se você estiver usando popen() e pclose() , você verá 128 + N como o código de saída quando um sinal terminar o processo.

Outra consideração aqui é o comportamento dos limites de tempo do PHP, se você marcar o % co_de Documentação% que você verá

The set_time_limit() function and the configuration directive max_execution_time only affect the execution time of the script itself. Any time spent on activity that happens outside the execution of the script such as system calls using system(), stream operations, database queries, etc. is not included when determining the maximum time that the script has been running. This is not true on Windows where the measured time is real.

Você pode provar isso com um script curto:

<?php
# for (;;);
$exe="sleep 20";
print "exit code: " . pclose(popen($exe, 'r'));
?>

Invoque com set_time_limit() . O script será executado por 20 segundos, não haverá erro. Se você matar o processo de sono, por exemplo time php -d max_execution_time=5 -f sleep3.php em outro terminal, você verá um código de saída de 138 (128 + SIGUSR1).

Se você descomentar o loop kill -USR1 $(pgrep sleep) infinito, o script terminará após 5 segundos.

    
por 22.08.2013 / 13:10
2

Um processo que foi morto não existe mais no sistema. Na verdade, em um host ocupado, há uma boa chance de que um processo de longa duração e desde que o processo de morte tenha sido reutilizado pelo PID por algum outro processo, então você acabaria vendo algo totalmente não relacionado!

Quando um processo é morto, ele não retorna nada. Ele simplesmente deixa de existir. Isso contrasta com um sinal acessível como HUP ou USR1, que o processo pode (mas não necessariamente) capturar e manipular da maneira que achar melhor ( incluindo sair com um código de saída definido).

Se você tiver acesso suficiente, poderá instalar algum tipo de biblioteca ou módulo do kernel que intercepte e registre os syscalls, o que acaba resultando no processo em questão recebendo um sinal, mas parece mais fácil como sugerido pelo pjc50 apenas adicione um script de wrapper e grave o status de saída em algum lugar.

Uma alternativa seria não fazer o usuário esperar, mas sim mover para uma arquitetura assíncrona para o que você está fazendo e que leva tanto tempo para ser concluído.

    
por 22.08.2013 / 11:57
1

Você quer waitpid () , mas não sei se isso está disponível em PHP. Não se esqueça de adicionar WNOHANG se você não quiser bloquear. No entanto, isso só funciona se o PID for filho do processo de chamada.

Pode ser mais fácil colocar um script wrapper em torno do script python que registra seu status de finalização em um arquivo de log em algum lugar.

    
por 22.08.2013 / 10:57
1

Obter o status de saída se o PHP não for mais o processo pai é simples. Enrole o script Python em um script BASH para registrar o status de saída em um arquivo. Por exemplo:

#!/bin/bash
false # false is a program that always has an exit status of 1.
echo $? > exit_status.log

Colocará "1" em exit_status.log . Apenas substitua false pelo seu script Python real.

Disparar o script para rodar como processo não-bloqueado deserdado no PHP é mais difícil. Considere acionar o processo com um cronograma Cron e evitar o PHP em conjunto!

Caso contrário, uma possível solução é chamar o script usando at . Aqui está uma solução completa:

Da chamada PHP:

<?php
exec("at -f commands now");

No arquivo, commands :

sleep 10 # Do nothing for 10 seconds
false # Return status 1
echo $? > exit_status.log

Se você não tem acesso root e at não está instalado, então compile-o localmente em seu diretório inicial, usando algo como ./configure --prefix=$HOME/local . Caso contrário, peça ao administrador para instalar o pacote.

Outras leituras: man at , man cron

    
por 22.08.2013 / 12:00
1

Outra alternativa:

Do PHP:

exec("./wrapper.sh");

Em wrapper.sh :

#!/bin/bash
./wrapperlogger.sh >/dev/null 2>&1 &

Em wrapperlogger.sh :

./yourPythonScript
echo $? > exit_status.log

O primeiro wrapper origina o processo. O segundo wrapper monitora o código de saída e o registra em um arquivo. Tenha cuidado para que este script substitua o mesmo arquivo de registro sempre que for executado.

    
por 22.08.2013 / 12:25

Tags