Les TD/TP avec corrections Cours « Programmation Système

Transcription

Les TD/TP avec corrections Cours « Programmation Système
UNIVERISTE MOHAMMED PREMIER
Faculté des Sciences d’Oujda
Oujda - Maroc
Les TD/TP avec corrections
Cours « Programmation Système »
Programme : Filière – SMI – S6
Pr. El Mostafa DAOUDI
Département de Mathématiques et
d’Informatique
Année universitaire 2014/2015
Contenu
Enoncés
TD/TP série N° 1 …………………...……………………............................................................................... 3
TD/TP série N° 2 …………………...………………………………………………………………………... 5
TD/TP série N° 3 …………………...……………………………………………………………………..… 6
TD/TP série N° 4 …………………...……………………………………………………………………….. 7
Corrections
TD/TP série N° 1 …………………...……………………............................................................................. 8
TD/TP série N° 2 …………………...………………………………………………………………………. 16
TD/TP série N° 3 …………………...………………………………………………………………………. 23
TD/TP série N° 4 …………………...………………………………………………………………………. 32
2
Université Mohammed Premier
Faculté des Sciences
Département de Mathématiques
et d’Informatique
Année universitaire 2014-2015
Filière SMI
Module : Programmation Système
Semestre : S6
TD/TP: Série Numéro 1
Partie I : Environnement de compilation : gcc
Supposons que le fichier « calc-moy.c » contient un programme principal écrit en langage « C » (un
programme qui contient la fonction « main() »). Il consiste à calculer et afficher la moyenne des nombres
réels lus au clavier. Les calculs des moyennes s’effectuent à l’aide des fonctions suivantes :
-
La fonction « moy2(arg1,arg2) » calcule et retourne la moyenne des réels « arg1 » et « arg2 ».
-
La fonction « moy3(arg1,arg2,arg3) » calcule et retourne la moyenne des réels « arg1 », « arg2 » et
« arg3 ».
1. On suppose que les fonctions « moy2() » et « moy3() » sont définies dans le «fichier « calc-moy.c » et
qu’elles sont écrites après la fonction « main() ».
-
Ecrire le programme « calc_moy.c » et ensuite le compiler et l’exécuter.
-
Afficher la taille du fichier exécutable.
-
Recompiler avec l’option « -static ». Afficher la taille du fichier exécutable.
-
Conclure.
2. Maintenant, on suppose que les fonctions « moy2() » et « moy3() » sont définies dans des fichiers
séparés : la fonction « moy2() » est définie dans un fichier nommé « moyenne2.c » et « moy3() » est
définie dans un autre fichier nommée « moyenne3.c ».
-
Modifier le programme « calc_moy.c » ensuite le compiler et l’exécuter.
3. Ecrire un fichier nommé « utile.h » qui contient les prototypes des fonction « moy2() » et « moy3() »
ensuite le sauvegarder dans le répertoire courant. Inclure ce fichier dans le programme principal et
recompiler.
4. Recompiler le programme en utilisant la commande « make ».
5. Créer le répertoire « ~/include » et déplacer le fichier « utile.h » dans ce répertoire. Ensuite recomplier et
exécuter le programme principale.
6. Créer une bibliothèque statique nommée « liboutils.a » et une bibliothèque dynamique nommée
« liboutils.so » qui contiennent les fichiers « moyenne2.o » et « moyenne3.o ». Sauvegarder ces
bibliothèques dans le répertoire « ~/lib »
Compiler et exécuter le programme « calc_moy.c » en utilisant ces bibliothèques.
3
Partie II : Introduction au processus
Exercice 1 :
Ecrire un programme telle que l’arbre généalogique des processus engendrés par son exécution et un :
- arbre binaire de profondeur 1
- arbre binaire de profondeur 2
- arbre binaire de profondeur 3
Exercice 2 :
Considérons le programme suivant :
#include<unistd.h>
main( ) {
fork();
fork();
fork();
}
1. Sans faire l’exécution, donner le nombre de processus générés et dessiner l’arbre des processus
engendrés.
2. Remplacez le premier appel à « fork() » par un appel à « execl() » qui exécute le programme luimême. Que se passe-t-il ?
3. Remplacez le deuxième appel à « fork() » par un appel à « execl() » qui exécute le programme luimême. Que se passe-t-il maintenant ?
Exercice 3
Considérons les 3 programmes définis ci-dessous.
1. Sans les exécuter, dessiner l’arbre généalogique engendré par leurs exécutions.
2. Implémenter et exécuter ces programmes pour comparer les résultats.
#include<unistd.h>
main() {
fork() && fork() ;
}
#include<unistd.h>
main() {
( fork() || fork() ) ;
}
#include<unistd.h>
main() {
fork()&&( fork() || fork() ) ;
sleep(2)
}
4
Université Mohammed Premier
Faculté des Sciences
Département de Mathématiques
et d’Informatique
Année universitaire 2014-2015
Filière SMI
Module : Programmation Système
Semestre : S6
TD/TP: Série Numéro 2
Exercice 1 :
Ecrire un programme qui n’accepte de tuer le processus correspondant par un CTRL-C qu’après vérification
par un mot de passe.
Exercice 2 :
Ecrire un programme qui masque les signaux «SIGINT » et « SIGQUIT » pendant 30 secondes et ensuit les
rétablit. Pour connaitre les combinaisons des touches taper la commande « %stty -a ».
Exercice 3 :
Ecrire un programme qui effectue un ping-pong des signaux entre un processus et son fils. Le signal
« SIGUSR1 » est envoyé par le fils au père et « SIGUSR2 » est envoyé du père au fils. Le premier signal est
envoyé par le père. Dans un premier temps on suppose que le nombre de signaux est égal à 2, ensuite on
récupère le nombre de signaux sur la ligne de commande.
Exercice 4 :
Considérons l'arbre des processus suivant :
p0
p1
p5
p2
p3
p4
P0 crée trois fils P1, P2 et P5. P1 est crée en premier suivi de P2. Le processus P5 est crée après la
terminaison de P1 et P2. Le processus P2 crée P3 puis P4.
Écrire le programme qui permet de créer ces processus en respectant les liens père-fils et l’ordre de création
des processus du schéma. Chaque processus doit se mettre en attente de ses fils avant de se terminer. Chaque
processus Pi exécute une fonction Fi().
Exercice 5 :
Sans utiliser la constante prédéfinie PIPE_BUF, écrire un proramme qui permet de déterminer la capacité
maximale d'un tube anonyme.
Exercice 6
Écrire un programme où le père et le fils communiquent par l'intermédiaire d'un tube anonyme. Le père
envoie au fils N nombres entiers. Le fils calcule la somme et l'envoie au père qui l'affiche à l'écran.
5
Université Mohammed Premier
Faculté des Sciences
Département de Mathématiques
et d’Informatique
Année universitaire 2014-2015
Filière SMI
Module : Programmation Système
Semestre : S6
TD/TP: Série Numéro 3
Exercice 1 :
Le but est de faire communiquer deux processus entre eux en utilisant les tubes anonymes. Écrire un
programme dans lequel :
- Le processus père crée un processus fils et lui envoie un message.
- Le processus fils lit le message et ensuite répond au processus père.
- Le processus père attend la réponse de son fils avant de se terminer.
Exercice 2 :
Le but est de faire communiquer le processus père avec ces deux processus fils.
1. Écrire un programme dans lequel :
a. le processus père crée deux processus fils « pid1 » et « pid2 » et ensuite écrit dans un tube les 26 lettres
de l’alphabet (chaque lettre minuscule est suivie de la même lettre majuscule : aAbBcC….).
b. les processus fils lisent les lettres une à une ensuite les affichent.
2. Modifier le programme pour synchroniser la lecture des processus fils.
a. Le processus fils « pid1 » lit les lettres minuscules.
b. Le processus fils « pid2 » lit les lettres majuscules.
Exercice 3 :
Ecrire un programme dans lequel :
- Le thread principal (fonction « main() ») crée deux threads.
- Le premier thread affiche « nb1 » fois une lettre donnée.
- Le deuxième thread affiche « nb2 » fois une autre lettre donnée.
Les lettres à afficher ainsi que les entiers « nb1 » et « nb1 » sont passés en argument à la fonction de thread.
Exercice 4 :
Le but est de faire coopérer deux threads pour déterminer tous les entiers ≥10 dans un tableau.
1. Écrire un programme qui détermine tous les entiers ≥ 10 dans un tableau.
2. Modifier le programme pour que :
a. le thread principal crée deux thread fils « thr1 » et « thr2 ».
b. le thread « thr1 » détermine tous les entiers ≥ 10 dans la première moitié du tableau.
c. le thread « thr2 » détermine tous les entiers ≥ 10 dans la deuxième moitié du tableau.
d. le thread principal collecte les deux résultats et affiche le résultat final
6
Université Mohammed Premier
Faculté des Sciences
Département de Mathématiques
et d’Informatique
Année universitaire 2014-2015
Filière SMI
Module : Programmation Système
Semestre : S6
TP: Série Numéro 4
TP 1 :
Le but est de synchroniser un producteur (thread principal) avec deux consommateurs (threads fils).
1. Ecrire un programme dans lequel :
- Le thread principal (fonction « main() ») crée deux threads fils « th1 » et « th2 », ensuite écrit dans
« tab » (tableau global) les 26 lettres de l’alphabet (chaque lettre minuscule est suivie de la même
lettre majuscule : aAbBcC….).
- Le premier thread lit et affiche les lettres minuscules.
- Le deuxième thread lit et affiche les lettres majuscules.
2. On suppose maintenant que le thread principal écrit dans une variable partagée les 26 lettres de l’alphabet
(les lettres minuscules uniquement). Ecrire un programme qui permet de synchroniser le thread père avec
le thread fils « th1 » afin que le thread « th1 » lit et affiche les lettres minuscules écrites par le thread père.
3. Modifier le programme précédent afin que le thread principal écrit dans une variable partagée les 26
lettres de l’alphabet (chaque lettre minuscule est suivie de la même lettre majuscule : aAbBcC….) et le
thread fils « th1 » lit et affiche les lettres écrites par le thread père.
4. Dans cette question on synchronise le thread principal avec ces deux threads fils « th1 » et « th2 ».
Modifier le programme précédent afin que le thread « th1 » lit et écrit les lettres miniscules alors que le
thread « th2 » lit et écrit les lettres majuscules.
TP 2 :
Le but est de faire communiquer des processus entre eux en utilisant les files de messages. Écrire un
programme dans lequel :
- Le processus père crée deux processus fils.
- Chaque processus fils envoie (écrit ) « n » messages dans la file. Chaque message est un nom (chaine
de caractères) suivi d’une note (un nombre réel simple précision). L’écriture dans la file s’effectue de
manière concurrente entre les deux processus (les deux processus ne sont pas synchronisés pour écrire
dans la file).
Le processus père attend la terminaison de ses fils ensuite lit les messages écrits dans la file de manière
alternée, c'est-à-dire, il lit un message envoyé par un fils suivi d’un autre message envoyé par l’autre fils et
ainsi de suite
7
Correction
TD/TP: Série Numéro 1
Partie I : Environnement de compilation : gcc
Supposons que le fichier « calc-moy.c » contient un programme principal écrit en langage « C » (un
programme qui contient la fonction « main() »). Il consiste à calculer et afficher la moyenne des nombres
réels lus au clavier. Les calculs des moyennes s’effectuent à l’aide des fonctions suivantes :
- La fonction « moy2(arg1,arg2) » calcule et retourne la moyenne des réels « arg1 » et « arg2 ».
- La fonction «moy3(arg1,arg2,arg3)» calcule et retourne la moyenne des réels « arg1 », « arg2 » et «arg3».
1. On suppose que les fonctions « moy2() » et « moy3() » sont définies dans le «fichier « calc-moy.c » et
qu’elles sont écrites après la fonction « main() ».
- Ecrire le programme « calc_moy.c » et ensuite le compiler et l’exécuter.
Réponse:
 Ci-dessous un exemple de programme
