0% ont trouvé ce document utile (0 vote)
21 vues95 pages

Gestion des processus sous UNIX

Ce document présente un guide sur le système d'exploitation UNIX, abordant des sujets tels que les éditeurs de texte, la gestion des processus, la communication inter-processus, et la synchronisation. Il détaille les commandes de base, la création et l'héritage de processus, ainsi que la gestion des processus zombies. Le document inclut également des exemples pratiques et des exercices pour illustrer les concepts discutés.

Transféré par

aliyounes
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
21 vues95 pages

Gestion des processus sous UNIX

Ce document présente un guide sur le système d'exploitation UNIX, abordant des sujets tels que les éditeurs de texte, la gestion des processus, la communication inter-processus, et la synchronisation. Il détaille les commandes de base, la création et l'héritage de processus, ainsi que la gestion des processus zombies. Le document inclut également des exemples pratiques et des exercices pour illustrer les concepts discutés.

Transféré par

aliyounes
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Travaux pratiques - Système d’exploitation UNIX

SMI S4

Yasmine Lamari

Faculté des Sciences de Rabat


[Link]@[Link]

30 avril 2018

1/84
Plan

1 UNIX:Premiers pas
Les éditeurs de texte
Compilation et exécution d’un programme
2 Gestion des Processus
Création de processus
Héritage
Processus Zombies
Synchronisation père-fils
3 Communication inter-processus
La communication par pipes
La communication par signaux
4 Résumé

2/84
UNIX: Premiers pas

3/84
Les éditeurs de texte

3/84
Editeur de texte vi I
Unix supporte plusieurs éditeurs de texte : vi, xedit, emacs.
Nous allons utiliser l’éditeur vi que nous exécutons avec la commande :
vi nom programme.c
Vi possède 3 modes de fonctionnement :
Le mode normal: celui dans lequel vous êtes à l’ouverture du fichier. Il
permet de taper des commandes.
Le mode insertion: Ce mode permet d’insérer les caractères que vous
saisissez à l’intérieur du document. Pour passer en mode insertion, il
suffit d’appuyer sur la touche Insert de votre clavier, ou à défaut sur la
touche i.
Le mode de remplacement: Ce mode permet de remplacer le texte
existant par le texte que vous saisissez. Il vous suffit de ré-appuyer sur
insert (ou i) pour passer du mode insertion au mode remplacement, et
d’appuyer sur la touche Echap pour revenir en mode normal.

3/84
Editeur de texte vi II

Commandes de base

4/84
Editeur de texte vi III

Commandes d’édition

5/84
Compilation et exécution d’un programme

6/84
Compilation et exécution d’un programme

premier programme avec vi

1 Pour appeler le compilateur C sous UNIX on utilise la commande :


cc programme.c
2 Toutes les phases de la compilation et du chargement seront exécutées
automatiquement. Le programme, en langage machine, sera dans le
fichier : [Link] Nous pourrons alors l’exécuter en entrant :
[Link]

6/84
Gestion des Processus

7/84
Création de processus

7/84
Création de processus I
Sous Unix, la création de processus se fait avec deux appels système :
fork() et exec().

Primitive fork()
Un appel à fork () par un processus, appelé processus-père, demande à UNIX
de mettre en activité un nouveau processus (appelé processus-fils) qui est une
copie conforme du processus courant, pour la plupart de ses attributs.
Cette fonction rend :
-1 en cas d’échec,
Dans le processus-fils, fork() renvoie 0 alors que dans le processus-père
elle renvoie le PID de la copie.
La syntaxe est la suivante :

7/84
Création de processus II
Remarques:
À l’issue d’un appel système fork() les deux processus s’exécutent
simultanément (pseudo-parallélisme).
Les deux processus (père et fils) sont exactement identiques et exécutent
le même programme sur deux copies distinctes des données.
En effet, les espaces de données sont complètement séparés: la
modification d’une variable dans l’un est invisible dans l’autre.

8/84
Quelques primitives utiles
Primitives getpid() et getppid()
Un processus a accès respectivement à son PID et à celui de son père par
l’intermédiaire respectivement de la fonction getpid() et getppid().

