Exercice 1 : Convertir en majuscules avec un tube
Code
c
Copier le code
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main() {
pid_t retour;
int tube[2];
char buffer[81], converted[81];
if (pipe(tube) == -1) {
perror("Création du tube impossible\n");
exit(1);
}
switch (retour = fork()) {
case -1:
perror("Création impossible");
exit(1);
case 0: // Fils
printf("Processus fils\n");
close(tube[1]); // Fermer le côté écriture du tube
while (read(tube[0], buffer, sizeof(buffer)) > 0) {
for (int i = 0; buffer[i] != '\0'; i++) {
converted[i] = toupper(buffer[i]);
}
printf("Fils : %s\n", converted);
}
close(tube[0]); // Fermer le côté lecture du tube
exit(0);
default: // Père
printf("Père : a créé le processus %d\n", retour);
close(tube[0]); // Fermer le côté lecture du tube
while (1) {
printf("Père : Entrez une chaîne (STOP pour terminer) : ");
fgets(buffer, sizeof(buffer), stdin);
buffer[strcspn(buffer, "\n")] = '\0'; // Supprimer le \n
if (strcmp(buffer, "STOP") == 0) {
break;
}
write(tube[1], buffer, strlen(buffer) + 1);
}
close(tube[1]); // Fermer le côté écriture du tube
wait(NULL); // Attendre la fin du fils
printf("Père : le fils a terminé\n");
}
return 0;
}
Explications
1. Création du tube
c
Copier le code
if (pipe(tube) == -1) {
perror("Création du tube impossible\n");
exit(1);
}
pipe() crée un canal unidirectionnel de communication entre deux processus.
tube[0] : côté lecture du tube.
tube[1] : côté écriture du tube.
Si le tube ne peut pas être créé, le programme affiche une erreur et se termine.
2. Création du processus fils
c
Copier le code
switch (retour = fork()) {
case -1:
perror("Création impossible");
exit(1);
fork() crée un processus fils.
Retourne -1 si la création échoue (erreur fatale).
Retourne 0 dans le fils.
Retourne un identifiant positif dans le père.
3. Comportement du processus fils
c
Copier le code
case 0: // Fils
printf("Processus fils\n");
close(tube[1]); // Fermer le côté écriture du tube
Le fils n'a pas besoin d'écrire dans le tube, donc on ferme tube[1].
Il lit les données envoyées par le père depuis le tube (tube[0]).
c
Copier le code
while (read(tube[0], buffer, sizeof(buffer)) > 0) {
for (int i = 0; buffer[i] != '\0'; i++) {
converted[i] = toupper(buffer[i]);
}
printf("Fils : %s\n", converted);
}
Le fils utilise read() pour récupérer les données transmises par le père.
Il transforme chaque caractère en majuscule avec toupper() et affiche la chaîne
convertie.
c
Copier le code
close(tube[0]); // Fermer le côté lecture du tube
exit(0);
Une fois toutes les données lues, le fils ferme le tube et se termine.
4. Comportement du processus père
c
Copier le code
default: // Père
printf("Père : a créé le processus %d\n", retour);
close(tube[0]); // Fermer le côté lecture du tube
Le père n'a pas besoin de lire dans le tube, donc il ferme tube[0].
c
Copier le code
while (1) {
printf("Père : Entrez une chaîne (STOP pour terminer) : ");
fgets(buffer, sizeof(buffer), stdin);
buffer[strcspn(buffer, "\n")] = '\0'; // Supprimer le \n
if (strcmp(buffer, "STOP") == 0) {
break;
}
write(tube[1], buffer, strlen(buffer) + 1);
}
Le père lit les chaînes de caractères saisies par l'utilisateur.
Si l'utilisateur entre "STOP", la boucle s'arrête.
Sinon, la chaîne est écrite dans le tube avec write().
c
Copier le code
close(tube[1]); // Fermer le côté écriture du tube
wait(NULL); // Attendre la fin du fils
printf("Père : le fils a terminé\n");
Une fois les données transmises, le père ferme le tube et attend la fin du fils
avec wait().
Résumé du fonctionnement
Création du tube : Permet de transmettre les données entre les processus.
Création du fils : Le fils lit les données du tube et les convertit en majuscules.
Communication : Le père saisit des chaînes, les envoie via le tube, et le fils les
traite.
Arrêt propre : Une fois que "STOP" est saisi, le père termine la communication et
attend la fin du fils.
Si vous souhaitez que je traite d'autres exercices dans ce format, dites-le-moi !
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$$
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$$$
Exercice 2 corrigé : Fin de saisie avec ^D
Code
c
Copier le code
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>
int main() {
pid_t retour;
int tube[2];
char buffer[2], tableau[100];
int index = 0;
if (pipe(tube) == -1) {
perror("Création du tube impossible\n");
exit(1);
}
switch (retour = fork()) {
case -1:
perror("Création du processus fils impossible\n");
exit(1);
case 0: // Fils
printf("Processus fils : démarrage\n");
close(tube[1]); // Fermer le côté écriture du tube
while (read(tube[0], buffer, 1) > 0) {
tableau[index++] = buffer[0];
}
tableau[index] = '\0'; // Terminer la chaîne
printf("Fils : caractères reçus = '%s'\n", tableau);
close(tube[0]); // Fermer le côté lecture
exit(0);
default: // Père
printf("Processus père : démarrage\n");
close(tube[0]); // Fermer le côté lecture
printf("Père : Entrez des caractères (CTRL+D pour terminer) :\n");
while (read(STDIN_FILENO, buffer, 1) > 0) { // Lire un caractère du
clavier
if (islower(buffer[0])) { // Si c'est une lettre minuscule
write(tube[1], buffer, 1); // Écrire dans le tube
}
}
close(tube[1]); // Fermer le côté écriture
wait(NULL); // Attendre la fin du fils
printf("Père : le fils a terminé\n");
}
return 0;
}
Structure globale du code
Création d’un tube :
Le tube est un mécanisme permettant une communication unidirectionnelle entre le
processus père et son fils.
pipe(tube) crée un tube avec deux descripteurs de fichiers :
tube[0] : utilisé pour lire les données.
tube[1] : utilisé pour écrire des données.
Création d’un processus fils :
fork() crée un processus fils.
La valeur de retour de fork() permet de distinguer le père (retourne l'ID du fils),
le fils (retourne 0) et les erreurs (retourne -1).
Fonctionnement du père et du fils :
Le père lit des caractères au clavier et envoie les lettres minuscules au fils via
le tube.
Le fils lit les données du tube, les stocke dans un tableau et les affiche.
Détails du code
Initialisation
c
Copier le code
if (pipe(tube) == -1) {
perror("Création du tube impossible\n");
exit(1);
}
pipe() crée le tube. Si une erreur survient, le programme affiche un message
d’erreur (perror()) et termine.
Création du processus fils
c
Copier le code
switch (retour = fork()) {
case -1:
perror("Création du processus fils impossible\n");
exit(1);
Si fork() échoue, le programme affiche un message d’erreur et termine.
Dans le processus fils
c
Copier le code
case 0: // Fils
printf("Processus fils : démarrage\n");
close(tube[1]); // Fermer le côté écriture du tube
while (read(tube[0], buffer, 1) > 0) {
tableau[index++] = buffer[0];
}
tableau[index] = '\0'; // Terminer la chaîne
printf("Fils : caractères reçus = '%s'\n", tableau);
close(tube[0]); // Fermer le côté lecture
exit(0);
Fermeture du côté inutilisé :
Le fils n’écrit pas dans le tube, il ferme donc tube[1].
Lecture des données :
read(tube[0], buffer, 1) lit un caractère à la fois depuis le tube.
Les caractères reçus sont ajoutés à un tableau tableau.
Fin de la lecture :
Quand le père ferme le tube ou qu’il n’y a plus de données, read() retourne 0, ce
qui termine la boucle.
Affichage des résultats :
Les caractères reçus sont affichés en une seule chaîne.
Fermeture des ressources :
Le fils ferme tube[0] avant de terminer.
Dans le processus père
c
Copier le code
default: // Père
printf("Processus père : démarrage\n");
close(tube[0]); // Fermer le côté lecture
printf("Père : Entrez des caractères (CTRL+D pour terminer) :\n");
while (read(STDIN_FILENO, buffer, 1) > 0) { // Lire un caractère du clavier
if (islower(buffer[0])) { // Si c'est une lettre minuscule
write(tube[1], buffer, 1); // Écrire dans le tube
}
}
close(tube[1]); // Fermer le côté écriture
wait(NULL); // Attendre la fin du fils
printf("Père : le fils a terminé\n");
Fermeture du côté inutilisé :
Le père n’a pas besoin de lire depuis le tube, il ferme donc tube[0].
Lecture de l’entrée utilisateur :
read(STDIN_FILENO, buffer, 1) lit un caractère à la fois depuis l’entrée standard
(clavier).
^D (CTRL+D) envoie un EOF (fin de fichier), ce qui fait que read() retourne 0 et
arrête la boucle.
Écriture dans le tube :
Si le caractère lu est une lettre minuscule (islower(buffer[0])), il est envoyé au
fils via write(tube[1], buffer, 1).
Fin de la communication :
Une fois la saisie terminée, le père ferme tube[1], ce qui indique au fils qu’il
n’y a plus de données.
Synchronisation :
Le père attend la fin du fils avec wait(NULL).
Message final :
Le père indique que le fils a terminé.
Résumé des échanges
Initialisation :
Un tube est créé, et le processus fils est lancé.
Interaction du père :
Le père lit les caractères au clavier.
Il envoie les lettres minuscules au fils via le tube.
Interaction du fils :
Le fils lit les données du tube, les stocke, et les affiche une fois la lecture
terminée.
Terminaison :
Lorsque l'utilisateur tape ^D, le père ferme le tube.
Le fils détecte la fermeture et termine.
Exemple d'exécution avec explications
Entrée utilisateur :
css
Copier le code
a
1
b
^D
Sortie :
less
Copier le code
Processus père : démarrage
Père : Entrez des caractères (CTRL+D pour terminer) :
Fils : caractères reçus = 'ab'
Père : le fils a terminé
Ce qui se passe :
Le père lit a, qui est une lettre minuscule, et l’envoie au fils via le tube.
Le père lit 1, qui n’est pas une lettre minuscule, donc il ne l’envoie pas.
Le père lit b, qui est une lettre minuscule, et l’envoie au fils.
Le père détecte EOF (^D) et termine.
Le fils collecte les caractères envoyés (a et b) et les affiche.
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$$$
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$$$
Code de l'exercice 3
c
Copier le code
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
pid_t filsNeg, filsPos;
int tubeNeg[2], tubePos[2]; // Tubes pour communication avec les deux fils
int sommeNeg = 0, sommePos = 0, entier;
char buffer[10];
// Création des tubes
if (pipe(tubeNeg) == -1 || pipe(tubePos) == -1) {
perror("Erreur lors de la création des tubes\n");
exit(1);
}
// Création du fils pour les nombres négatifs
if ((filsNeg = fork()) == 0) {
close(tubeNeg[1]); // Fermer l'écriture
while (read(tubeNeg[0], &entier, sizeof(int)) > 0) {
sommeNeg += entier; // Calculer la somme des nombres reçus
}
close(tubeNeg[0]); // Fermer la lecture
printf("Fils négatif : somme = %d\n", sommeNeg);
exit(sommeNeg); // Retourner la somme comme code de sortie
}
// Création du fils pour les nombres positifs
if ((filsPos = fork()) == 0) {
close(tubePos[1]); // Fermer l'écriture
while (read(tubePos[0], &entier, sizeof(int)) > 0) {
sommePos += entier; // Calculer la somme des nombres reçus
}
close(tubePos[0]); // Fermer la lecture
printf("Fils positif : somme = %d\n", sommePos);
exit(sommePos); // Retourner la somme comme code de sortie
}
// Dans le processus père
close(tubeNeg[0]); // Fermer la lecture des tubes
close(tubePos[0]);
printf("Entrez des entiers (0 pour terminer) :\n");
while (1) {
fgets(buffer, sizeof(buffer), stdin);
entier = atoi(buffer); // Convertir la chaîne en entier
if (entier == 0) break; // Arrêter si l'entrée est 0
if (entier < 0) {
write(tubeNeg[1], &entier, sizeof(int)); // Envoyer au fils négatif
} else {
write(tubePos[1], &entier, sizeof(int)); // Envoyer au fils positif
}
}
close(tubeNeg[1]); // Fermer l'écriture
close(tubePos[1]);
// Attendre les fils et récupérer leurs sommes
int statusNeg, statusPos;
waitpid(filsNeg, &statusNeg, 0);
waitpid(filsPos, &statusPos, 0);
if (WIFEXITED(statusNeg)) {
sommeNeg = WEXITSTATUS(statusNeg); // Récupérer le code de sortie
}
if (WIFEXITED(statusPos)) {
sommePos = WEXITSTATUS(statusPos); // Récupérer le code de sortie
}
printf("Père : somme des négatifs = %d\n", sommeNeg);
printf("Père : somme des positifs = %d\n", sommePos);
return 0;
}
Explications du code
Création des tubes :
Deux tubes (tubeNeg et tubePos) permettent la communication entre le père et les
fils :
tubeNeg pour les nombres négatifs.
tubePos pour les nombres positifs.
Création des fils :
Fils négatif :
Lit les nombres négatifs envoyés par le père.
Calcule leur somme.
Retourne la somme au père comme code de sortie.
Fils positif :
Fait la même chose pour les nombres positifs.
Dans le père :
Le père lit les entiers au clavier.
Il les envoie aux fils via les tubes en fonction de leur signe.
L'entrée 0 termine la saisie.
Fermeture des tubes :
Chaque processus ferme les côtés inutilisés des tubes pour éviter des conflits.
Récupération des résultats :
Le père attend la fin des fils (waitpid()).
Il récupère leurs codes de sortie (WEXITSTATUS) pour afficher les sommes.
Exemple d'exécution
Entrée utilisateur :
diff
Copier le code
-5
10
-15
20
0
Sortie :
java
Copier le code
Entrez des entiers (0 pour terminer) :
Fils négatif : somme = -20
Fils positif : somme = 30
Père : somme des négatifs = -20
Père : somme des positifs = 30
Explications supplémentaires
Gestion de 0 :
Le père termine la saisie dès que l'utilisateur entre 0.
Le père ferme les tubes, ce qui informe les fils qu’il n’y a plus de données à
lire.
Utilisation des tubes :
Les tubes sont utilisés pour envoyer les nombres entre les processus.
La communication est unidirectionnelle : le père écrit, les fils lisent.
Récupération des sommes :
Les fils retournent leurs sommes comme code de sortie via exit().
Le père récupère ces codes avec WEXITSTATUS().
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@2
EXERCICE3: AVEC SWITCH CASE:
Code avec switch-case
c
Copier le code
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
int tubeNeg[2], tubePos[2]; // Tubes pour la communication
int entier, sommeNeg = 0, sommePos = 0;
char buffer[10];
pid_t retour1, retour2;
// Création des tubes
if (pipe(tubeNeg) == -1 || pipe(tubePos) == -1) {
perror("Erreur lors de la création des tubes\n");
exit(1);
}
// Création du premier fils (gestion des nombres négatifs)
switch (retour1 = fork()) {
case -1:
perror("Erreur lors de la création du processus fils négatif\n");
exit(1);
case 0: // Fils négatif
close(tubeNeg[1]); // Fermer le côté écriture
printf("Fils négatif : démarrage\n");
while (read(tubeNeg[0], &entier, sizeof(int)) > 0) {
sommeNeg += entier; // Calculer la somme des nombres négatifs
}
close(tubeNeg[0]); // Fermer le côté lecture
printf("Fils négatif : somme = %d\n", sommeNeg);
exit(sommeNeg); // Retourner la somme comme code de sortie
}
// Création du second fils (gestion des nombres positifs)
switch (retour2 = fork()) {
case -1:
perror("Erreur lors de la création du processus fils positif\n");
exit(1);
case 0: // Fils positif
close(tubePos[1]); // Fermer le côté écriture
printf("Fils positif : démarrage\n");
while (read(tubePos[0], &entier, sizeof(int)) > 0) {
sommePos += entier; // Calculer la somme des nombres positifs
}
close(tubePos[0]); // Fermer le côté lecture
printf("Fils positif : somme = %d\n", sommePos);
exit(sommePos); // Retourner la somme comme code de sortie
}
// Processus père
printf("Père : démarrage\n");
close(tubeNeg[0]); // Fermer le côté lecture du tube négatif
close(tubePos[0]); // Fermer le côté lecture du tube positif
printf("Entrez des entiers (0 pour terminer) :\n");
while (1) {
fgets(buffer, sizeof(buffer), stdin);
entier = atoi(buffer); // Convertir la chaîne en entier
if (entier == 0) break; // Arrêter si l'entrée est 0
if (entier < 0) {
write(tubeNeg[1], &entier, sizeof(int)); // Envoyer au fils négatif
} else {
write(tubePos[1], &entier, sizeof(int)); // Envoyer au fils positif
}
}
// Fin de la saisie
close(tubeNeg[1]); // Fermer le côté écriture du tube négatif
close(tubePos[1]); // Fermer le côté écriture du tube positif
// Attente des fils
int statusNeg, statusPos;
waitpid(retour1, &statusNeg, 0); // Attendre le fils négatif
waitpid(retour2, &statusPos, 0); // Attendre le fils positif
// Récupérer les sommes
if (WIFEXITED(statusNeg)) {
sommeNeg = WEXITSTATUS(statusNeg); // Somme des négatifs
}
if (WIFEXITED(statusPos)) {
sommePos = WEXITSTATUS(statusPos); // Somme des positifs
}
// Afficher les résultats
printf("Père : somme des négatifs = %d\n", sommeNeg);
printf("Père : somme des positifs = %d\n", sommePos);
return 0;
}
Explications des modifications
Organisation en switch-case :
Chaque processus (fils négatif, fils positif, père) est clairement séparé à l’aide
de switch-case, ce qui améliore la lisibilité et correspond au format demandé.
Dans le fils négatif :
Le fils négatif gère uniquement les nombres négatifs.
Il calcule leur somme et retourne ce résultat au père via exit(sommeNeg).
Dans le fils positif :
Le fils positif gère uniquement les nombres positifs.
Il calcule leur somme et retourne ce résultat au père via exit(sommePos).
Dans le père :
Le père lit les entiers au clavier et envoie les nombres négatifs au fils négatif
et les positifs au fils positif.
Il récupère les sommes en attendant la terminaison des fils.
Gestion des tubes :
Les tubes inutilisés (lecture dans le père, écriture dans les fils) sont fermés
immédiatement après la création des processus.
Exemple d’exécution
Entrée utilisateur :
diff
Copier le code
-10
5
-20
15
0
Sortie :
java
Copier le code
Fils négatif : démarrage
Fils positif : démarrage
Entrez des entiers (0 pour terminer) :
Fils négatif : somme = -30
Fils positif : somme = 20
Père : somme des négatifs = -30
Père : somme des positifs = 20
Résumé du fonctionnement
Initialisation :
Deux tubes sont créés pour la communication entre le père et chaque fils.
Lecture et envoi des entiers :
Le père lit les entiers et les transmet au fils approprié en fonction de leur
signe.
Calcul dans les fils :
Chaque fils calcule la somme des nombres qu’il reçoit et retourne cette somme au
père.
Récupération des résultats :
Le père récupère les sommes des fils via leur code de sortie (WEXITSTATUS).
Affichage des résultats :
Le père affiche les sommes calculées par les fils.