Como devo verificar se um determinado PID está em execução?

16

Estou escrevendo um script Perl que analisa arquivos de log para coletar PIDs e verifica se esse PID está em execução. Estou tentando pensar na melhor maneira de fazer essa verificação. Obviamente, eu poderia fazer algo como:

system("ps $pid > /dev/null") && print "Not running\n";

No entanto, eu preferiria evitar a chamada do sistema, se possível. Por isso, pensei que poderia usar o /procsistema de arquivos (portabilidade não é uma preocupação, isso sempre estará sendo executado em um sistema Linux). Por exemplo:

if(! -d "/proc/$pid"){
    print "Not running\n";
}

Isso é seguro? Posso sempre assumir que, se não houver /proc/$pid/diretório, o PID associado não está sendo executado? Espero que sim, já que o pspróprio AFAIK obtém suas informações de /procqualquer maneira, mas como isso é para código de produção, quero ter certeza.

Portanto, pode haver casos em que um processo em execução não possui /proc/PIDdiretório ou onde /proc/PIDexiste um diretório e o processo não está em execução? Existe algum motivo para preferir a análise em psvez de verificar a existência do diretório?

terdon
fonte
2
há também a killfunção perl usando o sinal 0, que não mata, mas diz se você pode fazê-lo (ou seja, você precisa de permissão para sinalizar esse processo).
Meu16
11
'/ proc / $ PID' deve ficar bem se você estiver fazendo isso no Linux.
likewhoa
7
@terdon Observe que qualquer método que você usar (e kill -0é o melhor), isso apenas informa se existe um processo em execução com o PID especificado . Ele não informa se o processo ainda estará em execução um milissegundo mais tarde e não informa se o processo é o que você está interessado ou se um processo não relacionado foi atribuído ao mesmo PID após a morte do processo interessante . Quase sempre é um erro testar se um determinado PID está sendo executado : há muito poucas circunstâncias em que isso não é propenso a condições de corrida.
Gilles 'SO- stop be evil'
11
@ Gilles, de fato, também devo verificar o nome do processo. No entanto, neste caso, não me importo se houver uma alteração de status um milissegundo mais tarde. Eu tenho processos listados como ativos em um dB e um arquivo correspondente com os pids. Eu só preciso verificar se alguma coisa que o banco de dados acha que está em execução não está realmente em execução e ter controle suficiente sobre a instalação para saber que não pode iniciar novamente aleatoriamente.
terdon
3
@MickLH uau, foi o que me disseram. Na verdade, esse seria um comentário útil, em vez de uma celebração do seu próprio brilho, se você se desse ao trabalho de explicar o que é tão ruim e perigoso. Não duvido que você esteja certo, nunca afirmei ser um programador, e foi por isso que fiz a pergunta, mas você conseguiu ser um insulto e inútil.
terdon

Respostas:

20

A função perl kill(0,$pid)pode ser usada.

Se o código de retorno for 1, o PID existe e você pode enviar um sinal para ele.

Se o código de retorno for 0, será necessário verificar $ !. Pode ser EPERM (permissão negada), o que significa que o processo existe ou ESRCH; nesse caso, o processo não existe.

Se o seu código de verificação estiver em execução root, você poderá simplificar isso apenas verificando o código de retorno do kill; 0 => erro, 1 => ok

Por exemplo:

% perl -d -e 0

Loading DB routines from perl5db.pl version 1.37
Editor support available.

Enter h or 'h h' for help, or 'man perldebug' for more help.

main::(-e:1):   0
  DB<1> print kill(0,500)
0
  DB<2> print $!
No such process
  DB<3> print kill(0,1)
0
  DB<4> print $!
Operation not permitted
  DB<5> print kill(0,$$)
1

Isso pode ser transformado em uma função simples

use Errno;

sub test_pid($)
{
  my ($pid)=@_;

  my $not_present=(!kill(0,$pid) && $! == Errno::ESRCH);

  return($not_present);
}