Primitive exit()
La fonction exit(int status) met fin au processus qui l’a émis, avec un code
de retour status.
Si le processus à des fils lorsque exit() est appelé, ils ne sont pas
modifiés mais comme le processus père prend fin, le nom de leur
processus père est changé en 1.
Par convention, un code de retour égal à 0 signifie que le processus s’est
terminé correctement, et un code non nul (généralement 1) signifie
qu’une erreur s’est produite.
Le père du processus qui effectue un exit() reçoit son code retour à
travers un appel à wait().

9/84
Exemple 1: création d’un processus-fils via fork() I

Généralement, on souhaite exécuter des tâches distinctes dans le


processus père et le processus fils.
La valeur retournée par fork() est donc très importante pour différencier
le processus père du processus fils.
Un test (if/else, switch) permet d’exécuter des parties de code
différentes dans le père et le fils.

10/84
Exemple 1: création d’un processus-fils via fork() II

11/84
Exemple 1: création d’un processus-fils via fork() III

12/84
Exercice: fork()

Écrire un programme C qui crée deux fils, l’un affiche les entiers de 1 à 50,
l’autre de 51 à 100.

13/84
Héritage

14/84
Héritage

Le processus-fils hérite:
les propriétaires réels et effectifs,
le répertoire de travail,
la valeur de nice.
Le processus-fils n’hérite pas:
l’identité de son père,
les temps d’exécution car ils sont initialisés à 0 chez le fils,
verrous sur les fichiers détenus par le père,
signaux pendants (arrivées, en attente d’être traités) du père.

14/84
Quelques Fonctions utiles I

getuid()
(propriétaire réel) retourne le numéro d’identification de l’utilisateur à qui
appartient le processus. Il en est de même pour le propriétaire effectif via la
fonction geteuid().

getgid()
(propriétaire réel) retourne le numéro d’identification du groupe à qui
appartient le processus. Il en est de même pour le propriétaire effectif via la
fonction getegid().

getcwd(char * buf, unsigned long taille)


retourne le chemin absolu du répertoire de travail courant.

15/84
Quelques Fonctions utiles II

sleep(int sec)
permet de suspendre l’exécution d’un processus pendant un intervalle de
temps passé en paramètre.

nice(int incr)
À chaque processus est associée une valeur du paramètre système nice,
entre -20 et 19, et qui indique son niveau de priorité (l’importance
relative des processus).
Par défaut, cette valeur est égale à 0.
Plus cette valeur est faible, plus le processus est prioritaire pour accéder
à la ressource CPU de la machine.

16/84
Quelques Fonctions utiles III
times(struct tms *buffer);
retourne les temps CPU de traitement actuels dans struct tms pointée par
buf. Il existe 4 champs dans la structure struct tms:

Le champ tms utime contient le temps CPU passé à exécuter les instructions du
processus appelant.
Le champ tms stime contient le temps CPU passé dans le système lors de l’exécution
des tâches pour le compte du processus appelant.
Le champ tms cutime contient la somme des valeurs tms utime et tms cutime pour
tous les enfants terminés en attente.
Le champ tms cstime contient la somme des valeurs tms stime et tms cstime pour
tous les enfants terminés attendus.

17/84
Exemple 1: caractéristiques des processus père et fils I

Écrire un programme C qui crée un fils, en suite affiche les caractéristiques du


père et du fils en appelant les fonctions: getuid(), geteuid(), getegid(),
getcwd(), nice(), et la structure tms, afin de produire l’affichage suivant:

18/84
Exemple 1: caractéristiques des processus père et fils II

19/84
Copie des données

Juste après l’appel de fork(), les deux processus (père et fils) sont
exactement identiques et exécutent le même programme sur deux copies
distinctes des données.
Les espaces de données sont complètement séparés, c’est à dire, la
modification d’une variable dans l’un est invisible dans l’autre.
L’exemple 2 illustre ce principe à travers la création de deux variables m
et n ayant comme valeurs initiales 1000 chacune, ensuite la création
d’un fils qui héritera au début ces valeurs. Nous allons voir ce qui se
passera lorsque les processus fils et père modifient les valeurs de m et n.

