Comment démarrer un processus en état suspendu sous Linux?

19

Pratiquement, j'ai besoin d'un processus qui se comporte comme si j'avais appuyé Ctrl+Zjuste après le début.

Avec un peu de chance, il est possible de faire une telle chose en utilisant un script shell.

(De plus, connaître le PID résultant serait formidable, donc je pourrais continuer le processus par la suite.)

java.is.for.desktop
la source

Réponses:

7

Après avoir démarré un processus, vous pouvez lui envoyer SIGSTOP pour le suspendre. Pour le reprendre, envoyez SIGCONT. Je pense que ce petit script peut vous aider

#!/bin/bash
$@ &
PID=$!
kill -STOP $PID
echo $PID
wait $PID

Il exécute le processus (commande envoyer comme paramètre), le suspend, imprime l'ID du processus et attend jusqu'à la fin.

radieux
la source
1
Je l'ai modifié pour continuer à la fin: [entrer] #!/bin/bash [entrer] $@ & [entrer] PID=$! [entrer] kill -STOP $PID [entrer] echo "Suspended: $PID, press ENTER to continue it" [entrer] read [entrer] kill -CONT $PID [entrer] wait $PID [entrer]echo
java.is.for.desktop
7
Juste pour que vous le sachiez, le sous-processus peut se terminer avant même que vous n'ayez la possibilité de lui envoyer le signal STOP.
MikeyB
16
Un autre jour aux courses.
ctrl-alt-delor
23

À partir de quel environnement créez-vous le processus?

Si vous le faites à partir d'un environnement tel que le code C, vous pouvez fork () puis dans l'enfant, vous envoyer un SIGSTOP avant exec (), empêchant ainsi toute exécution.

Une solution plus générique (probablement la meilleure) serait de créer un stub qui le fait. Ainsi, le programme de remplacement:

  • Prendre des arguments composés du nom et des arguments du programme réel
  • envoyer un SIGSTOP à lui-même
  • exec () le vrai programme avec les arguments appropriés

Cela garantira que vous éviterez toutes sortes de conditions de concurrence liées au nouveau traitement d'aller trop loin avant de pouvoir lui envoyer un signal.


Un exemple pour shell:

#!/bin/bash
kill -STOP $$
exec "$@"

Et en utilisant ci-dessus codez:

michael@challenger:~$ ./stopcall.sh ls -al stopcall.sh

[1]+  Stopped                 ./stopcall.sh ls -al stopcall.sh
michael@challenger:~$ jobs -l
[1]+ 23143 Stopped (signal)        ./stopcall.sh ls -al stopcall.sh
michael@challenger:~$ kill -CONT 23143; sleep 1
-rwxr-xr-x 1 michael users 36 2011-07-24 22:48 stopcall.sh
[1]+  Done                    ./stopcall.sh ls -al stopcall.sh
michael@challenger:~$ 

Le jobs -lmontre le PID. Mais si vous le faites à partir d'un shell, vous n'avez pas besoin directement du PID. Vous pouvez simplement faire: kill -CONT %1(en supposant que vous n'ayez qu'un seul emploi).

Le faire avec plus d'un travail? Gauche comme exercice pour le lecteur :)

MikeyB
la source
J'espérais le faire à partir d'un script shell ...
java.is.for.desktop
Génial, donc comme vous voulez le faire à partir d'un script shell, utilisez le programme stub.
MikeyB
20

La réponse de MikeyB est correcte. À partir de cette question sur le superutilisateur, voici une version plus concise:

( kill -SIGSTOP $BASHPID; exec my_command ) &

Pour comprendre cela, il faut comprendre comment les processus sont démarrés sous unix: l' execappel système remplace le programme en cours d'exécution par un nouveau , en préservant le PID existant. Ainsi , la manière d' un processus indépendant est créé est d'abord fork, puis exec, en remplaçant le programme en cours d' exécution dans le processus de l' enfant avec le programme souhaité.

Les ingrédients de cette commande shell sont:

  1. Les parenthèses (...)démarrent un sous-shell: une autre instance de BASH.
  2. La killcommande envoie le signal STOP à ce processus, ce qui le met dans l'état suspendu.
  3. Dès que vous autorisez le processus à se poursuivre (en lui envoyant le CONTsignal), la execcommande le fait se remplacer par le programme souhaité. my_commandconserve le PID d'origine du sous-shell.
  4. Vous pouvez utiliser la $!variable pour obtenir le PID du processus.
nibot
la source
2
J'aime celui-ci, car il est fondamentalement correct (juste que j'ai dû utiliser à la -s STOPplace de -SIGSTOP). Je me demande encore: pourquoi il faut que ce soit au $BASHPIDlieu de $$? (Je ne comprends peut-être pas vraiment la différence).
Hibou57
1
Pour $$vs $BASHPID, je vérifie que ce dernier est le PID du sous-shell, pas le premier. Il y a un drôle de problème: appliquer l'astuce dans un script bash, échoue le plus souvent, et je dois faire quelque chose comme ça: PID=$!; ps -p $PID; kill -s CONT $PID;… il échoue si je supprime la ps -p $PIDpartie: le processus semble disparaître (tué?) Sans message d'erreur n'importe où . C'est OK à partir d'un shell interactif, le problème vient uniquement du script. C'est trop mystérieux.
Hibou57
J'ai essayé d'utiliser cette solution pour répertorier les descripteurs de fichiers de ma_commande. ( goo.gl/cNbUE8 ) mais les descripteurs sont toujours les mêmes quelle que soit ma_commande.
rasty.g
Pour les options à remplacer $BASHPIDpour les obus autres que bash, voir ici
Jakob