¿Por qué "su -c <command> &" aparentemente permite que un comando se ejecute en segundo plano sin colgar?

11

Estaba ayudando a un colega que estaba teniendo problemas con un proceso de fondo muriendo de forma intermitente.

Descubrí que estaban iniciando el proceso en segundo plano al iniciar sesión en el servidor y ejecutar:

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

"Ajá", exclamé. "Si inicia un comando con" & "se colgará cuando salga del terminal de control. Debe usar algo como nohup para lograr esto. Realmente este proceso debe admitir la ejecución como un demonio, tut tut".

Probamos el comando anterior para demostrar mi punto y ... parecía funcionar: el proceso iniciado por el comando no salió cuando salimos del terminal que ejecutó el comando anterior.

El comando es un script de Python personalizado cuya salida va a un archivo. Por lo que puedo decir, no hay una capacidad inteligente de "demonización" en el script. No hace ninguna de las cosas necesarias para ejecutarse como un demonio que aparece en la página Wikipedia: Daemon (informática): Creación .

Ejecutar el comando así se comporta como se esperaba:

<command> &
exit

En el caso anterior, el proceso en segundo plano iniciado por el comando sale cuando salimos del terminal.

Mi pregunta es esta:

  1. Qué sucede cuando agregamos "su - -c &" que impide que el proceso salga cuando nuestro terminal sale. Me gustaría entender en detalle con respecto al terminal de control, entrada y salida estándar, etc.

  2. ¿Es esta una forma razonable de lograr el objetivo de ejecutar este comando como un proceso en segundo plano? ¿Si no, porque no?

Quiero propagar las mejores prácticas dentro de mi empresa, pero necesito poder demostrar y respaldar cualquier recomendación que haga.

También quiero entender qué está sucediendo exactamente.

Sam Elstob
fuente

Respuestas:

12

Hay varias formas en que un proceso puede ser eliminado debido a una terminal que se está muriendo.

  1. La primera forma es que el controlador de terminal en el núcleo envía una señal SIGHUP al proceso de control para el cual el terminal es el terminal de control . En la mayoría de los casos, el proceso de control es el shell que se inicia inicialmente en el terminal, y su terminal de control es aquel al que están conectados sus stdin, stdout y stderr. Un proceso se desconecta del terminal de control si llama setsid.

  2. La segunda forma es que el shell, cuando recibe SIGHUP, reenvía esa señal a sus subprocesos, más precisamente, a sus trabajos en segundo plano. Algunos shells (ksh, bash, zsh) tienen una función disownincorporada que le dice al shell que no envíe un SIGHUP a un trabajo en particular.

  3. Si el terminal desaparece y un programa intenta leerlo, se le notifica una condición de fin de archivo (o posiblemente EIO para un trabajo en segundo plano). Si el terminal desaparece y un programa intenta escribir en él, la escritura regresa con un error EIO. Esto puede hacer que el programa salga.

Entonces, lo que sucambia aquí es que (2) normalmente causaría que el shell inicial matara el trabajo en segundo plano, pero dado que ese proceso en segundo plano se ejecuta como un usuario diferente, la señal no se puede entregar y el proceso en segundo plano sigue vivo.

Gilles 'SO- deja de ser malvado'
fuente
He visto al usuario root hacer chroot --userspec root:root / sh -c "exec some_forever_process" &. El trabajo se ejecuta como el mismo usuario, sin explícito nohupantes o disowndespués. Entonces, para este caso, ¿cómo es que la señal no se puede entregar a la salida del plazo?
Toddius Zho
@ToddiusZho Presumable some_forever_processestaba destinado a ejecutarse para siempre (un demonio) y, por lo tanto, se protege contra la recepción de SIGHUP desde una salida de terminal (ejecutándose en su propio grupo de procesos).
Gilles 'SO- deja de ser malvado'
bash / tail no protege contra SIGHUP, pero eso todavía funciona: `` `$ local ssh root @ REMOTE_HOST remote # chroot --userspec root: root / sh -c" exec bash -c 'tail -f / dev / null '"& [1] 6376 remoto # 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 pid, uid, command -ww 6376 0 tail -f / dev / null `` `
Toddius Zho