• Aucun résultat trouvé

Attention ! un signal non ignor´e peut interrompre certains appels syst`eme. En g´en´eral, les appels syst`emes interruptibles sont seulement des appels syst`emes dits lents, qui peuvent a priori prendre un temps arbitrairement long : par exemple, lectures/´ecritures au terminal, select (voir plus loin), system, etc. En cas d’interruption, l’appel syst`eme n’est pas ex´ecut´e et d´eclenche l’exception EINTR. Noter que l’´ecriture/lecture dans un fichier ne sont pas interruptibles : bien que ces op´erations puissent suspendre le processus courant pour donner la main `a un autre le temps que les donn´ees soient lues sur le disques, lorsque c’est n´ecessaire, cette attente sera toujours br`eve—si le disque fonctionne correctement. En particulier, l’arriv´ee des donn´ees ne d´epend que du syst`eme et pas d’un autre processus utilisateur.

Les signaux ignor´es ne sont jamais d´elivr´es. Un signal n’est pas d´elivr´e tant qu’il est masqu´e. Dans les autres cas, il faut se pr´emunir contre une interruption possible.

Un exemple typique est l’attente de la terminaison d’un fils. Dans ce cas, le p`ere ex´ecute waitpid [] pid o`u pid est le num´ero du fils `a attendre. Il s’agit d’un appel syst`eme bloquant, donc ≪lent≫, qui sera interrompu par l’arriv´ee ´eventuelle d’un signal. En particulier, le signal

sigchld est envoy´e au processus p`ere `a la mort d’un fils.

Le module Misc contient la fonction suivante restart_on_EINTR de type (’a -> ’b) -> ’a -> ’b qui permet de lancer un appel syst`eme et de le r´ep´eter lorsqu’il est interrompu par un signal, i.e. lorsqu’il l`eve l’exception EINTR.

1 let rec restart_on_EINTR f x =

2 try f x with Unix_error (EINTR, _, _) -> restart_on_EINTR f x

Pour attendre r´eellement un fils, on pourra alors simplement ´ecrire restart_on_EINTR (waitpid flags) pid.

Exemple: Le p`ere peut aussi r´ecup´erer ses fils de fa¸con asynchrone, en particulier lorsque la valeur de retour n’importe pas pour la suite de l’ex´ecution. Cela peut se faire en ex´ecutant une fonction free_children `a la r´eception du signal sigchld. Nous pla¸cons cette fonction d’usage g´en´eral dans la biblioth`eque Misc.

let free_children signal =

try while fst (waitpid [ WNOHANG ] (-1)) > 0 do () done with Unix_error (ECHILD, _, _) -> ()

Cette fonction appelle la fonction waitpid en mode non bloquant (option WNOHANG) et sur n’importe quel fils, et r´ep`ete l’appel quand qu’un fils a pu ˆetre retourn´e. Elle s’arrˆete lorsque il ne reste plus que des fils vivants (z´ero est retourn´e `a la place de l’identit´e du processus d´elivr´e) ou lorsqu’il n’y a plus de fils (exception ECHILD). Lorsque le processus re¸coit le signal sigchld il est en effet impossible de savoir le nombre de processus ayant termin´e, si le signal est ´emis plusieurs fois dans un intervalle de temps suffisamment court, le p`ere ne verra qu’un seul signal. Noter qu’ici il n’est pas n´ecessaire de se pr´emunir contre le signal EINTR car waitpid n’est pas bloquant lorsqu’il est appel´e avec l’option WNOHANG.

Dans d’autres cas, ce signal ne peut ˆetre ignor´e (l’action associ´ee sera alors de lib´erer tous les fils ayant termin´e, de fa¸con non bloquante—on ne sait jamais combien de fois le signal `a ´et´e ´emis).

Exemple: La commande system du module Unix est simplement d´efinie par let system cmd =

match fork() with 0 ->

begin try

execv "/bin/sh" [| "/bin/sh"; "-c"; cmd |]; assert false with _ -> exit 127

end

| id -> snd(waitpid [] id);;

L’assertion qui suit l’appel syst`eme execv est l`a pour corriger une restriction erron´ee du type de retour de la fonction execv (dans les version ant´erieures ou ´egale `a 3.07). L’appel syst`eme ne retournant pas, aucune contrainte ne doit porter sur la valeur retourn´ee, et bien sˆur l’assertion ne sera jamais ex´ecut´ee.

La commande system de la biblioth`eque standard de la biblioth`eque C pr´ecise que le p`ere ignore les signaux sigint et sigquit et masque le signal sigchld pendant l’ex´ecution de la commande. Cela permet d’interrompre ou de tuer le programme appel´e (qui re¸coit le signal) sans que le programme principal ne soit affect´e pendant l’ex´ecution de la commande.

Nous pr´ef´erons d´efinir la fonction system comme sp´ecialisation d’une fonction plus g´en´erale exec_as_system qui n’oblige pas `a faire ex´ecuter la commande par le shell. Nous la pla¸cons dans le module Misc.

1 let exec_as_system exec args =

2 let old_mask = sigprocmask SIG_BLOCK [sigchld ] in 3 let old_int = signal sigint Signal_ignore in

4 let old_quit = signal sigquit Signal_ignore in 5 let reset() =

6 ignore (signal sigint old_int); 7 ignore (signal sigquit old_quit);

8 ignore (sigprocmask SIG_SETMASK old_mask) in 9 let system_call () =

10 match fork() with 11 | 0 -> 12 reset(); 13 begin try 14 exec args 15 with _ -> exit 127 16 end 17 | k ->

18 snd (restart_on_EINTR (waitpid []) k) in 19 try_finalize system_call() reset();;

20

21 let system cmd =

22 exec_as_system (execv "/bin/sh") [| "/bin/sh"; "-c"; cmd |];;

Noter que le changement des signaux doit ˆetre effectu´e avant l’appel `a fork. En effet, imm´edia- tement apr`es cet appel, seulement l’un des deux processus fils ou p`ere a la main (en g´en´eral le fils). Pendant le laps de temps o`u le fils prend la main, le p`ere pourrait recevoir des signaux, notamment sigchld si le fils termine imm´ediatement. En cons´equence, il faut remettre les signaux `a leur valeur initiale dans le fils avant d’ex´ecuter la commande (ligne 13). En effet, l’ensemble des signaux ignor´es est pr´eserv´e par fork et exec et le comportement des signaux est lui-mˆeme pr´eserv´e par fork. La commande exec remet normalement les signaux `a leur valeur par d´efaut, sauf justement si le comportement est d’ignorer le signal.

Enfin, le p`ere doit remettre ´egalement les signaux `a leur valeur initiale, imm´ediatement apr`es l’appel, y compris en cas d’erreur, d’o`u l’utilisation de la commande try_finalize (ligne 20).