20/84
Exemple 2: Copie de données I

21/84
Exemple 2: Copie de données II

22/84
Processus Zombies

23/84
Processus Zombies I

Lorsque le fils se termine, si son père ne l’attend pas, le fils passe à l’état
defunct (ou zombi) dans la table des processus.
Exemple:

23/84
Processus Zombies II

C’est l’instruction sleep(30) qui en endormant le père pendant 30


secondes, rend le fils zombi (état ”Z ” et intitulé du processus:
”<defunct>”).

24/84
Synchronisation père-fils

25/84
Synchronisation père-fils I

Primitive wait(int *status)


permet à un processus appelant de suspendre son exécution en attente
de recevoir un signal de fin de l’un de ses fils.

25/84
Synchronisation père-fils II
Les principales actions de wait() sont:
Recherche de processus fils dans la table des processus : si le processus n’a pas de fils,
la primitive wait() renvoie une erreur, sinon elle incrémente un compteur.
S’il y a un zombie, il récupère les paramètres nécessaires (accounting) et libère
l’entrée correspondante de la table des processus.
S’il y a un fils mais pas de zombie, alors le processus se suspend (état endormi) en
attente d’un signal.
Lorsque le signal est reçu, la cause du décès est stockée dans la variable ”status”.
3 cas à distingués :
processus stoppé,
processus terminé volontairement par exit(),
processus terminé à la suite d’un signal.
Enfin la primitive wait() permet de récupéré le PID du processus fils : PID =
wait(status).

26/84
Synchronisation père-fils III

Primitive waitpid(pid t pid, int *status, int options)


permet de tester la terminaison d’un processus particulier, dont on
connaı̂t le PID.

27/84
Cas 1: Le processus-père attend la terminaison du
processus-fils I

Afin de simuler le cas normal, c’est-à-dire, lorsque le processus-père


attend la terminaison du processus-fils, on propose l’exemple suivant.
L’exemple consiste à créer un processus-fils à l’aide de la primitive
fork(). Ensuite, le fils affichera son identité à l’aide de la primitive
getpid() et se terminera en utilisant la primitive exit(int statut) avec
un code de retour status.
Côté processus-père, il attend la terminaison du fils à l’aide de la
primitive wait(). Une fois le fils est terminé, le père se réveille et affiche
la valeur de retour du wait() (PID) et le status.

28/84
Cas 1: Le processus-père attend la terminaison du
processus-fils II

29/84
Cas 1: Le processus-père attend la terminaison du
processus-fils III

30/84
Cas 2: Cas d’un Zombie:le père n’attend pas son fils et est
toujours en vie après la mort de son fils I

31/84
Cas 2: Cas d’un Zombie:le père n’attend pas son fils et est
toujours en vie après la mort de son fils II

32/84
Cas 3: Cas d’un Zombie momentanément I

Cas où le père reçoit le signal de terminaison de son fils et n’exécute le


wait qu’après.
Le processus fils reste zombie momentanément, le temps pour le père de
recevoir les différentes informations.

33/84
Cas 3: Cas d’un Zombie momentanément II

34/84
Cas 3: Cas d’un Zombie momentanément III

35/84
Communication inter-processus

36/84
La communication par pipes

36/84
Les pipes

Un tube (pipe en anglais) est un moyen de transmission de données d’un


processus à un autre.
Il existe deux types de tube:
les tubes anonymes, encore appeler tube ordinaire
les tubes nommés

36/84
Les pipes ordinaires I

Les principales caractéristiques sont d’un pipe sont:


La nature d’un pipe est de type FIFO.
Un pipe est unidirectionnel: un processus peut soit lire soit écrire dans
un pipe
L’information disparaı̂t après lecture et le pipe après le dernier processus
La taille du pipe est limitée de 4k à 20k dépendant du matériel.
Le pipe est un objet du type fichier associé à 2 descripteurs de fichier et
à 2 entrées dans la table des fichiers ouverts
Le pipe n’a pas de noms dans l’arborescence de Unix et donc son
ouverture ne peut se faire à l’aide de open mais plutôt de pipe.
La communication n’a lieu qu’entre processus de la même famille par
héritage des descripteurs.