#include <stdio.h>
double moy2(double , double);
double moy3(double, double, double);
void main() {
double m1,m2,m3;
printf("Entrer deux reels double m1 et m2 :\n");
printf("Entrer m1: ");
scanf("%lf",&m1);
printf("Entrer m2: ");
scanf("%lf",&m2);
printf("La moyenne est : %lf \n",moy2(m1,m2));
printf("Entrer trois reels double m1 m2 et m3:\n");
printf("Entrer m1: ");
scanf("%lf",&m1);
printf("Entrer m2: ");
scanf("%lf",&m2);
printf("Entrer m3: ");
scanf("%lf",&m3);
printf("La moyenne est : %lf \n",moy3(m1,m2,m3));
}
double moy2(double m1, double m2) {
return (m1+m2)/2;
}
double moy3(double m1, double m2, double m3) {
return (m1+m2+m3)/3;
}

Compilation
%gcc cal_moy.c
Le fichier sorti (le fichier exécutable) aura pour nom « a.out ». Pour nommer (spécifier le
nom) le fichier exécutable, on utilise l’option « -o ».
8
%gcc -o calc_moy calc_moy.c
Dans ce cas, le nom du fichier exécutable est si on veut que l’exécutable ait le nom
« cal_moy », la
 Afficher la taille d’un fichier on utilise l’ooption « -s » ou « -sh » pour avoir plus d’infos.
%ls -s cal_moy
8 calc_moy
%ls -sh cal_moy
8K calc_moy
Veut dire que la taille du fichier exécutable « calc_moy » est 8Ko.
- Recompiler avec l’option « -static ». Afficher la taille du fichier exécutable.
Reponse:
 Compiler avec l’option « -static ».
%gcc -o calc_moy calc_moy.c -static
 Afficher la taille:
%ls -s cal_moy
572 calc_moy
La taille du fichier «exécutable « calc_moy » est maintenant égale à 572 Ko
- Conclure.
Réponse :
Avec l’option « -static » la taille du fichier exécutable est plus grande car le fichier exécutable
contient aussi les codes des fonctions définies dans les librairies, par exemple les codes des
fonctions « printf() » et « scanf() » dans notre cas.
N.B. Par défaut, l’édition de lien force l’utilisation des librairies dynamique.
2. Maintenant, on suppose que les fonctions « moy2() » et « moy3() » sont définies dans des fichiers
séparés : la fonction « moy2() » est définie dans un fichier nommé « moyenne2.c » et « moy3() » est
définie dans un autre fichier nommée « moyenne3.c ».
- Modifier le programme « calc_moy.c » ensuite le compiler et l’exécuter.
Réponse
 Fichier « moyenne2.c »
double moy2(double m1, double m2) {
return (m1+m2)/2;
}
 Fichier « moyenne3.c »
double moy3(double m1, double m2, double m3) {
return (m1+m2+m3)/3;
}
 Fichier « calc_moy.c »