print "PID 500 not present\n" if test_pid(500);
print "PID 1 not present\n" if test_pid(1);
print "PID $$ not present\n" if test_pid($$);
Stephen Harris
fonte
FWIW, eu adicionei uma função simples mostrando como você pode fazer isso.
Stephen Harris
Sim, acho que vou com isso. Excluí meu comentário desde que percebi que tudo o que precisava era if (!kill(0,$pid) && $! =~ /No such process/){ exit; }ou algo parecido. Eu gosto mais da sua Errnosolução, obrigado. Embora eu provavelmente vá com isso, esperarei um pouco para que alguém possa responder à pergunta subjacente do Linux.
terdon
2
Se /procestiver montado, em seguida, todos os PID visível no espaço de nomes estarão presentes, para que o seu -d /proc/$pidteste iria trabalhar ... mas envolve sair para o sistema de arquivos em vez de usar as chamadas do sistema nativas.
Stephen Harris
O que é exatamente o que eu queria evitar, sim.
terdon
2
@terdon: Acabei de perceber que minha confusão veio do fato de que por "chamada do sistema" você realmente quis dizer " systemchamada" - ou seja, uma chamada para a systemfunção em si, não uma "chamada do sistema" . O último você não pode evitar, mas o primeiro você certamente pode. Faz sentido agora!
Mehrdad
6
  • Tenho 99,9% de certeza de que verificar se existe (e é um diretório) é 98% tão confiável quanto a técnica. A razão pela qual os 98% não são 100% é um ponto em que Stephen Harris tocou (e ricocheteou) em um comentário - ou seja, o sistema de arquivos pode não estar montado. Pode ser válida a reivindicação de que um sistema Linux sem é uma danificada, o sistema degradado - afinal, as coisas gostam , e provavelmente não vai funcionar - e por isso este pode não ser um problema para um sistema de produção. Mas é (teoricamente) possível que nunca tenha sido montado (embora isso possa impedir que o sistema chegue ao estado normal), é definitivamente possível desmontá-lo (eu testei 1/proc/PIDkill 0/proc/procpstoplsof) e acredito que não há garantia de que ela exista (isto é, não é exigida pelo POSIX). E, a menos que o sistema seja completamente mangueira, killfuncionará.
  • O comentário de Stephen fala sobre "sair para o sistema de arquivos" e "usar chamadas de sistema nativas". Eu acredito que isso é basicamente um arenque vermelho.
    • Sim, qualquer tentativa de acesso /proc requer a leitura do diretório raiz para localizar o /procsistema de arquivos. Isto é verdade para qualquer tentativa de acessar qualquer arquivo por um caminho absoluto, incluindo coisas /bin, /etce /dev. Isso acontece com tanta frequência que o diretório raiz certamente fica armazenado em cache na memória durante toda a vida útil (tempo de atividade) do sistema, portanto, essa etapa pode ser realizada sem nenhuma E / S de disco. E, uma vez que você tenha o inode /proc, tudo o que acontece fica na memória.
    • Como você acessa /proc? Com stat, open, readdir, etc., que são sistema nativo chama cada bocado tanto quanto kill.
  • A pergunta fala sobre um processo em execução. Esta é uma frase escorregadia. Se você realmente deseja testar se o processo está em execução (por exemplo, na fila de execução; talvez o processo atual em alguma CPU; não esteja em espera, em espera ou parado), pode ser necessário fazer ae ler a saída ou observar . Mas não vejo nenhuma sugestão em sua pergunta ou comentário de que você esteja preocupado com isso.ps PID /proc/PID/stat

    O elefante na sala, no entanto, é que um processo de zumbi 2 pode ser difícil de distinguir de um processo que está vivo e bem.  kill 0trabalha em um zumbi e existe. Você pode identificar zumbis com as técnicas listadas no parágrafo anterior (executando e lendo a saída ou examinando ). Meu ocasional (ou seja, não muito completo) teste muito rápido e sugere que você também pode fazer isso, fazendo uma ou on , ou - estes irão falhar em zumbis. (No entanto, eles também falharão nos processos que você não possui.)/proc/PIDps PID/proc/PID/statreadlinklstat/proc/PID/cwd/proc/PID/root/proc/PID/exe

____________
1  se o -f( f opção orce) não funcionar, tente -l( l Azy).
2  ou seja, um processo que saiu / morreu / terminou, mas cujo pai ainda não fez a wait.

G-Man Diz 'Reinstate Monica'
fonte
Obrigado pelo seu comentário sobre a minha resposta, que excluí porque estava errado. Não acredito que a página de kill(2)manual indique diretamente o comportamento que você apontou, mas a perlfuncpágina de manual indica . Vou enviar um email para Michael Kerrisk para ver o que ele tem a dizer sobre a página de manual do sistema.
Jrw32982 suporta Monica
Eu enviei um relatório de bug para esclarecer a página de manual kill(2)sobre permissões para "enviar" o sinal 0. #
jrw32982 suporta Monica
@ jrw32982: Bem, a página de manual diz coisas como: “O argumento sig … pode ser 0, caso em que a verificação de erro é realizada…” e “ ERROS - A chamada do sistema kill () falhará e nenhum sinal será enviado se: … O processo de envio não é o superusuário e seu ID de usuário efetivo não corresponde ao ID de usuário efetivo do processo de recebimento. Agora que você mencionou, suponho que possa ser interpretado de mais de uma maneira, mas, lamentavelmente, muitas páginas de manual do Unix são escritas nesse estilo, exigindo que você leia nas entrelinhas. Michael pode acreditar que está claro o suficiente como está.
G-Man diz 'Reinstate Monica'
Michael fez uma alteração na página de kill(2)manual (ainda não a vejo on-line): "Se sig for 0, nenhum sinal será enviado, mas ainda serão realizadas verificações de existência e permissão; isso pode ser usado para verificar a existência de um ID do processo ou ID do grupo de processos que o chamador está autorizado a sinalizar. "
Jrw32982 suporta Monica