37/84
Les pipes ordinaires II

Les pipes créés par le processus P2 ne permettent la communication qu’entre


les processus P2, P4, P5 et leurs descendances.
Les processus P1 et P3 ne peuvent pas accéder à ces pipes.

38/84
Création d’un pipe ordinaire, lecture et écriture I

Primitive pipe(int p[2])


crée un pipe et lui associe deux descripteurs de fichier dans le tableau de
deux entiers p.
Par définition p[0] est le descripteur pour lire (sortie du pipe) et p[1]
celui pour écrire (entrée du pipe).
La valeur retour de pipe() est 0 en cas de succès, -1 sinon (trop de
descripteurs actifs, ou de fichiers ouverts, etc...).

Exemple:

39/84
Création d’un pipe ordinaire, lecture et écriture II

Primitive read()
permet de lire dans un pipe en utilisant le descripteur p[0] retourné par
pipe().

Exemple:

40/84
Création d’un pipe ordinaire, lecture et écriture III
Primitive write()
permet d’écrire dans un pipe. L’écriture est faite en utilisant le
descripteur p[1] cette fois-ci.
L’écriture sur un pipe fermé en lecture renvoie un signal SIGPIPE d’où
l’interruption du processus si le signal n’est pas ignoré.
Un processus qui tente d’écrire sur un pipe plein reste bloqué (état
endormi).
La fermeture est effectuée par le close.
Exemple:

41/84
Création d’un pipe ordinaire, lecture et écriture IV

Un processus qui ferme les descripteurs p[0] et p[1], renonce définitivement


à l’utilisation du pipe et ne pourra plus l’ouvrir ne disposant d’aucun moyen
pour le faire.

42/84
Communication entre deux processus I

Le schéma de fonctionnement de base est le suivant:


1 On suppose que le père crée un pipe (pipe()) et deux fils (fork()).
2 Le premier fils (producteur) écrit dans le pipe (write()). Ensuite, il se
termine (exit()).
3 Le deuxième fils (consommateur) lit dans le pipe (read()). Ensuite, il se
termine (exit()) également.
4 Le père attend la terminaison de ses deux fils (wait()) pour finir.

43/84
Communication entre deux processus II

44/84
Communication entre deux processus III

Exécutez les tests suivants et observez ce qui se passe:


Remplir le pipe jusqu’à dépasser sa taille.
Fermer le pipe avant de quitter le programme et essayer de le ré-ouvrir.
Lire d’un pipe vide.
Lire deux fois les même données du pipe.

45/84
Les pipes nommés

Un pipe nommé a les mêmes caractéristiques qu’un pipe ordinaire.


En plus, il a un nom sous Unix comme un fichier ordinaire.
Il est créé de la même manière qu’un fichier spécial avec mknod ou
mkfifo, Ensuite, il peut être ouvert avec la primitive open.
Il est accessible par les processus n’ayant pas de lien de parenté.
Il subit les mêmes règles qu’un fichier ordinaire.
Donc, comme son nom l’indique, le pipe dispose d’un nom dans le système
de fichier. Il suffit qu’un processus l’appelle par son nom, et il donne droit au
processus appelant de lire ou écrire en son intérieur.

46/84
Création d’un pipe nommé, lecture et écriture I

Création d’un pipe nommé


Un pipe nommé est créé par la commande mkfifo ou mknod, ou par l’appel
système mknod() ou mkfifo().

Le mode est indiqué par trois sortes de flags :


Type de fichier (S FIFO=0010000 pour un FIFO),
Permission,
Exécution particulière.

47/84
Création d’un pipe nommé, lecture et écriture II

Ouverture d’un pipe nommé


Il existe deux façons pour ouvrir un pipe nommé:
Ouverture bloquante pour la lecture / écriture:
open (/uo/tcom/mypipe, O RDWR, 2);
Ouverture non bloquante pour la lecture / ecriture:
open (/uo/tcom/mypipe, O RDWR | O NDELAY, 2);

