Como parar todos os processos filhos gerados por um serviço

5

Eu tenho um serviço rodando no Ubuntu com a seguinte configuração:

#/etc/init/my_service.conf

start on (local-filesystems and net-device-up IFACE=eth1)

respawn

exec python -u /opt/XYZ/my_prog.py 2>&1 \
                 | logger -t my_prog.py

Ao interromper o serviço com sudo service my_service stop , o processo python não é eliminado. Matar o processo pai com kill também não mata o processo python.

Como faço para interromper completamente o serviço (por exemplo, todos os processos filhos são mortos)? Idealmente, eu gostaria de não modificar o arquivo de configuração acima.

    
por DhDd 28.01.2014 / 17:32

3 respostas

5

Ideally, I would like to not modify the configuration file above.

Difícil! É a coisa certa a fazer.

Você precisa alterar seu exec para script e interromper a execução desse programa python em um subprocesso bifurcado como parte de um pipeline. Esta resposta do ServerFault explica como fazer isso em um script de shell incorporado. Eu faria apenas uma mudança no roteiro dado lá, na última linha:

exec python -u /opt/XYZ/my_prog.py 2>&1

Não há uma boa razão para não registrar erros padrão, afinal de contas.

Reviravoltas cada vez mais complexas para lidar com bifurcações, de expect daemon a comutação para systemd , deixam de perceber que a coisa certa a fazer é parar o daemon de bifurcar . Se há uma coisa boa a sair da confusão atual, é a confirmação contínua de que o que a IBM escreveu e recomendou em 1995 esteve certo todos esses anos.

Acostume-se com a idéia de daemons carregamento de cadeia . Há muitos conjuntos de ferramentas que simplificam essas coisas. Acostume-se com a ideia de não usar scripts de shell também. Há uma abundância de conjuntos de ferramentas que são projetados especificamente para este trabalho, que eliminam as sobrecargas de shells ( que é uma boa idéia conhecida no mundo do Ubuntu ).

Por exemplo: os comandos shell na resposta do ServerFault podem ser substituídos por um script que use execline tools de Laurent Bercot que são projetados para serem capazes de fazer isso sem subcasos e FIFOs desvinculados:

#!/command/execlineb -PW
pipeline -w {
    logger -t my_prog.py
} 
fdmove -c 2 1 
python -u /opt/XYZ/my_prog.py

que você simplesmente faria

exec /foo/this_execlineb_script

Com o meu nosh toolset , seria similarmente um script contendo:

#!/usr/local/bin/nosh
pipe 
fdmove -c 2 1 
python -u /opt/XYZ/my_prog.py | logger -t my_prog.py

Ou, como alternativa, pode-se ter essa sub-rotina diretamente na definição de tarefa do Upstart (usando um truque para evitar metacaracteres do shell para que o Upstart não crie um shell):

exec /usr/local/bin/exec pipe --separator SPLIT fdmove -c 2 1 python -u /opt/XYZ/my_prog.py SPLIT logger -t my_prog.py

Leitura adicional

  • Russ Allbery (2013-12-18). Bug # 727708: upstart propôs política no Debian . debian-ctte.
  • Jonathan de Boyne Pollard (2001). " Não bifurque () para ' coloque o daemon no fundo '. ". Erros a evitar ao projetar programas do programa Unix . Respostas frequentemente dadas.
por 28.01.2014 / 22:32
3

No GNU / Linux, normalmente, não há como parar um serviço e todos os processos filhos gerados, porque os processos filhos podem alterar seu PPID (ID do processo pai). A única maneira de saber seria rastrear as chamadas do sistema para gerar processos à medida que são feitos e manter uma lista desses processos.

O sistema de inicialização do Ubuntu, upstart , não faz isso. Então a resposta para sua pergunta é é impossível - no Ubuntu - sem:

  1. Modificando esse script;
  2. Saber exatamente quais IDs do processo são gerados por esse processo;
  3. Acompanhar esses IDs de processo manualmente;
  4. Matando-os individualmente.

É por isso que você deve executar uma distribuição de Linux que execute systemd . Como você pode ver, systemd controla todos os processos filhos e pode matar todos eles com um único comando. É assim que a administração do sistema GNU / Linux deve ser , mas porque o systemd é tão novo, e porque é "Não inventado aqui" (ou seja, a Canonical não inventou), o Ubuntu não quer usá-lo.

    
por 28.01.2014 / 17:50
1

Você ignorou a sub-rotina esperar . O livro de receitas do upstart especifica:

Warning

This stanza is extremely important: read this section carefully!

Upstart will keep track of the process ID that it thinks belongs to a job. If a job has specified the instance stanza, Upstart will track the PIDs for each unique instance of that job.

If you do not specify the expect stanza, Upstart will track the life cycle of the first PID that it executes in the exec or script stanzas. However, most Unix services will "daemonize", meaning that they will create a new process (using fork(2)) which is a child of the initial process. Often services will "double fork" to ensure they have no association whatsoever with the initial process. (Note that no services will fork more than twice initially since there is no additional benefit in doing so).

In this case, Upstart must have a way to track it, so you can use expect fork, or expect daemon which allows Upstart to use ptrace(2) to "count forks".

To allow Upstart to determine the final process ID for a job, it needs to know how many times that process will call fork(2). Upstart itself cannot know the answer to this question since once a daemon is running, it could then fork a number of "worker" processes which could themselves fork any number of times. Upstart cannot be expected to know which PID is the "master" in this case, considering it does not know if worker processes will be created at all, let alone how many times, or how many times the process will fork initially. As such, it is necessary to tell Upstart which PID is the "master" or parent PID. This is achieved using the expect stanza.

Você precisa disso porque o pipe | que você está usando está criando processos filhos. Você pode encontrar no livro Programaçà £ o avançada do Linux a breve introdução a isso, onde se afirma que:

For example, this shell command causes the shell to produce two child processes, one for ls and one for less:

 $ ls | less

O que eu não sei é se isso implica em um ou dois garfos, para que eu experimente modificar a linha respawn em seu código com

 expect fork
 respawn

ou

 expect daemon
 respawn

Eu não acredito que isso possa ser alcançado apenas com systemd , embora meu logotipo mostre claramente que eu sou fã de systemd .

    
por 28.01.2014 / 18:17