J'ai un certain nombre de scripts bash qui exécutent de nombreuses tâches similaires et utilisent des programmes binaires externes. Le problème est que les programmes binaires ne se terminent souvent pas comme ils le devraient. Comme mes scripts les exécutent des milliers de fois, il arrive rapidement que beaucoup d'instances inactives/presque mortes de ces processus s'accumulent. Je ne peux pas réparer ces programmes, donc je dois m'assurer que mes scripts bash les terminent.Tuer tous les processus démarrés par un script Bash
Il existe déjà des sujets dans SE qui traitent de cette tâche d'arrêt des processus de scripts bash. J'ai appliqué et testé ce qui était écrit là-bas, et dans une certaine mesure cela fonctionne. Mais ça ne marche pas assez bien pour mon cas, et je ne comprends pas pourquoi, donc j'ouvre une nouvelle question.
Mes scripts ont une hiérarchie, ici représentée de manière simplifiée: Le script A appelle le script B, et le script B appelle plusieurs instances du script C en parallèle pour utiliser toutes les CPU. Par exemple. Le script B exécute 5 instances du script C en parallèle, et lorsqu'une instance du script C est terminée, elle en lance une nouvelle, au total des milliers d'exécutions du script C. Et le script C appelle plusieurs binaires/commandes externes qui ne se terminent pas bien. Ils sont en parallèle en arrière-plan et communiquent entre eux.
Cependant, mon script C est capable de détecter quand les commandes externes sont terminées avec leur travail, même si elles ne sont pas terminées, et ensuite mon script bash se ferme.
Afin de mettre fin à tous les programmes externes au cours de la fin du script bash, j'ai ajouté un piège de sortie:
# Exit cleanup
cleanup_exit() {
# Running the termination in an own process group to prevent it from preliminary termination. Since it will run in the background it will not cause any delays
setsid nohup bash -c "
touch /tmp/trace_1 # To see if this code was really executed to this point
# Trapping signals to prevent that this function is terminated preliminary
trap '' SIGINT SIGQUIT SIGTERM SIGHUP ERR
touch /tmp/trace_2 # To see if this code was really executed to this point
# Terminating the main processes
kill ${pids[@]} 1>/dev/null 2>&1 || true
touch /tmp/trace_3
sleep 5
touch /tmp/trace_4
kill -9 ${pids[@]} 1>/dev/null 2>&1 || true
touch /tmp/trace_5
# Terminating the child processes of the main processes
echo "Terminating the child processes"
pkill -P ${pids[@]} 1>/dev/null 2>&1 || true
touch /tmp/trace_6
sleep 1
pkill -9 -P ${pids[@]} 1>/dev/null 2>&1 || true
touch /tmp/trace_7
# Terminating everything else which is still running and which was started by this script
pkill -P $$ || true
touch /tmp/trace_8
sleep 1
pkill -9 -P $$ || true
touch /tmp/trace_9
"
}
trap "cleanup_exit" SIGINT SIGQUIT SIGTERM EXIT
Maintenant, cela semble fonctionner si je ne diffusez que très peu de cas de script C en parallèle . Si j'augmente le nombre à plus, par ex. 10 (le poste de travail est puissant et devrait être capable de gérer des douzaines d'instances parallèles du script C et des programmes externes en parallèle), alors cela ne fonctionne plus, et des centaines d'instances des programmes externes s'accumulent rapidement.
Mais je ne comprends pas pourquoi. Par exemple, le PID d'un de ces processus qui accumulaient était 32048. Et dans les journaux, je peux voir l'exécution du piège de sortie:
+ echo ' * Snapshot 190 completed after 3 seconds.'
* Snapshot 190 completed after 3 seconds.
+ break
+ cleanup_exit
+ echo
+ echo ' * Cleaning up...'
* Cleaning up...
+ setsid nohup bash -c '
touch /tmp/trace_1 # To see if this code was really executed to this point
# Trapping signals to prevent that this function is terminated preliminary
trap '\'''\'' SIGINT SIGQUIT SIGTERM SIGHUP ERR
touch /tmp/trace_2 # To see if this code was really executed to this point
# Terminating the main processes
kill 31678' '32048 1>/dev/null 2>&1 || true
touch /tmp/trace_3
sleep 5
touch /tmp/trace_4
kill -9 31678' '32048 1>/dev/null 2>&1 || true
touch /tmp/trace_5
# Terminating the child processes of the main processes
pkill -P 31678' '32048 1>/dev/null 2>&1 || true
touch /tmp/trace_6
sleep 1
pkill -9 -P 31678' '32048 1>/dev/null 2>&1 || true
touch /tmp/trace_7
# Terminating everything else which is still running and which was started by this script
pkill -P 31623 || true
touch /tmp/trace_8
sleep 1
pkill -9 -P 31623 || true
touch /tmp/trace_9
'
De toute évidence, le PID de ce processus a été utilisé dans le piège de sortie, mais le processus n'a pas quitté. Pour tester, j'exécute manuellement la commande kill sur ce processus, puis elle se ferme.
Et le plus intéressant, seuls les fichiers de trace jusqu'au numéro 5 apparaissent. Rien au-delà de 5, mais pourquoi?
Mise à jour: Je viens de découvrir que même si je n'exécute qu'une seule instance du script C en parallèle, c'est-à-dire séquentiellement, cela ne fonctionne que pendant un certain temps. Soudain, à un certain moment, les processus ponctuels ne se terminent plus, mais commencent à traîner pour toujours et à s'accumuler. La machine ne doit pas être surchargée avec un processus en parallèle. Et dans mes fichiers journaux le piège de sortie est encore appelé correctement comme avant, pas de différence là-bas. La mémoire est également gratuite, les processeurs sont également partiellement gratuits.
S'il vous plaît lire https://stackoverflow.com/help/mcve et essayer de créer un exemple minimal, complet et vérifiable – Vinny
@Vinny: C'est généralement une bonne idée, mais serait très difficile dans ce cas, car les programmes externes qui sont utilisés par mes scripts qui causent le problème sont des paquets scientifiques volumineux et complexes (non disponibles dans les repos), et je n'ai pas d'autres programmes plus simples qui reproduiraient leur comportement. – Jadzia
Eh bien, pense que quelqu'un qui veut essayer et aider les besoins d'environ ~ 10min juste pour lire la question. Juste dire, votre question serait beaucoup plus sensible si elle était plus courte (ou plus précise à quel est le problème) – Vinny