Lecture d’un pipe nommé


Une simple lecture d’un FIFO:
read(d, buf, 2000);

48/84
Création d’un pipe nommé, lecture et écriture III

Ecriture d’un pipe nommé


Exemple d’écriture de 5 caractères dans un FIFO de descripteur d:
write(d,”ABCDE”,5);
Si le pipe est plein et que l’ouverture soit non bloquante (le flag
O NDELAY positionné) l’écriture retourne un 0.
Si le pipe est plein et l’ouverture est bloquante (le flag O NDELAY non
positionné) le processus reste bloqué jusqu’à ce qu’il y ait de la place
(par suite d’une lecture).

Suppression d’un pipe nommé


L’utilisation de rm ou unlink permet la suppression d’un tube nommé .

49/84
Exemple: Les pipes nommés, écriture non bloquante,
lecture bloquante I

1 Créer le pipe ”mypipe” à partir du Shell dans votre répertoire avec


mknod mypipe.
2 Écrire un programme P1 qui ouvre de manière non bloquante mypipe,
et écrit dans un buffer des ’A’.
3 Écrire un programme P2 qui ouvre de manière bloquante mypipe, et lit
mypipe et affiche le buffer.

50/84
Exemple: Les pipes nommés, écriture non bloquante,
lecture bloquante II
Programme P1.c:

51/84
Exemple: Les pipes nommés, écriture non bloquante,
lecture bloquante III
Programme P2.c:

52/84
Exemple: Les pipes nommés, écriture non bloquante,
lecture bloquante IV

Exécutez les tests suivants en utilisant 2 terminaux et observez ce qui se


passe :
Lancez en premier P1 et ensuite P2, est ce que la lecture et réalisée?
Lancez en premier P2 et ensuite P1 que se passe-t-il pour la lecture?
Lancez P1 ensuite P2 deux fois en lisant 2000.
Lancez P2 avec lecture de 4000 et P1 avec écriture de 2000.

53/84
Exemple: Les pipes nommés, écriture non bloquante,
lecture bloquante V
Conclusions
Pour la lecture bloquante le processus se bloque jusqu’à réception d’un
message du processus envoyeur, si ce dernier l’envoie avant que le
récepteur se mette en attente, le récepteur continu à attendre jusqu’à
réception d’un autre message.
L’avantage majeur d’un pipe nommé par rapport à un pipe simple est lié
à sa désignation externe. Ainsi n’importe quel processus pourvu qu’il en
ait les droits peut ouvrir le pipe en lecture et en écriture et communiquer
avec d’autre processus ayant ouvert le même pipe.
Quant aux autres limites déjà soulignées, elles sont les mêmes pour les
deux types de pipe: Petite taille, caractère temporaire de l’information,
possibilité de blocage dans le cas de dépassement de la capacité du pipe.

54/84
Écritutre dans un pipe nommé plein

Écrire un programme C qui ouvre mypipe de manière bloquante et permet


l’écriture de 5000 lettres ’P’ dans mypipe, en utilisant un buffer de taille
2000.

55/84
La communication par signaux

56/84
Les signaux I

Un signal correspond à une interruption logicielle: c’est un moyen de


communication soit entre deux processus utilisateurs, soit entre le
système d’exploitation et un processus.
Il est défini par un code numérique dans le fichier:
/usr/include/sys/signal.h ou dans /usr/include/bits/signum.h.
La communication par signaux entre deux processus se fait en deux
étapes:
L’envoi su signal par le processus émetteur.
La prise en compte du signal par le processus récepteur.

56/84
Les signaux II

L’émission du signal est volontaire et donc prévisible dans le


déroulement chronologique du processus émetteur : c’est pour lui un
événement synchrone.
En revanche, la réception de ce signal par le processus récepteur peut
arriver à n’importe quel moment, c’est donc un événement asynchrone.
Contrairement aux autres mécanismes de communication
inter-processus, les signaux ne transportent pas de données, mais
indiquent des actions à effectuer dans certaines circonstances.