#include <stdio.h>
double moy2(double , double);
double moy3(double, double, double);
main() {
/* idem programme ci-dessus */
}
/* pas de définition des fonctions « moy2() » et « moy3() » ici
9

Compilation
%gcc -o calc_moy calc_moy.c moyenne2.c moyenne3.c
On peut aussi générer tout d’abord les fichiers objets (on utilise l’option « -c »), ensuite
utiliser ces fichier pour compiler et générer l’exécutable.
%gcc -c moyenne2.c /* génère le fichier « moyenne2.o ») */
%gcc -c moyenne3.c /* génère le fichier « moyenne3.o ») */
Générer l’exécutable
%gcc -o calc_moy calc_moy.c moyenne2.o moyenne3.o
Ou
%gcc -c moyenne2.c /* génère le fichier « moyenne2.o ») */
%gcc -c moyenne3.c /* génère le fichier « moyenne3.o ») */
%gcc -c calc_moy.c /* génère le fichier « calc_moy.o ») */
Générer l’exécutable
%gcc -o calc_moy calc_moy.o moyenne2.o moyenne3.o
3. Ecrire un fichier nommé « utile.h » qui contient les prototypes des fonction « moy2() » et « moy3() »
ensuite le sauvegarder dans le répertoire courant. Inclure ce fichier dans le programme principal et
recompiler.
Réponse :
 Fichier « utile.h »
double moy2(double , double);
double moy3(double, double, double);
 Fichier « calc_moy.c »
