Por que “su -c <comando> e” aparentemente permite que um comando seja executado em segundo plano sem desligar

11

Eu estava ajudando um colega que estava tendo problemas com um processo em segundo plano morrendo intermitentemente.

Descobri que eles estavam iniciando o processo em segundo plano efetuando login no servidor e executando:

su - <user> -c '<command>' &

"Aha", exclamei. "Se você iniciar um comando com" & ", ele será interrompido quando você sair do terminal de controle. Você precisa usar algo como nohup para conseguir isso. Realmente esse processo precisa suportar a execução como um daemon, tut tut."

Testamos o comando acima para demonstrar meu argumento e ... parecia funcionar: o processo iniciado pelo comando não saiu quando saímos do terminal que executava o comando acima.

O comando é um script Python personalizado cuja saída vai para um arquivo. Tanto quanto eu posso dizer, não existe um recurso "daemonize" inteligente no script. Ele não executa nenhuma das ações necessárias para executar como um daemon listado na página Wikipedia: Daemon (computação): Criação .

A execução do comando se comporta conforme o esperado:

<command> &
exit

No caso acima, o processo em segundo plano iniciado pelo comando sai quando saímos do terminal.

Minha pergunta é esta:

  1. O que está acontecendo quando adicionamos "su - -c &" que impede que o processo saia quando nosso terminal sai. Eu gostaria de entender em detalhes com relação ao terminal de controle, entrada e saída padrão etc.

  2. Essa é uma maneira razoável de atingir o objetivo de executar este comando como um processo em segundo plano. Se não, por que não?

Quero propagar as melhores práticas dentro da minha empresa, mas preciso demonstrar e fazer backup de todas as recomendações que faço.

Eu também quero entender exatamente o que está acontecendo.

Sam Elstob
fonte

Respostas:

12

Existem várias maneiras de um processo ser morto por causa de um terminal que está morrendo.

  1. A primeira maneira é que o driver do terminal no kernel envie um sinal SIGHUP ao processo de controle para o qual o terminal é o terminal de controle . Na maioria dos casos, o processo de controle é o shell que é iniciado inicialmente no terminal, e seu terminal de controle é aquele ao qual stdin, stdout e stderr estão conectados. Um processo é desconectado do terminal de controle, se ele ligar setsid.

  2. A segunda maneira é que o shell, quando recebe SIGHUP, reenvia esse sinal para seus subprocessos - mais precisamente, para seus trabalhos em segundo plano. Alguns shells (ksh, bash, zsh) têm um disownbuiltin para dizer ao shell para não enviar um SIGHUP para um trabalho específico.

  3. Se o terminal desaparecer e um programa tentar lê-lo, ele será notificado sobre uma condição de final de arquivo (ou possivelmente EIO para um trabalho em segundo plano). Se o terminal desaparecer e um programa tentar gravar nele, a gravação retornará com um erro de EIO. Isso pode causar a saída do programa.

Portanto, o que sumuda aqui é que (2) normalmente faria com que o shell inicial acabasse com o trabalho em segundo plano, mas, como esse processo em segundo plano está sendo executado como um usuário diferente, o sinal não pode ser entregue e o processo em segundo plano permanece.

Gilles 'SO- parar de ser mau'
fonte
Eu vi o usuário root fazer chroot --userspec root:root / sh -c "exec some_forever_process" &. O trabalho está sendo executado como o mesmo usuário, sem explícito nohupantes ou disowndepois. Então, neste caso, como é que o sinal não pode ser entregue na saída a termo?
Toddius Zho
O @ToddiusZho Presumable some_forever_processfoi projetado para ser executado para sempre (um daemon) e, portanto, protege-se contra o recebimento de SIGHUP de uma saída do terminal (executando em seu próprio grupo de processos).
Gilles 'SO- stop be evil'
O bash / tail não protege contra o SIGHUP, mas ainda funciona: `` `local $ ssh root @ REMOTE_HOST remote # chroot --userspec root: root / sh -c" exec bash -c 'tail -f' - -d / dev / null '"& [1] 6376 remote # ps -p $! --no-header -o pid, uid, command -ww 6376 0 tail -f / dev / null remote # exit local $ ssh root @ REMOTE_HOST remote # ps -p 6376 --no-header -o comando pid, uid WW 6376 0 tail -f / dev / null `` `
Toddius Zho