57/84
Types de signaux I

58/84
Types de signaux II

la commande kill -l permet d’afficher la liste des signaux.

59/84
Etats d’un signal

Un signal peut être :


Pendant: un signal a été envoyé au processus mais pas encore pris en
compte. Il est mémorisé dans le blocs de contrôle du processus.
Délivré: le signal est pris en compte. Le processus réalise l’action
correspondante à ce signal.
Bloqué: la prise en compte du signal est différée jusqu’à ce que le signal
ne soit plus bloqué. Remarque: les signaux SIGKILL, SIGSTOP et
SIGCONT ne peuvent pas être bloqués.
Ignoré: le signal est délivré mais le bit correspondant dans le bloc de
contrôle du processus est remis à zéro.
Masqué: c’est la même chose que bloqué, le bit correspondant au signal
est à 1 dans le masque.

60/84
Emission d’un signal: kill()
La primitive kill permet l’émission du signal de numéro sig à destination
du processus de numéro pid. Syntaxe:

Les processus émetteurs et destinataire doivent avoir le même


identificateur (réel ou effectif) d’utilisateur.
pid > 0 : le signal est destiné au processus dont le PID = pid.
pid = 0 : le signal est destiné à tous les processus de même groupe que le
processus émetteur à l’exception des processus spéciaux : init, swapper,
sched . . .
pid = 1 : et l’euid n’est pas celui de su le signal est envoyé à tous les
processus (hormis les spéciaux) dont l’uid = euid émetteur.
pid = -1 : et euid = root le signal est envoyé à tous les processus sauf les
processus spéciaux.
pid < -1 : le signal est envoyé à tous les processus dont l’identificateur de
groupe de processus est égal à la valeur absolue de pid.
Kill retourne un 0 s’il n’y a pas d’erreur et -1 dans le cas d’erreur.

61/84
Réception d’un signal: signal()
Trois comportements sont possibles lors de la réception d’un signal :
Action par défaut: SIG DFL; c’est la mort du processus.
Ignorer le signal: SIG IGN; le signal reçu est ignoré par le processus,
sauf le cas SIGKILL. Par exemple un processus peut ignorer le signal
SIGCLD émis à la mort d’un fils: signal(SIGCLD, SIG IGN).
Une routine est exécutée par le processus: cette routine est pointée par
func. Exemple d’une routine traitant le cas d’une écriture dans un pipe
sans lecteur : signal(SIGPIPE, e pipe).
Le numéro du signal sig est le seul argument à passer à la fonction de
traitement du signal.
Au retour de la routine du traitement, le processus reprend son exécution
au point d’interruption.
Lorsque le signal arrive pendant un appel système (read, write, open,
ioctl) sur un device lent (un terminal par exemple) ou pendant un appel
pause ou wait, la routine de traitement du signal sera exécutée, et l’appel
système sera interrompu avec un code de retour -1. Le seul signal ne
pouvant pas être capté est SIGKILL

62/84
Exemple 1: Écriture dans un pipe sans lecteur - SIGPIPE I

Exécution d’une fonction e pipe(int sig) lors de la réception du signal


SIGPIPE d’écriture dans un pipe p sans lecteur (close(p[0])). Cette
fonction affiche ce qui suit:

63/84
Exemple 1: Écriture dans un pipe sans lecteur - SIGPIPE II

64/84
Exemple 2: Ignorer un signal - SIGCLD I

Un processus n’attendant pas la mort du fils en ignorant le signal


SIGCLD, donc on évite le cas de zombie.

65/84
Exemple 2: Ignorer un signal - SIGCLD II

66/84
Exemple 3: Ignorer un signal - SIGHUP

Un processus ignore le signal SIGHUP, c’est-à-dire, même si on ferme


le terminal le processus demeure en vie ! On ne peut donc le tué qu’en
utilisant la commande: kill -9 pid du processus.
Afin de tester cela, exécutez la commande: ps -aux avant et après la
fermeture du terminal dans lequel vous avez exécuté votre code.

67/84
Exemple 4: Utilisation de SIGUSR1 I