#include <stdio.h>
#include »utile.h » /* ceci remplace la définition des prototypes de «moy2()» et «moy3()» */
main() {
/* idem programme ci-dessus */
}
/* pas de définition des fonctions « moy2() » et « moy3() » ici
4. Recompiler le programme en utilisant la commande « make ».
Réponse :
 Le fichier « makefile »
calc_moy: calc_moy.o moyenne2.o moyenne3.o
gcc -o calc_moy calc_moy.o moyenne2.o moyenne3.o
moyenne2.o: moyenne2.c
gcc -c moyenne2.c
moyenne3.o: moyenne3.c
gcc -c moyenne3.c
calc_moy.o: calc_moy.c utile.h
gcc -c calc_moy.c
#pour effacer
clean:
rm -rf *.o
mrproper: clean
rm -rf calc_moy
 Pour compiler avec « make »
%make
10
5. Créer le répertoire « ~/include » et déplacer le fichier « utile.h » dans ce répertoire. Ensuite recomplier et
exécuter le programme principale.
Réponse :
 Ligne de commande (sans « make ») : Il faut rajouter l’option « -I » suivi du chemin pour
spécifier qu'il faut chercher, en plus, les fichiers « .h » dans le répertoire précisé dans le
chemin. Dans notre cas il faut rajouter « -I ~/include »
%gcc -o calc_moy calc_moy.c moyenne2.c moyenne3.c -I ~/include
Ou avec les fichiers objets
%gcc -o calc_moy calc_moy.o moyenne2.o moyenne3.o -I ~/include
 Avec la commande « make », il faut mettre à jour le fichier « makefile »
calc_moy: calc_moy.o moyenne2.o moyenne3.o
gcc -o calc_moy calc_moy.o moyenne2.o moyenne3.o
moyenne2.o: moyenne2.c
gcc -c moyenne2.c
moyenne3.o: moyenne3.c
gcc -c moyenne3.c
calc_moy.o: calc_moy.c ~/include/utile.h
gcc -c calc_moy.c -I ~/include
#pour effacer
clean:
rm -rf *.o
mrproper: clean
rm -rf calc_moy
6. Créer une bibliothèque statique nommée « liboutils.a » et une bibliothèque dynamique nommée
« liboutils.so » qui contiennent les fichiers « moyenne2.o » et « moyenne3.o ». Sauvegarder ces
bibliothèques dans le répertoire « ~/lib »
Compiler et exécuter le programme « calc_moy.c » en utilisant ces bibliothèques.
Réponse
 Pour créer une bibliothèque statique :
% ar -rv -o liboutils.a moyenne2.o
% ar -rv -o liboutils.a moyenne3.o
Ou



% ar -rv -o liboutils.a moyenne2.o moyenne3.o
% ar -rv -o liboutils.a moyenne2.o moyenne3.o
Pour créer une bibliothèque dynamique :
% gcc -o liboutils.so -shared moyenne2.o moyenne3.o
Ensuite il faut « déplacer les fichiers « liboutils.a » et « liboutils.so » dans le répertoire
« ~/lib ».
Compilation: Il faut rajouter l’option « -L » suivi du chemin pour spécifier qu'il faut chercher
les librairies dans le chemin spécifier en plus ensuite dans le chemin standard.
% gcc -o calc_moy calc_moy.c -I ~/include -L ~/lib -loutils
Pour les librairies statiques, il faut rajouter l’option « -static »
% gcc -o calc_moy calc_moy.c -I ~/include -L ~/lib -loutils -static
11
Partie II : Introduction au processus
Exercice 1 :
Ecrire un programme telle que l’arbre généalogique des processus engendrés par son exécution et un :
- arbre binaire de profondeur 1
- arbre binaire de profondeur 2
- arbre binaire de profondeur 3
Arbre binaire
Réponse 1
#include<stdio.h>
#include<unistd.h>
main() {
int pid;
printf("Pere PID = %d \n",getpid());
if(fork()!=0) { /* le père */
if(fork()==0) { /* fils gauche */
printf("fils gauche PID = %d PPID = %d \n",getpid(), getppid());
}
Sleep(5); /* pour synchroniser. La primitive « wait() » n’est pas encore vue */
}
else { /* fils droit */
printf("fils droit PID = %d PPID = %d \n",getpid(), getppid());
}
}
Réponse 2 :
/*solution2 avec définition de la fonction « arbre_binaire() »*/
void arbre_binaire(int pid, int *pid_gauche, int *pid_droit) {
if (fork()!=0) { /* ensuite le père crée le fils gauche */
if(fork()==0) {
*pid_gauche=getpid();
printf("fils gauche PID = %d PPID = %d \n",*pid_gauche, getppid());
}
sleep(5);
}
else {
*pid_droit=getpid();
printf("fils droit PID = %d PPID = %d \n",*pid_droit, getppid());
}
}
Arbre binaire de profondeur 2
Réponse 1 :
#include<stdio.h>
#include<unistd.h>
main() {
int pid;
12
if(fork()!=0) {
if(fork()==0) { /* fils gauche */
printf("fils gauche PID = %d PPID = %d \n",getpid(), getppid());
if(fork()!=0) { /* le fils gauche crée ses fils */
if (fork()==0) {
printf("fils gauche du fils gauche PID = %d PPID = %d \n",getpid(), getppid());
}
sleep(5) ;
}
else {
printf("fils droit du fils gauche PID = %d PPID = %d \n",getpid(), getppid());
}
}
sleep(5) ;
}
else {
printf("fils droit PID = %d PPID = %d \n",getpid(), getppid());
if(fork()!=0) {
if(fork()==0) {
printf("fils gauche du fils droit PID = %d PPID = %d \n",getpid(), getppid());
}
sleep(5);
}
else {
printf("fils droit du fils droit PID = %d PPID = %d \n",getpid(), getppid());
}
}
}
Réponse 2 : utilisation de la fonction « arbre_binaire() »
#include<stdio.h>
#include<unistd.h>
void arbre_binaire(int,int *, int *);
main() {
int pid;
int pid_d,pid_g;
int pid_dd,pid_gg;
int pid_gd,pid_dg;
printf("Pere PID = %d \n",getpid());
arbre_binaire(getpid(), &pid_g, &pid_d);
if (getpid()==pid_g)
arbre_binaire(pid_g, &pid_gg, &pid_gd);
if (getpid()==pid_d)
arbre_binaire(pid_d, &pid_dg, &pid_dd);
}
13
Exercice 2 :
Considérons le programme suivant :
#include<unistd.h>
main( ) {
fork();
fork();
fork();
}
1. Sans faire l’exécution, donner le nombre de processus générés et dessiner l’arbre des processus
engendrés.
2. Remplacez le premier appel à « fork() » par un appel à « execl() » qui exécute le programme luimême. Que se passe-t-il ?
3. Remplacez le deuxième appel à « fork() » par un appel à « execl() » qui exécute le programme luimême. Que se passe-t-il maintenant ?
Réponse:
/* Question 1 */
#include<stdio.h>
#include<unistd.h>
main() {
fork();
fork();
fork();
printf("PID = %d PPID = %d \n",getpid(), getppid());
sleep(3);
}
Exécution : L’exécution Génération de 8 processus y compris le père
/*Question 2 */
#include<stdio.h>
#include<unistd.h>
main() {
execl("/home/mdaoudi/exo2_2_fork","exo2_2_fork",NULL);
fork();
fork();
printf("PID = %d PPID = %d \n",getpid(), getppid());
sleep(3);
}
Exécution : Boucle infinie puisque appel récurssif du même programme sans aucune condition d’arrêt.
/*Question 3 :*/
#include<stdio.h>
#include<unistd.h>
main() {
fork();
execl("/home/mdaoudi/exo2_2_fork","exo2_2_fork",NULL);
fork();
printf("PID = %d PPID = %d \n",getpid(), getppid());
sleep(3);
}
Exécution : Le système se bloque car création un nombre infini de processus qui chargera le système.
14
Exercice 3
Considérons les 3 programmes définis ci-dessous.
1. Sans les exécuter, dessiner l’arbre généalogique engendré par leurs exécutions.
2. Implémenter et exécuter ces programmes pour comparer les résultats.
#include<unistd.h>
main() {
fork() && fork() ;
}
Réponse :
Arbre binaire : le père crée deux fils
#include<unistd.h>
main() {
( fork() || fork() ) ;
}
Réponse :
Le père crée un fils qui à son tour crée un fils.
#include<unistd.h>
main() {
fork()&&( fork() || fork() ) ;
sleep(2)
}
Réponse :
Le père crée deux fils. Un des fils crée à son tour un fils.
15
Correction
TD/TP: Série Numéro 2
Exercice 1 :
Ecrire un programme qui n’accepte de tuer le processus correspondant par un CTRL-C qu’après vérification
par un mot de passe.
Réponse :
#include <stdio.h>
#include <sys/signal.h>
char PASS[4]="1234";
void fonc_hand (int s) {
char entree[30];
printf("Entrer le mot de passé: \n ");
scanf("%s",entree);
if(strcmp(entree,PASS)==0) {
signal(SIGINT,SIG_DFL); /* signal(s,SIG_DFL); */
kill(getpid(),SIGINT); /* kill(getpid(),s); */
}
else {
printf("Mot de passe incorrect\n");
}
}
void main() {
signal(SIGINT, fonc_hand);
while (1) {
sleep(1);
printf("le processus boucle .\n");}}
}
}
Exercice 2 :
Ecrire un programme qui masque les signaux «SIGINT » et « SIGQUIT » pendant 30 secondes et ensuit les
rétablit. Pour connaitre les combinaisons des touches taper la commande « %stty -a ».
Réponse
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
main( ) {
int i,p;
signal(SIGINT,SIG_IGN);
signal(SIGQUIT,SIG_IGN);
printf("Les signaux SIGINT et SIGQUIT sont ignorés \n");
sleep(10);
printf("Rétablissement des signaux \n");
signal(SIGINT,SIG_DFL);
signal(SIGQUIT,SIG_DFL);
while(1);
}
16
Exercice 3 :
Ecrire un programme qui effectue un ping-pong des signaux entre un processus et son fils. Le signal « SIGUSR1 » est
envoyé par le fils au père et « SIGUSR2 » est envoyé du père au fils. Le premier signal est envoyé par le père. Dans un
premier temps on suppose que le nombre de signaux est égal à 2, ensuite on récupère le nombre de signaux sur la ligne
de commande.
Réponse :
# include <stdio.h>
# include <stdlib.h>
# include <sys/wait.h>
# include <sys/types.h>
# include <unistd.h>
# include <signal.h>
void handlerSignal ( ) ;
void sendSignal(int pid, int sig) ;
int cptr , limite ;
pid_t pid;
main ( int argc,char **argv ) {
limite=2;
/* Si limite est passé en argument
if(argv[1]==0) {
printf (" format : ping n\n" ) ;
exit(1) ;
}
limite=atoi(argv[1]);
ou
if(argc==1) {
printf (" format : ping n\n" ) ;
exit(1) ;
}
limite=atoi(argv[1]);
*/
cptr =0;
pid=fork();
if(pid==0) {
signal(SIGUSR2, handlerSignal ) ;
printf(" Fils => gestionnaire installe, PID=%d\n" , getpid( )) ;
pause();
while (cptr<limite-1) {
cptr++;
sendSignal(getppid(),SIGUSR1) ;
pause();
}
cptr++;
sendSignal (getppid(),SIGUSR1) ;
exit(0);
}
17
else {
signal(SIGUSR1,handlerSignal) ;
printf("Pere => gestionnaire installe , PID=%d \n", getpid()) ;
cptr++;
sendSignal(pid,SIGUSR2) ;
pause();
while (cptr<limite){
cptr++;
sendSignal(pid,SIGUSR2);
pause();
}
}
}
void handlerSignal(int sig){
printf ("\t \t [%d ] gestionnaire %d => signal capte\n",getpid(), sig);
}
void sendSignal(int pid,int sig){
sleep(1) ;
if ( kill(pid,sig )==-1 ) {
printf ( "ERREUR kill PID=%d\n",pid);
exit(1);
}
printf ( "#%d [%d ] signal %d envoye a %d\n",cptr,getpid(),sig,pid);
}
Exercice 4 :
Considérons l'arbre des processus suivant :
p0
p1
p5
p2
p3
p4
P0 crée trois fils P1, P2 et P5. P1 est crée en premier suivi de P2. Le processus P5 est crée après la
terminaison de P1 et P2. Le processus P2 crée P3 puis P4.
Écrire le programme qui permet de créer ces processus en respectant les liens père-fils et l’ordre de création
des processus du schéma. Chaque processus doit se mettre en attente de ses fils avant de se terminer. Chaque
processus Pi exécute une fonction Fi().
Réponse 1 :
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/types.h>
18
main(){
if(fork()!=0)
if(fork()!=0){
printf("P0 = %d \n",getpid());
wait(0);
wait(0);
if(fork()==0)
printf("P5 = %d père = %d \n",getpid(), getppid());
else
wait(0);
}
else{
printf(" P2 = %d père = %d \n",getpid(), getppid());
if(fork()!=0)
if(fork()==0)
printf(" P4 = %d père = %d \n",getpid(),getppid());
else{
wait(0);
wait(0);
}
else
printf("P3 = %d père = %d \n",getpid(),getppid());
}
else
printf(" P1 = %d père = %d \n",getpid(),getppid());
}
Réponse2 : On remplace les différents « wait() » par une boucle sur le « wait() ».
/* les includes
main(void){
if(fork()!=0){
if(fork()!=0){
printf("P0= %d \n",getpid());
if(fork()==0)
printf("P5=%d pere= %d\n",getpid(),getppid());
}
else{
printf("P2= %d pere= %d\n",getpid(),getppid());
if(fork()!=0) {
if(fork()==0)
printf("P4= %d pere= %d \n",getpid(),getppid());
}
else
printf("P3= %d pere= %d \n",getpid(),getppid());
}
}
else
printf("P1= %d pere = %d \n",getpid(),getppid());
while (wait(0)!=-1);
}
19
Exercice 5 :
Sans utiliser la constante prédéfinie PIPE_BUF, écrire un programme qui permet de déterminer la capacité
maximale d'un tube anonyme.
Réponse
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
main () {
char c='a';
int p[2];
int i;
pipe(p);
printf("Ecriture dans le tube ");
i=0;
while(write(p[1],&c,1)==1){
i=i+1;
printf("PIPE_BUF=%d Le nombre d'octets écrits est %d \n",PIPE_BUF,i);
}
}
Exercice 6
Écrire un programme où le père et le fils communiquent par l'intermédiaire d'un tube anonyme. Le père
envoie au fils N nombres entiers. Le fils calcule la somme et l'envoie au père qui l'affiche à l'écran.
Réponse
La communication du père vers le fils et le calcul de la somme et son affichage chez le fils sont déjà fait dans
un exemple dans le cours. Il reste à faire communiquer la somme au père
Version1 : sans faire des tests
#include <stdio.h>
#include <stdlib.h>
# define N 10 // on suppose que N < la limie maximale
# define NN N*sizeof(int)
main () {
int *c, *s;
int somme=0,som;
/* on peut utiliser la même variable */
int p1[2],p2[2];
int i, nb_lus, nb_ecrits;
c=(int *)malloc(N*sizeof(int));
s=(int *)malloc(N*sizeof(int));
pipe(p1); /* création du premier tube */
pipe(p2); /* création du deuxieme tube */
20
if (fork()!=0) { /* le père */
close(p1[0]);/* on ferme le descripteurs non utilisés */
close(p2[1]);
for (i=0; i<N; i++)
c[i]=i+1;
nb_ecrits=write(p1[1],c,NN)
printf("pere: nb_ecrits = %d \n ", nb_ecrits);
nb_lus=read(p2[0],&som,4);
printf("pere: somme recu est: %d \n ", som);
close(p1[1]);
close(p2[0]);
}
else { // le fils
close(p1[1]);
close(p2[0]);
nb_lus=read(p1[0],s,NN);
somme=0;
for (i=0; i<N; i++)
somme=somme+s[i];
nb_ecrits=write(p2[1],&somme,4);
printf("fils: nb_ecrits = %d \n ", nb_ecrits);
close(p1[0]);close(p2[1]);
}
sleep(5);
}
Version2 : avec les tests
#include <stdio.h>
#include <stdlib.h>
# define N 10 // on suppose que N < la limie maximale
# define NN N*sizeof(int)
main () {
int *c, *s;
int somme=0,som;
/* on peut utiliser la même variable */
int p1[2],p2[2];
int i, nb_lus, nb_ecrits;
c=(int *)malloc(N*sizeof(int));
s=(int *)malloc(N*sizeof(int));
pipe(p1); /* création du premier tube */
pipe(p2); /* création du deuxieme tube */
21
if (fork()!=0) { /* le père */
close(p1[0]);/* on ferme le descripteurs non utilisés */
close(p2[1]);
for (i=0; i<N; i++)
c[i]=i+1;
if ((nb_ecrits=write(p1[1],c,NN))==-1) {
printf("père: Erreur d'écriture dans le tube \n");
exit(0);
}
printf("pere: nb_ecrits = %d \n ", nb_ecrits);
if ((nb_lus=read(p2[0],&som,4))==-1) {
printf("pere: Erreur de lecture a partir du tube \n");
exit(0);
}
else if (nb_lus==0) {
printf("pere: pas de caractère lu \n");
exit(0);
}
printf("pere: somme recu est: %d \n ", som);
close(p1[1]);
close(p2[0]);
}
else { // le fils
close(p1[1]);
close(p2[0]);
if ((nb_lus=read(p1[0],s,NN))==-1){
printf("fils: Erreur de lecture dans le tube \n");
exit(0);
}
else if (nb_lus==0) {
printf("fils: pas de caractère lu \n");
exit(0);
}
somme=0;
for (i=0; i<N; i++)
somme=somme+s[i];
if ((nb_ecrits=write(p2[1],&somme,4))==-1){
printf("fils: Erreur de lecture dans le tube \n");
exit(0);
}
printf("fils: nb_ecrits = %d \n ", nb_ecrits);
close(p1[0]);close(p2[1]);
}
sleep(5);
}
22
Correction
TD/TP: Série Numéro 3
Exercice 1 :
Le but est de faire communiquer deux processus entre eux en utilisant les tubes anonymes. Écrire un
programme dans lequel :
- Le processus père crée un processus fils et lui envoie un message.
- Le processus fils lit le message et ensuite répond au processus père.
- Le processus père attend la réponse de son fils avant de se terminer.
Réponse : à compléter avec les tests
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define MSG1 " Salam fils "
#define MSG2 " Salam père "
int main() {
int p1[2], p2[2];
int nb_lus1,nb_lus2,nb_ecrits1,nb_ecrits2;
int pid;
char buf1[sizeof(MSG1)], buf2[sizeof(MSG2)];
pipe(p1);
pipe(p2);
pid = (int)fork();
if (pid == 0) { /* le processus fils */
close(p2[0]);
close(p1[1]);
nb_lus1 = read(p1[0], buf1, sizeof(buf1));
printf("%s\n", buf1);
nb_ecrits2= write(p2[1], MSG2, sizeof(MSG2));
exit(0);
}
else { /* Processus père*/
close(p1[0]);
close(p2[1]);
nb_ecrits1 = write(p1[1], MSG1, sizeof(MSG1));
nb_lus2 = read(p2[0], buf2, sizeof(buf2));
printf("%s\n", buf2);
close(p1[1]);
close(p2[0]);
}
return 0;
}
23
Exercice 2 :
Le but est de faire communiquer le processus père avec ces deux processus fils.
3. Écrire un programme dans lequel :
a. le processus père crée deux processus fils « pid1 » et « pid2 » et ensuite écrit dans un tube les
26 lettres de l’alphabet (chaque lettre minuscule est suivie de la même lettre majuscule :
aAbBcC….).
b. les processus fils lisent les lettres une à une ensuite les affichent.
4. Modifier le programme pour synchroniser la lecture des processus fils.
a. Le processus fils « pid1 » lit les lettres minuscules.
b. Le processus fils « pid2 » lit les lettres majuscules.
Réponse : Question 1
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
int main() {
int nb_lus,nb_ecrits;
int pid;
char c1,c2;
int i;
int p[2],p1[2],p2[2];
char test[26];
pid_t pid1,pid2;
pipe(p);
pid1=fork();
if (pid1!=0) {
char s[52];
int j;
pid2=fork();
if (pid2!=0) { /*
/* processus père */
close(p[0]);
j=0;
for(i=0;i<26;i++) {
s[j]='a'+i;
s[j+1]='A'+i;
j=j+2;
}
nb_ecrits= write(p[1],s,52*sizeof(char));
wait(0);
wait(0);
exit(0);
}
24
else { /* processus pid2 */
char *buf;
char c2;
buf=(char *)malloc(53*sizeof(char));
close(p[1]);
for(i=0;i<26;i++) {
nb_lus=read(p[0],buf+i,sizeof(char));
}
printf("pid2 %s \n", buf);
}
}
else { /* processus pid1 */
char *buf;
char c2;
buf=(char *)malloc(52*sizeof(char));
close(p[1]);
for(i=0;i<26;i++) {
nb_lus=read(p[0],buf+i,sizeof(char));
}
printf("pid1 %s \n", buf);
}
return 0;
}
Réponse : Question 2 :
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
void hand(int sig) {
}
int main() {
int nb_lus,nb_ecrits;
int pid;
char c1,c2;
int i;
int p[2],p1[2],p2[2];
char test[26];
pid_t pid1,pid2;
pipe(p);
pipe(p1);
pipe(p2);
pid1=fork();
25
if (pid1!=0) {
char s[52];
int j;
pid2=fork();
if (pid2!=0) { /* processus père */
close(p[0]);
j=0;
for(i=0;i<26;i++) {
s[j]='a'+i;
s[j+1]='A'+i;
j=j+2;
}
nb_ecrits= write(p[1],s,52*sizeof(char));
wait(0);
wait(0);
exit(0);
}
else { /* processus pid2 */
char *buf;
char c2;
int pid1,pid2;
signal(SIGUSR1,hand); /* signal(SIGUSR1,SIG_IGN);
pid2=getpid();
nb_lus=read(p1[0],&pid1,sizeof(int));
nb_ecrits= write(p2[1],&pid2,sizeof(int));
buf=(char *)malloc(53*sizeof(char));
close(p[1]);
for(i=0;i<26;i++) {
pause();
nb_lus=read(p[0],buf+i,sizeof(char));
kill(pid1,SIGUSR1);
}
printf("pid2 %s \n", buf);
}
}
else { /* processus pid 1 */
char *buf;
char c2;
int pid1,pid2;
signal(SIGUSR1,hand); /* signal(SIGUSR1,SIG_IGN);
pid1=getpid();
nb_ecrits= write(p1[1],&pid1,sizeof(int));
nb_lus=read(p2[0],&pid2,sizeof(int));
buf=(char *)malloc(52*sizeof(char));
26
close(p[1]);
sleep(3);
for(i=0;i<26;i++) {
nb_lus=read(p[0],buf+i,sizeof(char));
kill(pid2,SIGUSR1);
pause();
}
printf("pid1 %s \n", buf);
}
return 0;
}
Exercice 3 :
Ecrire un programme dans lequel :
- Le thread principal (fonction « main() ») crée deux threads.
- Le premier thread affiche « nb1 » fois une lettre donnée.
- Le deuxième thread affiche « nb2 » fois une autre lettre donnée.
Les lettres à afficher ainsi que les entiers « nb1 » et « nb1 » sont passés en argument à la fonction de thread.
Réponse
# include <pthread.h>
# include <stdio.h>
struct data {
char c;
int count ;
};
void* affiche(void* k ) {
struct data *p = (struct data *)k ;
int i;
for (i = 0; i < (*p).count ; ++i)
fputc ((*p).c , stderr );
/* ou
for (i = 0; i < p-> count ; ++i)
fputc (p->c , stderr );
*/
return NULL ;
}
/* Programme principal . */
int main () {
pthread_t thread1_id ;
pthread_t thread2_id ;
27
struct data arg1 ;
struct data arg2 ;
arg1.c = 'a';
arg1. count = 300;
pthread_create (&thread1_id, NULL, &affiche, &arg1 );
arg2.c ='b';
arg2.count = 400;
pthread_create (&thread2_id , NULL, &affiche, &arg2 );
pthread_join(thread1_id,NULL);
pthread_join(thread2_id,NULL);
return 0;
}
Exercice 4 :
Le but est de faire coopérer deux threads pour déterminer tous les entiers ≥10 dans un tableau.
3. Écrire un programme qui détermine tous les entiers ≥ 10 dans un tableau.
4. Modifier le programme pour que :
a. le thread principal crée deux thread fils « thr1 » et « thr2 ».
b. le thread « thr1 » détermine tous les entiers ≥ 10 dans la première moitié du tableau.
c. le thread « thr2 » détermine tous les entiers ≥ 10 dans la deuxième moitié du tableau.
d. Le thread principal collecte les deux résultats et affiche le résultat final
Réponse 1 :
# include <pthread.h>
# include <stdio.h>
# include <stdlib.h>
#define N 8
/*Affiche des x sur stderr. Paramètre inutilisé. Ne finit jamais */
int tab[N];
int res[N];
int n;
void *calc1 (void* k) { // ou void* affiche_a (void * v) ? {
int *fin=(int *)k ;
int i;
int j=0;
for(i=0;i<*fin;i++)
if(tab[i]>=10) {
res[j]=tab[i];
j++;
}
pthread_exit((void *)j);
}
void *calc2 (void* k) { // ou void* affiche_a (void * v) ? {
int *debut=(int *)k;
28
int i, j;
j=n-1;
for(i=*debut;i<n;i++)
if(tab[i]>=10) {
res[j]=tab[i];
j--;
}
pthread_exit((void *)j);
}
int main () {
void *res1,*res2;
int n1;
int fin,debut;
int i;
pthread_t thread_id1, thread_id2 ;
n=N;// on peut lire ici une valeur de n<N
/* Les valeurs fin (arg1) et debut et fin peuvent être lues au clavier n1 peut être lu au clavier */
fin = n/2; // on peut la lire
debut = fin;
tab[0]=13; tab[1]=2; tab[2]=16; tab[3]=19;
tab[4]=66; tab[5]=11; tab[6]=6; tab[7]=17;
/* Crée 2 nouveaux threads*/
pthread_create (&thread_id1 , NULL , &calc1,(void *)&fin );
pthread_create (&thread_id2 , NULL , &calc2,(void *)&debut);
pthread_join(thread_id1,&res1);
pthread_join(thread_id2,&res2);
n1=(int)res1;
for (i=0; i<n1; i++)
printf(" %d \n",res[i]);
n1=(int)res2;
for (i=N-1; i>=n1+1; i--)
printf(" %d \n",res[i]);
return 0;
}
29
Réponse2:
# include <pthread.h>
# include <stdio.h>
# include <stdlib.h>
#define N 8
int tab[N];
int res[N];
int n;
struct data {
int debut;
int fin ;
};
void *calc1 (void* k) { // ou void* affiche_a (void * v) ? {
struct data *p = (struct data *)k ;
int debut=(*p).debut ;
int fin=(*p).fin;
int i;
int j;
if (debut==0) {
j=0;
for(i=debut;i<fin;i++)
if(tab[i]>=10) {
res[j]=tab[i];
j++;
}
}
else {
j=n-1;
for(i=debut;i<n;i++)
if(tab[i]>=10) {
res[j]=tab[i];
j--;
}
}
pthread_exit((void *)j);
}
int main () {
void *res1,*res2;
int n1;
int i;
pthread_t thread_id1, thread_id2 ;
struct data arg1 ;
struct data arg2 ;
30
n=N;// on peut lire ici une valeur de n<N
/* Les valeurs fin (arg1) et debut et fin peuvent être lues au clavier n1 peut être lu au clavier */
arg1.debut = 0;
arg1.fin = n/2;
arg2.debut = arg1.fin+1;
arg2.fin = n;
tab[0]=1; tab[1]=2; tab[2]=0; tab[3]=9;
tab[4]=6; tab[5]=11; tab[6]=6; tab[7]=0;
/* Crée2 nouveaux threads*/
pthread_create (&thread_id1 , NULL , &calc1 , (void *)&arg1 );
pthread_create (&thread_id2 , NULL , &calc1, (void *)&arg2);
pthread_join(thread_id1,&res1);
pthread_join(thread_id2,&res2);
n1=(int)res1;
for (i=0; i<n1; i++)
printf(" %d \n",res[i]);
n1=(int)res2;
for (i=N-1; i>=n1+1; i--)
printf(" %d \n",res[i]);
return 0;
}
31
Correction
TP: Série Numéro 4
TP 1 :
Le but est de synchroniser un producteur (thread principal) avec deux consommateurs (threads fils).
5. Ecrire un programme dans lequel :
- Le thread principal (fonction « main() ») crée deux threads fils « th1 » et « th2 », ensuite écrit dans
« tab » (tableau global) les 26 lettres de l’alphabet (chaque lettre minuscule est suivie de la même
lettre majuscule : aAbBcC….).
- Le premier thread lit et affiche les lettres minuscules.
- Le deuxième thread lit et affiche les lettres majuscules.
6. On suppose maintenant que le thread principal écrit dans une variable partagée les 26 lettres de l’alphabet
(les lettres minuscules uniquement). Ecrire un programme qui permet de synchroniser le thread père avec
le thread fils « th1 » afin que le thread « th1 » lit et affiche les lettres minuscules écrites par le thread père.
7. Modifier le programme précédent afin que le thread principal écrit dans une variable partagée les 26
lettres de l’alphabet (chaque lettre minuscule est suivie de la même lettre majuscule : aAbBcC….) et le
thread fils « th1 » lit et affiche les lettres écrites par le thread père.
8. Dans cette question on synchronise le thread principal avec ces deux threads fils « th1 » et « th2 ».
Modifier le programme précédent afin que le thread « th1 » lit et écrit les lettres miniscules alors que le
thread « th2 » lit et écrit les lettres majuscules.
Réponse : Question 1 :
# include <pthread.h>
# include <stdio.h>
# include <stdlib.h>
#define N 52
char tab[N+1];
char res1[N/2+1];
char res2[N/2+1];
void *lire1 (void* k) {
int i,j=0;
for(i=0;i<N/2;i++) {
res1[i]=tab[j];
j+=2;
}
printf(" \n thread 1 : %s \n",res1);
pthread_exit(NULL);
}
void *lire2 (void* k) {
int i,j=1;
for(i=0;i<N/2;i++) {
res2[i]=tab[j];
j+=2;
}
printf(" \n thread : %s \n",res2);
pthread_exit(NULL);
}
32
int main () {
int i,j=0;
pthread_t thread_id1, thread_id2 ;
j=0;
for(i=0;i<26;i++) {
tab[j]='a'+i;
tab[j+1]='A'+i;
j=j+2;
}
/* Crée 2 nouveaux threads*/
pthread_create (&thread_id1 , NULL , (void *)&lire1,NULL );
pthread_create (&thread_id2 , NULL , (void *)&lire2,NULL);
pthread_join(thread_id1,NULL);
pthread_join(thread_id2,NULL);
return 0;
}
Réponse : Question 2
#include <stdio.h>
#include <pthread.h>
char v;
int test1 =0, test2=0;
pthread_t th1, th2;
pthread_mutex_t mutex1,mutex2;
pthread_cond_t condition1, condition2;
void *lire1 (void* k);
main ( ) {
int i;
pthread_mutex_init (&mutex1,NULL);
pthread_cond_init (&condition1, NULL);
pthread_mutex_init (&mutex2,NULL);
pthread_cond_init (&condition2, NULL);
v='a';
pthread_create (&th1, NULL, &lire1, NULL) ;
for(i=1;i<26;i++) {
pthread_mutex_lock (&mutex1);
pthread_cond_signal(&condition1);
test1=1;
pthread_mutex_unlock(&mutex1);
pthread_mutex_lock (&mutex2);
while (test2==0)
pthread_cond_wait(&condition2,&mutex2);
test2=0;
v='a'+i;
pthread_mutex_unlock(&mutex2);
}
33
pthread_mutex_lock (&mutex1);
pthread_cond_signal(&condition1);
test1=1;
pthread_mutex_unlock(&mutex1);
pthread_join(th1,NULL);
}
void *lire1 (void* k) {
int i,j=0;
for(i=0;i<26;i++) {
pthread_mutex_lock (&mutex1);
while (test1==0)
pthread_cond_wait(&condition1,&mutex1);
test1=0;
fputc (v , stderr );
pthread_mutex_unlock(&mutex1);
pthread_mutex_lock (&mutex2);
pthread_cond_signal(&condition2);
test2=1;
pthread_mutex_unlock(&mutex2);
}
}
Réponse : Question 3
#include <stdio.h>
#include <pthread.h>
char v;
int test1 =0, test2=0;
pthread_t th1, th2;
pthread_mutex_t mutex1,mutex2;
pthread_cond_t condition1, condition2;
void *lire1 (void* k);
main ( ) {
int i;
pthread_mutex_init (&mutex1,NULL);
pthread_cond_init (&condition1, NULL);
pthread_mutex_init (&mutex2,NULL);
pthread_cond_init (&condition2, NULL);
v='a';
pthread_create (&th1, NULL, &lire1, NULL) ;
34
for(i=1;i<26;i++) {
pthread_mutex_lock (&mutex1);
pthread_cond_signal(&condition1);
test1=1;
pthread_mutex_unlock(&mutex1);
pthread_mutex_lock (&mutex2);
while (test2==0)
pthread_cond_wait(&condition2,&mutex2);
test2=0;
v='A'+i-1;
pthread_mutex_unlock(&mutex2);
pthread_mutex_lock (&mutex1);
pthread_cond_signal(&condition1);
test1=1;
pthread_mutex_unlock(&mutex1);
pthread_mutex_lock (&mutex2);
while (test2==0)
pthread_cond_wait(&condition2,&mutex2);
test2=0;
v='a'+i;
pthread_mutex_unlock(&mutex2);
}
pthread_mutex_lock (&mutex1);
pthread_cond_signal(&condition1);
test1=1;
pthread_mutex_unlock(&mutex1);
pthread_mutex_lock (&mutex2);
while (test2==0)
pthread_cond_wait(&condition2,&mutex2);
test2=0;
v='A'+25;
pthread_mutex_unlock(&mutex2);
pthread_mutex_lock (&mutex1);
pthread_cond_signal(&condition1);
test1=1;
pthread_mutex_unlock(&mutex1);
pthread_join(th1,NULL);
}
35
void *lire1 (void* k) {
int i,j=0;
for(i=0;i<26;i++) {
pthread_mutex_lock (&mutex1);
while (test1==0)
pthread_cond_wait(&condition1,&mutex1);
test1=0;
fputc (v , stderr );
pthread_mutex_unlock(&mutex1);
pthread_mutex_lock (&mutex2);
pthread_cond_signal(&condition2);
test2=1;
pthread_mutex_unlock(&mutex2);
pthread_mutex_lock (&mutex1);
while (test1==0)
pthread_cond_wait(&condition1,&mutex1);
test1=0;
fputc (v , stderr );
pthread_mutex_unlock(&mutex1);
pthread_mutex_lock (&mutex2);
pthread_cond_signal(&condition2);
test2=1;
pthread_mutex_unlock(&mutex2);
}
}
Réponse : Question 4
#include <stdio.h>
#include <pthread.h>
char v;
int test1 =0, test2=0,test3 =0, test4=0;
pthread_t th1, th2;
pthread_mutex_t mutex1,mutex2,mutex3,mutex4;
pthread_cond_t condition1, condition2,condition3, condition4;
void *lire1 (void* k);
void *lire2 (void* k);
main ( ) {
int i;
pthread_mutex_init (&mutex1,NULL);
pthread_cond_init (&condition1, NULL);
pthread_mutex_init (&mutex2,NULL);
36
pthread_cond_init (&condition2, NULL);
pthread_mutex_init (&mutex3,NULL);
pthread_cond_init (&condition3, NULL);
pthread_mutex_init (&mutex4,NULL);
pthread_cond_init (&condition4, NULL);
pthread_create (&th1, NULL, &lire1, NULL) ;
pthread_create (&th2, NULL, &lire2, NULL) ;
v='a';
for(i=1;i<26;i++) {
pthread_mutex_lock (&mutex1);
pthread_cond_signal(&condition1);
test1=1;
pthread_mutex_unlock(&mutex1);
pthread_mutex_lock (&mutex2);
while (test2==0)
pthread_cond_wait(&condition2,&mutex2);
test2=0;
v='A'+i-1;
pthread_mutex_unlock(&mutex2);
pthread_mutex_lock (&mutex3);
pthread_cond_signal(&condition3);
test3=1;
pthread_mutex_unlock(&mutex3);
pthread_mutex_lock (&mutex4);
while (test4==0)
pthread_cond_wait(&condition4,&mutex4);
test4=0;
v='a'+i;
pthread_mutex_unlock(&mutex4);
}
pthread_mutex_lock (&mutex1);
pthread_cond_signal(&condition1);
test1=1;
pthread_mutex_unlock(&mutex1);
pthread_mutex_lock (&mutex2);
while (test2==0)
pthread_cond_wait(&condition2,&mutex2);
test2=0;
37
v='A'+25;
pthread_mutex_unlock(&mutex2);
pthread_mutex_lock (&mutex3);
pthread_cond_signal(&condition3);
test3=1;
pthread_mutex_unlock(&mutex3);
pthread_join(th1,NULL);
pthread_join(th2,NULL);
}
void *lire2 (void* k) {
int i,j=0;
for(i=0;i<26;i++) {
pthread_mutex_lock (&mutex3);
while (test3==0)
pthread_cond_wait(&condition3,&mutex3);
test3=0;
fputc (v , stderr );
fputs ("thread1\n" , stderr );
pthread_mutex_unlock(&mutex3);
pthread_mutex_lock (&mutex4);
pthread_cond_signal(&condition4);
test4=1;
pthread_mutex_unlock(&mutex4);
}
}
void *lire1 (void* k) {
int i,j=0;
for(i=0;i<26;i++) {
pthread_mutex_lock (&mutex1);
while (test1==0)
pthread_cond_wait(&condition1,&mutex1);
test1=0;
fputc (v , stderr );
fputs ("thread2\n" , stderr );
pthread_mutex_unlock(&mutex1);
pthread_mutex_lock (&mutex2);
pthread_cond_signal(&condition2);
test2=1;
pthread_mutex_unlock(&mutex2);
}
}
38
TP 2 :
Le but est de faire communiquer des processus entre eux en utilisant les files de messages. Écrire un
programme dans lequel :
- Le processus père crée deux processus fils.
- Chaque processus fils envoie (écrit ) « n » messages dans la file. Chaque message est un nom (chaine
de caractères) suivi d’une note (un nombre réel simple précision). L’écriture dans la file s’effectue de
manière concurrente entre les deux processus (les deux processus ne sont pas synchronisés pour écrire
dans la file).
- Le processus père attend la terminaison de ses fils ensuite lit les messages écrits dans la file de
manière alternée, c'est-à-dire, il lit un message envoyé par un fils suivi d’un autre message envoyé par
l’autre fils et ainsi de suite.
Réponse :
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#define N 3 // chaque processus fils envoie N message à son père
struct message {
long type;
char nom[25];
float moy;
};
void main() {
key_t cle;
int i;
int id;
int msgid;
char *mn;
pid_t pid1, pid2;
struct message msg1, msg2, msg;
mn=(char *)malloc(25*sizeof(char));
if ((cle = ftok ("./fiche_cle", 20)) == -1) {
printf("erreur de création de la clé \n");
}
if ((msgid=msgget(cle,IPC_CREAT|0660))==-1) {
printf("erreur de création de la file \n");
}
39
pid1=fork();
if (pid1!=0) {
pid2=fork();
if (pid2!=0) { /* le père */
wait(0);
wait(0);
printf("Les message du processus %d \n",pid2);
for (i=0; i<N; i++){
if (msgrcv(msgid,&msg,sizeof(struct message), 2 ,IPC_NOWAIT)==-1) {
printf("erreur reception \n");
exit(1);
}
else {
printf("mesage %d : \n",i+1);
printf("\t nom: %s \t moy: %f \n",msg.nom, msg.moy);
}
}
printf("Les message du processus %d \n",pid1);
for (i=0; i<N; i++){
if (msgrcv(msgid,&msg,sizeof(struct message), 1,IPC_NOWAIT)==-1) {
printf("erreur reception \n");
exit(1);
}
else {
printf("mesage %d : \n",i+1);
printf("\t nom: %s \t moy: %f \n",msg.nom,msg.moy);
}
}
if (msgctl(msgid,IPC_RMID,NULL)==-1)
printf("erreur de suppression \n");
}
else { /* le fils pid2 */
msg.type=2;
/* mesage 1 */
mn="texte1";
strcpy(msg.nom,mn);
msg.moy=12;
if (msgsnd(msgid,&msg,sizeof(msg)-sizeof(long),IPC_NOWAIT)==-1){
printf("erreur1 \n");
}
/* mesage 2 */
mn="text2";
strcpy(msg.nom,mn);
40
msg.moy=13;
if (msgsnd(msgid, &msg,sizeof(msg)-sizeof(long), IPC_NOWAIT)==-1){
printf("erreur2 \n");
}
/* mesage 3 */
mn="texte3";
strcpy(msg.nom,mn);
msg.moy=14;
if (msgsnd(msgid, &msg,sizeof(msg)-sizeof(long), IPC_NOWAIT)==-1){
printf("erreur3 \n");
}
}
}
else { /* le fils pid1 */
msg.type=1;
/* mesage 1 */
mn="Serie1";
strcpy(msg.nom,mn);
msg.moy=5;
if (msgsnd(msgid, &msg,sizeof(msg)-sizeof(long), IPC_NOWAIT)==-1){
printf("erreur4 \n");
}
/* mesage 2*/
mn="Serie2";
strcpy(msg.nom,mn);
msg.moy=6;
if (msgsnd(msgid, &msg,sizeof(msg)-sizeof(long), IPC_NOWAIT)==-1){
printf("erreur5 \n");
}
/* mesage 3 */
mn="Serie3";
strcpy(msg.nom,mn);
msg.moy=7;
if (msgsnd(msgid, &msg,sizeof(msg)-sizeof(long), IPC_NOWAIT)==-1){
printf("erreur6 \n");
}
}
}
41