Objectifs :
Manipuler les signaux
Voir des gladiateurs s’entretuer
Se pencher à nouveau sur les redirections
Le but de cet exercice est de vous faire manipuler les signaux. Pour cela, nous recréons une arène à partir de la chaîne de processus que vous avez mise en œuvre à la séance précédente. L’arène est constituée de gladiateurs qui saluent César, et de César qui ordonnent leurs morts pour animer la soirée.
Dans cette première partie, nous créons les gladiateurs. Un gladiateur est un processus qui affiche toutes
les 5 secondes X : Ave Caesar
, où X est le PID du gladiateur, et qui meurt en criant X : Morituri te salutant
lorsqu’il reçoit un signal USR1.
Q1 : Copiez le script chaine.sh
de l’exercice sur les chaînes de processus en gladiateur.sh
. Au lieu d’attendre
la mort d’un enfant, d’afficher “Processus X termine” et de retourner un code d’erreur vrai, gladiateur.sh
doit maintenant exécuter une boucle infinie qui affiche toutes les 5 secondes X : Ave Caesar
où X est le PID
du gladiateur. Dans cette question comme dans toute cette partie, vous devez systématiquement tester votre
script en ne lançant qu’UN UNIQUE gladiateur (./gladiateur.sh 1
).
Q2 : Complétez votre script pour qu’il affiche X : Morituri Te Salutant
lorsqu’il reçoit un signal USR1.
Testez votre script en lançant un gladiateur dans un terminal (./gladiateur.sh 1
) et en envoyant un signal
USR1 au gladiateur avec la commande kill
dans un autre terminal.
Q3 : Complétez votre script pour qu’il termine le processus en renvoyant un code de retour vrai (0) juste
après avoir affiché X : Morituri Te Salutant
.
Q4 : Vous avez dû remarquer que lorsque vous envoyez un signal USR1, votre processus ne reçoit le signal
qu’après avoir terminé son attente de 5 secondes. Ce comportement est dû au fait que la commande interne
sleep
masque les signaux pendant qu’elle s’exécute.
De façon à éviter cette attente, nous utilisons le fait que la commande wait
, elle, peut être interrompue
par un signal. Au lieu de lancer la commande sleep
en avant plan, lancez la en arrière plan, et utilisez wait $!
pour attendre la fin de la commande sleep
(le $! permet de n’attendre que la fin de cette dernière commande,
c-à-d sleep
et non celle de tous les enfants). Testez votre programme avec un unique gladiateur et vérifiez
que le processus n’attend plus la fin de la commande sleep
.
Cette partie va vous montrer qu’il est difficile de tuer plusieurs gladiateurs à la fois.
Q5 : Lancez deux gladiateurs avec la commande ./gladiateur.sh 2
. Envoyez un signal USR1 a un des
gladiateurs. Que constatez vous?
Q6 : Abattez le deuxième gladiateur.
Q7 : Lancez encore une fois deux gladiateurs. Faites un Ctrl+C
. Que constatez-vous?
Q8 : Mettez à mort cet homme innocent qui avait sûrement une famille et des rêves.
Plus aucun gladiateur n’est censé être actif à ce stade.
Comme vous avez pu le constater, il est difficile de terminer plusieurs gladiateurs car lorsque vous envoyez
un signal, il n’est envoyé qu’à un seul gladiateur. Dans cette partie, nous résolvons le problème avec le
processus Caesar. Caesar, qui s’ennuie souvent au Colisée, apprécie envoyer des USR1 à tous les gladiateurs
pour ordonner leur mise à mort.
Pour connaître les PIDs des gladiateurs, Caesar lit un parchemin nommé arene.txt
, dans lequel se trouve
un PID de gladiateur par ligne.
Q9 : Un gladiateur doit maintenant enregistrer son PID dans le fichier arene.txt
de façon à ce que Caesar
puisse le trouver. Modifiez le script gladiateur.sh
pour qu’il ajoute le PID au fichier arene.txt
après avoir
vérifié que les paramètres sont corrects. Testez votre script en lançant deux fois un unique gladiateur que
vous interrompez avec un signal INT, et en vérifiant que arene.txt
contient bien les PID des gladiateurs.
$ ./gladiateur.sh 1
Processus 2460 démarre avec le processus initial 2460
Fin de chaîne
2460 : Ave Caesar
$ cat arene.txt
2460
$ ./gladiateur.sh 1
Processus 2473 démarre avec le processus initial 2473
Fin de chaîne
2473 : Ave Caesar
$ cat arene.txt
2460
2473
Q10 : Avant d’envoyer des signaux, écrivez un script caesar.sh
qui :
affiche un message d’erreur et renvoie faux si arene.txt
n’existe pas
lit ligne à ligne arene.txt
, et affiche chaque ligne sur la sortie standard
supprime le fichier arene.txt
après avoir lu chaque ligne
renvoie un code retour vrai
Q11 : Au lieu d’afficher les PIDs des gladiateurs, caesar.sh
doit maintenant envoyer un USR1 à chaque
gladiateur enregistré dans arene.txt
. Modifiez le script en conséquence.
Q12 : Nous pouvons maintenant utiliser le script caesar.sh
pour terminer proprement tous les gladiateurs
lorsque l’utilisateur utilise un Ctrl+C
ou la commande kill
. Modifiez gladiateur.sh
de façon à lancer caesar.sh
à la réception d’un des signaux de ces deux commandes.
Outre la culpabilité d’être responsable du décès d’un nombre important de pauvres processus, vous venez d’écrire votre premier protocole de terminaison. Techniquement, le protocole que vous venez de mettre en oeuvre permet de terminer proprement un ensemble de processus qui collaborent. Les gladiateurs sont des processus qui offrent un service à l’utilisateur (par exemple, chaque gladiateur pourrait être associé à un utilisateur connecté à un serveur Web), et César est le processus permettant de terminer proprement l’application.
caesar.sh
#! /bin/bash
if [ ! -f arene.txt ]; then
echo "arene.txt does not exist" >&2 # &2 est la sortie d’erreur
exit 1 # un code de retour faux
fi
while read line; do
echo Ceasar kill $line
kill -USR1 $line
done < arene.txt
rm arene.txt
exit 0
gladiator.sh
#! /bin/bash
if [ $# -ne 1 ]; then
echo "Please provide exactly one argument" >&2 # &2 est la sortie d’erreur
exit 1 # un code de retour faux
fi
echo $$ >> arene.txt
trap "./caesar.sh" INT
if [ -z "$pidInitial" ]; then
echo Processus $$ demare
else
echo Processus $$ demare avec le processus initial $pidInitial
fi
export pidInitial=$$
if [ $1 -gt 1 ]; then
k=$(expr $1 - 1)
echo Il reste $k processus à crée
./gladiator.sh $k
else
echo Fin de chaîne
fi
trap "echo $$ : Morituri Te Salutant; exit 0" USR1
while :
do
echo $$ ": Ave Caesar"
sleep 5 &
wait $!
done
echo Processus $$ termine
exit 0