1 Un processus-père crée deux fils, ensuite envoie le signal SIGUSR1 ses


fils en un seul appel kill(PID, SIGUSR1).
2 Les deux processus-fils attendent la réception du signal en exécutant
une routine sig user grâce à la primitive signal(SIGUSR1, sig user).

68/84
Exemple 4: Utilisation de SIGUSR1 II

Remarque 69/84
Attente d’un signal - pause() I

La primitive pause() permet à un processus de se mettre en attente


d’un signal qui ne doit pas être ignoré.
Si le signal n’est pas intercepté le processus fait un exit.
Si le signal est traité par une fonction utilisateur, l’exécution du
programme se poursuit après la fonction pause() qui retourne -1 et errno
= EINTR..
Exemple:
1 Un processus-père crée un fils, ensuite il s’endort 10 secondes et envoi le
signal SIGTERM à son fils pour mettre fin à son exécution.
2 Le processus-fils ignore le signal SIGTERM et reste en attente
(pause()).

70/84
Attente d’un signal - pause() II

71/84
Attente d’un signal - pause() III

72/84
Attente d’un signal - pause() IV

Essayez d’exécuter le programme en commentant le kill pour voir si le


signal est envoyé ou non.
Ensuite, essayez de commenter le signal pour voir si le signal est ignorer
et si le processus reste bloqué sur le pause() ou non.

73/84
Activation du signal SIGALRM I

Primitive alarm()
programme un réveil qui enverra un signal SIGALRM au processus
appelant dans nb sec secondes.
Tout appel de alarm() annule et remplace l’éventuelle alarme
précédente. L’appel alarm(0) annule toute alarme précédente et ne
programme pas de nouvelle alarme.
Les demandes d’alarmes ne sont pas empilables;
Les demandes successives non satisfaites ne font que réinitialiser
l’horloge d’alarme
Syntaxe:

74/84
Activation du signal SIGALRM II
Primitive system()
permet de lancer une commande Shell à partir d’un programme.
Exemple:

Exemple:
1 Un processus demande au système d’envoyer à lui même le signal
SIGALRM dans, au plus tôt, 10 secondes en utilisant alarm(10).
2 Suite à la réception du signal SIGALRM, ce même processus exécute une
routine qui affiche sur le terminal Bonjour en grand format, en utilisant
la commande printerbanner [-d] [-t] [-w width] message.

75/84
Activation du signal SIGALRM III

76/84
Activation du signal SIGALRM IV

77/84
Activation du signal SIGALRM V

Enlevez la primitive pause() dans le code précédent et remarquez le


comportement du programme.

78/84
Déroutement d’un signal - setjmp() et longjmp() I

Il est possible à la manière d’un goto de se dérouter vers un point déterminé


d’un programme à l’aide des deux primitives setjmp() et longjmp().

79/84
Déroutement d’un signal - setjmp() et longjmp() II

Primitive setjmp() et longjmp()


La fonction setjmp est assimilable à une étiquette, et longjmp à un
goto.
L’avantage de ces primitives est que les sauts peuvent intervenir même à
l’extérieur d’une procédure ou d’une fonction.
La seule condition est qu’un branchement ne peut se faire qu’à un
endroit où il y a déjà eû exécution.
setjmp sauve l’endroit (pointeur programme et pointeur dans la pile
d’exécution) dans le buffer env, et renvoie 0. On peut alors exécuter
longjmp avec une valeur discriminant. L’exécution reprend alors juste
après le setjmp correspondant, et la pseudo-valeur de retour de setjmp
est la valeur discriminant.

80/84
Déroutement d’un signal - setjmp() et longjmp() III

Le programme suivant attend 15 secondes une donnée d’entrée.


Après 15 secondes la fonction timeout est exécutée.
Au retour la fonction longjmp(env,1) transmet le contrôle à l’endroit de
l’enregistrement setjmp() avec la valeur 1.

81/84
Déroutement d’un signal - setjmp() et longjmp() IV

82/84
Résumé

83/84
Résumé

83/84

Vous aimerez peut-être aussi