Pourquoi «su -c <commande> &» autorise-t-il apparemment une commande à s'exécuter en arrière-plan sans raccrocher

11

J'aidais un collègue qui avait des problèmes avec un processus d'arrière-plan en train de mourir par intermittence.

J'ai découvert qu'ils commençaient le processus d'arrière-plan en se connectant au serveur et en exécutant:

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

"Aha", m'exclamai-je. "Si vous démarrez une commande avec" & ", elle raccroche lorsque vous quittez le terminal de contrôle. Vous devez utiliser quelque chose comme nohup pour y parvenir. Vraiment, ce processus doit prendre en charge l'exécution en tant que démon, tut tut."

Nous avons testé la commande ci-dessus pour démontrer mon point et ... cela semblait fonctionner: le processus démarré par la commande ne s'est pas arrêté lorsque nous avons quitté le terminal qui exécutait la commande ci-dessus.

commande est un script Python personnalisé dont la sortie va vers un fichier. Autant que je sache, il n'y a pas de capacité de démonisation intelligente dans le script. Il ne fait rien des choses nécessaires pour fonctionner en tant que démon répertorié dans la page Wikipedia: Daemon (computing): Creation .

L'exécution de la commande se comporte comme prévu:

<command> &
exit

Dans le cas ci-dessus, le processus d'arrière-plan lancé par la commande se termine lorsque nous quittons le terminal.

Ma question est la suivante:

  1. Que se passe-t-il lorsque nous ajoutons "su - -c &" qui empêche le processus de se terminer lorsque notre terminal se ferme. Je voudrais comprendre en détail en ce qui concerne le terminal de contrôle, l'entrée et la sortie standard, etc.

  2. Est-ce un moyen raisonnable d'atteindre l'objectif d'exécuter cette commande en tant que processus d'arrière-plan. Sinon pourquoi, non?

Je souhaite diffuser les meilleures pratiques au sein de mon entreprise, mais je dois pouvoir démontrer et sauvegarder toutes les recommandations que je fais.

Je veux aussi comprendre ce qui se passe exactement.

Sam Elstob
la source

Réponses:

12

Il existe plusieurs façons de tuer un processus à cause d'un terminal en voie de disparition.

  1. La première manière est que le pilote de terminal dans le noyau envoie un signal SIGHUP au processus de contrôle pour lequel le terminal est le terminal de contrôle . Dans la plupart des cas, le processus de contrôle est le shell qui est initialement démarré dans le terminal, et son terminal de contrôle est celui auquel ses stdin, stdout et stderr sont connectés. Un processus se déconnecte du terminal de contrôle s'il appelle setsid.

  2. La deuxième façon est que le shell, lorsqu'il reçoit SIGHUP, renvoie ce signal à ses sous-processus - plus précisément, à ses tâches d'arrière-plan. Certains shells (ksh, bash, zsh) ont une fonction disownintégrée pour dire au shell de ne pas envoyer de SIGHUP à un travail particulier.

  3. Si le terminal disparaît et qu'un programme essaie de le lire, il est averti d'une condition de fin de fichier (ou peut-être EIO pour un travail en arrière-plan). Si le terminal disparaît et qu'un programme essaie d'y écrire, l'écriture revient avec une erreur EIO. Cela pourrait entraîner la fermeture du programme.

Donc, ce qui suchange ici, c'est que (2) entraînerait normalement le shell initial pour tuer le travail d'arrière-plan, mais comme ce processus d'arrière-plan s'exécute en tant qu'utilisateur différent, le signal ne peut pas être délivré et le processus d'arrière-plan se poursuit.

Gilles 'SO- arrête d'être méchant'
la source
J'ai vu un utilisateur root faire chroot --userspec root:root / sh -c "exec some_forever_process" &. Le travail s'exécute en tant que même utilisateur, sans explicite nohupavant ou disownaprès. Donc, dans ce cas, comment se fait-il que le signal ne puisse pas être délivré à la sortie du terme?
Toddius Zho
@ToddiusZho Presumable some_forever_processétait destiné à être exécuté indéfiniment (un démon) et se protège ainsi contre la réception de SIGHUP à partir d'une sortie de terminal (en s'exécutant dans son propre groupe de processus).
Gilles 'SO- arrête d'être méchant'
bash / tail ne protège pas contre SIGHUP, mais cela fonctionne toujours: `` `local $ ssh root @ REMOTE_HOST remote # chroot --userspec root: root / sh -c" exec bash -c 'tail -f / 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 pid, uid, command -ww 6376 0 tail -f / dev / null ``
Toddius Zho