J'utilise `&`: pourquoi le processus ne s'exécute-t-il pas en arrière-plan?

24

Je sais que je peux ajouter &une commande pour exécuter le processus en arrière-plan.

Je suis SSH dans une boîte Ubuntu 12.04 et j'exécute un programme python avec $python program.py &- mais quand je vais fermer la fenêtre du terminal, je reçois un message disant que la fermeture du terminal tuera le processus en cours.

Pourquoi est-ce? J'utilise l'esperluette pour exécuter le processus en arrière-plan. Comment puis-je le faire fonctionner, que je sois SSH ou non?

bernie2436
la source
Duplicate of unix.stackexchange.com/q/4004/69080
Joshua Huber
écran d'installation d'apt-get, écran homme
captcha

Réponses:

51

Lorsque vous fermez une fenêtre de terminal, l'émulateur de terminal envoie un SIGHUP au processus qu'il exécute, votre shell. Votre shell transmet ensuite ce SIGHUP à tout ce qu'il exécute. Sur votre système local, c'est le ssh. Le ssh transmet ensuite le SIGHUP à ce qu'il exécute, le shell distant. Votre shell distant envoie alors un SIGHUP à tous ses processus, votre programme en arrière-plan.

Il y a 2 façons de contourner cela.

  1. Dissociez le programme en arrière-plan de votre shell.
    1. Utilisez la disowncommande après avoir mis en arrière-plan votre processus. Cela fera oublier la coquille.
    2. Préfixez votre commande avec nohup( nohup $python program.py &). Cela accomplit la même chose, mais en utilisant un processus intermédiaire. Fondamentalement, il ignore le signal SIGHUP, puis exécute et exécute votre programme qui hérite du paramètre, puis quitte. Puisqu'il a bifurqué, le programme en cours de lancement n'est pas un enfant du shell, et le shell ne le sait pas. Et à moins qu'il n'installe un gestionnaire de signaux pour SIGHUP, il conserve quand même l'action ignorer.
  2. Utilisez logoutau lieu de fermer la fenêtre du terminal. Lorsque vous utilisez logout, ce n'est pas un SIGHUP, et donc le shell n'enverra aucun SIGHUP à aucun de ses enfants.

De plus, vous devez vous assurer que votre programme n'écrit pas sur le terminal via STDOUT ou STDERR, car les deux n'existeront plus une fois le terminal fermé. Si vous ne les redirigez pas vers quelque chose comme /dev/null, le programme fonctionnera toujours, mais s'il essaie de leur écrire, il obtiendra un SIGPIPE, et l'action par défaut de SIGPIPE est de tuer le processus).

Patrick
la source
4
Techniquement, la sshmort entraîne la perte de la connexion, la connexion à la baisse provoque sshdla mort à l'autre extrémité. Ce sshd contrôlant le côté maître du pseudo-terminal que le shell distant exécute, quand il meurt, c'est un blocage (c'est comme tirer la fiche sur un vrai terminal), donc le système envoie un SIGHUP au shell distant.
Stéphane Chazelas
@ StéphaneChazelas C'est une chose dans laquelle je n'ai jamais creusé. Si c'est le noyau qui le fait, comment détermine-t-il quel processus SIGHUP? De toute évidence, il ne SIGHUP tout avec un descripteur de fichier ouvert pour ce TTY, car les programmes continueront de fonctionner tant qu'ils n'essaieront pas de l'utiliser. Alors, choisit-il le programme qui lit actuellement depuis STDIN (puisqu'un seul peut lire depuis STDIN à la fois)?
Patrick
4
Tant pis, a répondu à ma propre question. POSIX IEEE 1003.1 chap 11, Si une déconnexion de modem est détectée par l'interface de terminal pour un terminal de contrôle ... le signal SIGHUP doit être envoyé au processus de contrôle .
Patrick
2
Notez également que nohupgénère un nohup.outfichier avec la sortie du programme que vous démarrez avec. Il peut être ennuyeux de supprimer ce fichier chaque fois que vous avez utilisé nohup pour démarrer une application de cette façon (ou vous auriez besoin de rediriger sa sortie vers /dev/null).
Ruslan
1
Au lieu de nohup python program.py &je recommanderais d'utiliser à la setsid python program.pyplace, ce qui désavoue immédiatement le programme.
Hitechcomputergeek
14

Le processus s'exécute en arrière-plan dans le terminal, mais la sortie de stdout(et stderr) est toujours envoyée au terminal. Pour arrêter cela, ajoutez > /dev/null 2>&1avant le &pour rediriger les deux sorties vers /dev/null- l'ajout disowngarantit également que le processus n'est pas tué après la fermeture du terminal:

COMMAND > /dev/null 2>&1 & disown

Dans votre cas, ce serait:

python program.py > /dev/null 2>&1 & disown
Wilf
la source
3

L' &opérateur sépare les commandes à exécuter en parallèle, tout comme les ;commandes à exécuter en série. Les deux types de commandes s'exécuteront toujours en tant qu'enfant du processus shell .

Ainsi, lorsque vous fermerez le shell qui a déclenché ces enfants, les enfants seront également fermés.

Ce que vous semblez vouloir, c'est un processus démon , ce qui est beaucoup plus délicat car il doit se dissocier entièrement du processus parent. Le shell n'a généralement pas de moyen simple de le faire.

gros nez
la source
1

Lorsque vous vous déconnectez, les processus d'arrière-plan associés à la session de connexion sont également normalement supprimés. Si vous souhaitez qu'ils soient déconnectés de la session, exécutez-les avec nohup.

nohup python program.py &
Barmar
la source
1

Après la commande se terminant par esperluette ( &), à l'invite de commandes, exécutez la commande bg:

bash> python program.py &  
bash> bg  

Cela mettra la commande "&" en arrière-plan

bash> jobs  

Cela répertoriera les travaux en cours d'exécution en arrière-plan

bash> fg 1   

Cela mettra Job # 1 au premier plan

Une autre façon, (pour pouvoir vous déconnecter)

bash> at now  
bash> /full/path/python /full/path/program.py  
bash> ^d   `(# That is, Control-D, to run the command, Control-C to cancel)`  

Plusieurs lignes peuvent être soumises à la atcommande, avant le ^ d (Control-D)
voir man at.

Martin
la source