Mis à jour le 7 mars 2006
TD 4 : processus et signaux

TD 4 : processus et signaux

Mardi 31 janvier 2006


L'objectif de ce td est double : d'une part, il vous donne un aperçu rapide de ce qu'est un shell-script (Bash); d'autre part, au travers de ce script et de sa traduction en OCaml, il vous permet de voir et de mettre en oeuvre les divers appels systèmes de gestion de processus.

1  Shell script

Soit le shell-script suivant :
      
#!/bin/bash
set -o noclobber
NAME=tictac
LOCK=/tmp/$NAME.lock

error () {
  echo "$1" 1>&2
  exit 1
}

once () {
  if [ -f "$LOCK" ]
  then
    xterm -g 50x2 -e bash -c \
      "echo -n 'Daemon '$NAME' running pid='; cat $LOCK; sleep 5"
  fi
}

launch () {
  while true
  do
    if sleep 60
    then once
    else exit 1
    fi
  done
}

usage () {
    error "Usage: $0 [ -launch | -kill ] "
}

if [ $# != 1 ]
then
    usage
fi
case "$1" in
  -launch)
     if [ -f "$LOCK" ]
     then
       error "Daemon already running (not launched)!"
     else
       echo -n '' > "$LOCK" || error "Cannot write $LOCK"
       echo -n "Launching $NAME... "
       launch &
       echo $! >> "$LOCK"
       echo "done (pid=$!)"
     fi
     ;;
  -kill)
     if [ -f "$LOCK" ]
     then
       PID=$(cat "$LOCK")
       case "$PID" in [1-9][0-9]*) ;; *) error "$LOCK does not contain a pid";; esac
       echo -n "Killing daemon $PID... "
       if kill -KILL "$PID"
       then
         echo "Done"
         rm "$LOCK"
       else
         echo "Process $PID not running (cleaning $LOCK)!" 1>&2
         rm "$LOCK"
         exit 1
       fi
     else
       error "Daemon not running"
     fi
     ;;
  *) usage
esac
Que fait ce shell-script ? On pourra en particulier s'aider de la page de manuel de bash, dans lequel il est possible de rechercher un mot avec la touche / .

(corrigé)
Comment le shell-script connaît-il le numéro du processus qu'il doit arrêter ?

(corrigé)
Comment le shell-script vérifie-t-il que le processus est toujours actif ?

(corrigé)
Pourquoi dans ce shell-script doit-on tester la valeur de retour de chaque commande ?

(corrigé)

2  Une traduction en OCaml

La traduction en OCaml que nous allons proposer n'est pas une traduction exacte du shell-script. En effet, nous allons essayer de gérer proprement l'interruption des appels systèmes par des signaux. Il est important de bien distinguer dans le programme ci-dessus les fonctionnalités fournies par le shell (que nous devons réécrire en Ocaml) des fonctionnalités fournies par des commandes extérieures (nous devons réutiliser ces commandes extérieures depuis Ocaml).

Définir en Ocaml les constantes name et lock correspondant aux constantes NAME et LOCK du script shell précédent, puis écrire une fonction error équivalente à la fonction error du script (1>&2 indique que l'affichage doit être effectué sur la sortie d'erreur).
(corrigé)
Écrire une fonction mon_execvp telle que mon_execvp cmd argv exécute la commande cmd (Unix.excevp) dans un processus différent (Unix.fork) avec les arguments présents dans le tableau de chaînes de caractères argv. La fonction attend la fin de la commande cmd (Unix.waitpid) avant de se terminer. La fonction retourne la valeur de retour de la commande ou lève une exception. Attention, l'attente peut être intérompue par l'arrivée d'un signal (levée de l'exception Unix_error(EINTR,_,_), il faut alors relancer l'attente.
(corrigé)
Cette fonction mon_execvp ne prend pas en compte tous les cas où des signaux peuvent arriver. Modifier votre fonction pour que le signal SIGCHLD soit bloqué (Unix.sigprocmask) et les signaux SIGINT et SIGQUIT soient ignorés (Sys.signal avec Sys.Signal_ignore). Pensez également à remettre en place le comportement initial des signaux avant la fin de la commande quelle que soit la façon dont elle se termine.
(corrigé)
En utilisant la fonction mon_execvp, écrire une fonction mon_system qui exécute une commande passée en argument sous la forme d'une chaîne de caractères en passant cette dernière en argument de la commande /bin/sh -c.
(corrigé)
Écrire une fonction succeed telle que succeed f x appelle f x et retourne true si f x se termine normalement, false si l'appel a levé une exception de type Unix_error (l'exception est alors ignorée) et propage les exceptions différentes de Unix_error.
(corrigé)
En utilisant la fonction succeed précédente, écrire une fonction file_exists (correspondant à test -f) qui retourne true si le fichier dont le nom est passé en argument existe. Pour cela, on pourra utiliser la fonction Unix.access.

(corrigé)
En utilisant les fonctions mon_execvp et file_exists écrire une fonction once équivalente à celle du shell-script précédent.
(corrigé)
Écrire en utilisant mon_excevp une fonction launch qui a le même comportement que celle du shell-script.
(corrigé)
Écrire une fonction background telle que background lock f x appelle f x après avoir effectué un fork, et qui utilise un fichier de nom lock pour s'assurer qu'il n'y ait qu'une exécution de f x à la fois. Pour cela, on ouvrira le fichier avec les attributs O_EXCL et O_CREAT. Le fichier lock devra contenir le numéro de processus exécutant f x.
(corrigé)
Écrire une fonction read_pid_from_file qui retourne le numéro du processus stocké dans le fichier dont le nom est passé en argument. On pourra utiliser la fonction input_line de Ocaml.
(corrigé)
Écrire une fonction kill_daemon telle que kill_daemon lock termine le processus dont le numéro est présent dans le fichier de nom lock. Comme dans le shell-script on fera attention à traiter tous les cas d'erreurs possibles.
(corrigé)
On souhaite maintenant ne pas effacer le fichier si la commande kill a échoué parce que l'on n'avait pas les droits nécessaires pour arrêter le processus. Modifier la fonction kill_daemon en conséquence (il est plus compliqué de le faire avec shell-script).
(corrigé)
Terminer le programme pour lire les arguments de la ligne de commande.
(corrigé)



This document was translated from LATEX by HEVEA and HACHA.