je souhaite créer 3 processus fils qui affiche ce que le père leur envoie.
Le père récupère le numéro du fils saisie au clavier par l'utilisateur et envoie "fils numero ... (numero du fils), Je suis ton pere" au fils choisi par l'utilisateur.
Programme :
Je crée 3 tubes anonyme !
Je crée 3 fils avec une boucle for()
Je teste dans cette boucle si le retour de fork()
- Si retour == 0 :
/*dans ce cas je suis dans le fils */
- Si retour > 0 :
/*dans ce cas je suis dans le père*/
Fin boucle for
Mes problèmes :
Dans le code des fils le read (qui permet de lire ce que lui envoie le pere) est bloquant. Si je debloque le read() le processus fils continue à s'executer, or il ne fait qu'un printf et ensuite il fait un exit(EXIT_SUCCESS) et donc il s'arrête.
Dans le code père si je fais un scanf (permettant de lire le numero que l'utilisateur a entré), le scanf sera executé à chaque fois qu'un fils sera crée, or moi je veux que le père discute avec ces fils uniquement quand tous les fils sont créés...
Dans le code des fils le read (qui permet de lire ce que lui envoie le pere) est bloquant. Si je debloque le read() le processus fils continue à s'executer, or il ne fait qu'un printf et ensuite il fait un exit(EXIT_SUCCESS) et donc il s'arrête.
Si le processus fils doit faire une chose plusieurs fois (attendre l'arrivée d'un truc dans le tuyau et afficher), ça serait logique qu'il exécute une boucle.
- Edité par michelbillaud 28 janvier 2021 à 12:10:02
Ton problème se traite avec un select (chiant) ou poll (propre). Les fils écrivent/lisent sur leur descripteur, mais le père attend que un des trois a écrit. C'est exactement à ça que sert poll.
Pour faire simple : je schématise, il faut compléter.
struct pollfd children[3];
// en imaginant que la fonction birth créé un pipe et fasse un fork :
children[0].fd = birth();
children[1].fd = birth();
children[2].fd = birth();
// ensuite on indique qu'on souhaite attendre pour de la lecture .
children[0].events = children[1].events = children[2].events = POLLIN;
// tu fais une condition jusqu'à ce que les enfants soient terminés.
while (!jesus_returns) {
poll(children, 3, -1); // il faudrait vérifier le code de retour aussi.
if (children[0].revents & POLLIN)
// tu peux faire un read sur children[0].fd sans bloquer
// tester 1 et 2 aussi...
}
- Edité par markand 28 janvier 2021 à 13:23:20
git is great because Linus did it, mercurial is better because he didn't.
Je vous montre tout d'abord mon comment je crée mes tubes.
En réalité je fais un malloc car le nombre de fils choisis par l'utilisateur est passé en paramètre lors de l'exécution. Ainsi on ne sait pas combien de fils l'utilisateur souhaitent créés (Dans l'exemple de post j'ai dit "3 fils" pour simplifier votre compréhension).
J'ai donc créé une fonction qui fait un malloc et qui renvoie un pointeur (int**) car la première dimension correspond aux nombres de fils et la 2ème dimensions correspond au entrée lecture et écriture. En sachant que l'entrée lecture vaut "0" et l'entrée écriture vaut "1".
J'ai nommé mon pointeur pour chaque tube "pointeurPipe"
Par exemple, si le fils numéro 3 (2 en réalité car ca commence à 0) souhaite faire un read(), il devra utiliser son tube qui lui est dédié, soit le tube numéro 2, donc son file descriptor est pointeurPipe[2][0] ( c'est à dire, 2 car c'est le troisième fils et 0 car il se connecte à l'entrée de lecture).
int** creation_pipe(int nb_pipe){
int i,j;
int ** pointeurPipe;
/* initialisation de n pipe */
if( (pointeurPipe = (int**) malloc( nb_pipe * sizeof(int*) )) == NULL){
fprintf(stderr,"Erreur de malloc");
exit(EXIT_FAILURE);
}
/* Création des 2 entrées pour chaque pipe */
for( i=0 ; i < nb_pipe ; i++){
*(pointeurPipe + i) = (int*) malloc ( 2 * sizeof(int) );
}
/* Création des lecture et écrite pour chaque pipe */
/* pointeurPipe[i][0]
/* pointeurPipe[i][1]*/
for( i=0 ; i < nb_pipe ; i ++){
for( j=0 ; j < 2 ; j++ ){
pointeurPipe[i][j] = j;
}
}
return pointeurPipe;
}
A présent, je vous montre le code dédié à la création des fils :
for( i=0 ; i < nb_fils ; i++){
/* Création d'un fils */
if( (pid = fork()) < 0 ){
fprintf(stderr, "Erreur lors de la création du fils %d\n", i );
perror("Erreur : ");
exit(EXIT_FAILURE);
}
if( pid == 0 ){
/* on est dans le fils */
fils(i, nb_fils, pointeurPipe);
}
if( pid > 0) {
/* on est dans le père */
/* update pid, je récupère le pid
des fils créé et les met dans un tableau */
update_tab_pid(tab_pid,i,pid);
/* Le père attend vérifie la fin de chaque fils */
if( (waitpid(pid ,&statut, 0)) != pid) { perror("Erreur lors de l'attente du fils "); exit(EXIT_FAILURE);}
if( WIFEXITED(statut) )
printf("PERE : Le fils %d a termine ; valeur retournee = %d ; son PID : %d\n\n", i+1,WEXITSTATUS(statut), pid);
else
printf("Le fils %d a termine anormalement.\n", i+1);
}
}
Puis voici la fonction dédié à chaque fils, chaque doit fils après sa création attendre un message venant du père puis affiche le message et s'arrête.
Voici pour le moment mon code qui n'est pas fonctionnel, j'ai réussi à rendre le read() non bloquant grâce à fcntl() mais maintenant c'est ma boucle do while() qui empêche la création des autres fils, ici seul le premier fils est créé.
void fils(int num_fils, int nb_fils, int **pointeurPipe){
int attributs, tmp, res;
/* FCNTL : pour rendre le read non bloquant */
attributs = fcntl(pointeurPipe[num_fils][TUBE_LECTURE], F_GETFL);
fcntl(pointeurPipe[num_fils][TUBE_LECTURE], F_SETFL, attributs | O_NONBLOCK);
printf("\nFILS : Je suis le fils n° %d \n", (num_fils+1) );
do{
res = read(pointeurPipe[num_fils][TUBE_LECTURE], &tmp, sizeof(int));
if(res != -1){
printf("J'ai rien recu encore...\n");
sleep(1);
}
}while((res == -1) && (errno == EAGAIN));
printf("J'ai recu la valeur %d provenant du père \n", tmp);
exit(EXIT_SUCCESS);
}
un read est bloquant, du coup si je le rend PAS non bloquant, tout mon programme se bloque lors du read() du premier fils...
Pour les descripteurs, étant donné que je créé mais N tubes (avec N = nombre de fils) AVANT de créer les fils avec fork(), les N tubes sont dupliqués à chaque fils créés, et donc les N fils ont accès au N tubes.
Je sais qu'il faut fermer l'écriture de tous les tubes pour tous les fils car les fils n'écrivent pas, ils font que lire les messages du père. Du coup j'avais pensé à faire :
for( j=0 ; j < nombre_fils ; j++){
// on ferme le tube en écriture
// c'est l'indice "1"
close( pointeurPipe[j][1]);
}
Je sais aussi que si ma théorie expliqué ci dessus avec la duplication est vraie. Il faut fermé toutes les entrées lecture de tous les tubes sauf celle du tube du fils courant.
for( j=0 ; j < nombre_fils ; j++){
// on ferme le tube en lecture
// c'est l'indice "0"
// On ferme seulement les entrées lecture
// Des autres tubes pas celle du fils courant
if( j != numero_fils )
close( pointeurPipe[j][0]);
}
un read est bloquant, du coup si je le rend PAS non bloquant, tout mon programme se bloque lors du read() du premier fils...
Non. Seul le fils se bloque lui même. Les autres tournent à leur rythmes. La seule chose qui sera bloquante au niveau du processus parent c'est au moment de communiquer à du parent au fils (en écriture) si le parent n'a pas défini le descripteur en non bloquant. Le fils doit rester bloquant, c'est justement lui qui attend d'avoir du contenu pour travailler.
Si ton programme se fige parce qu'un des fils fait un read alors tu as mal codé quelque chose. C'est justement le principe de faire des fork, ne plus rien bloquer et laisser tourner des processus. C'est comme ça que sont implémentés openssh, sysvinit, etc.
- Edité par markand 28 janvier 2021 à 16:48:42
git is great because Linus did it, mercurial is better because he didn't.
Voila un exemple avec UN processus fils (je ne suis pas là pour faire les exercices)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
void action_pere(int out);
void action_fils(int in);
int main() {
int tuyau[2]; // permet au père d'écrire au fils.
pipe(tuyau); // écrire dans tuyau[1], lire dans tuyau[0]
pid_t pid_fils = fork();
if (pid_fils) {
// code du fils
close(tuyau[1]); // le fils ferme le bout qui sert à écrire dans le tuyau
action_fils(tuyau[0]);
exit(0);
}
// code du père
close(tuyau[0]); // le père ne lit pas dans le tuyau
action_pere(tuyau[1]);
kill(pid_fils, SIGTERM);
return 0;
}
void action_pere(int out) {
sleep(1);
write(out, "pim", 4);
sleep(1);
write(out, "pam", 4);
sleep(1);
write(out, "poum", 5);
sleep(1);
}
void action_fils(int in) {
char chaine[20];
for(;;) {
read (in, chaine, 20);
printf("* Le fils a lu : %s\n", chaine);
}
};
PS: le problème avec les cours de soi-disant "programmation système", c'est souvent que les exemples d'un intéret douteux sont programmés avec les pieds. Pas de découpage en fonctions, noms de variables affreux, etc.
PS2: en fait, la programmation système, c'est quand on écrit du logiciel qui n'est pas un programme "d'application", mais des utilitaires et bibliothèques qui servent à d'autres programmes. Ici ça n'a rien de plus "systeme" qu'un banal programme multi-threads en Java ou Python.
- Edité par michelbillaud 28 janvier 2021 à 17:10:36
void fils(int num_fils, int nb_fils, int **pointeurPipe){
int attributs, tmp, res;
/* FCNTL : pour rendre le read non bloquant */
attributs = fcntl(pointeurPipe[num_fils][TUBE_LECTURE], F_GETFL);
fcntl(pointeurPipe[num_fils][TUBE_LECTURE], F_SETFL, attributs | O_NONBLOCK);
printf("\nFILS : Je suis le fils n° %d \n", (num_fils+1) );
read(pointeurPipe[num_fils][TUBE_LECTURE], &tmp, sizeof(int));
printf("J'ai recu la valeur %d provenant du père \n", tmp);
exit(EXIT_SUCCESS);
}
Le programme fonctionne normalement, il ne se bloque pas car j'ai fait appel à fcntl. voici ce que j'obtiens lorsque je crée 2 fils :
Lorsque je supprime la partie de fcntl,
void fils(int num_fils, int nb_fils, int **pointeurPipe){
int tmp;
printf("\nFILS : Je suis le fils n° %d \n", (num_fils+1) );
read(pointeurPipe[num_fils][TUBE_LECTURE], &tmp, sizeof(int));
printf("J'ai recu la valeur %d provenant du père \n", tmp);
exit(EXIT_SUCCESS);
}
Voici ce que j'obtiens :
Le fils 1 est bloqué au niveau de son read(), et le programme se bloque
michelbillaud a écrit:
Comment se fait il que tu exécutes le code fils en faisant "if( pid_fils ) "
J'ai appris qu'on doit exécuter le code fils uniquement en faisant la condition pid == 0 :
/* Création d'un fils */
if( (pid = fork()) < 0 ){
fprintf(stderr, "Erreur lors de la création du fils %d\n", i );
perror("Erreur : ");
exit(EXIT_FAILURE);
}
if( pid == 0 ){
/* on est dans le fils */
fils(i, nb_fils, pointeurPipe);
}
De plus,
j'ai appris que si le pid était supérieur à > 0 on était dans le père
if( pid > 0) {
/* on est dans le père */
}
Enfin le code qui était en dehors des 3 fils ci dessus, j'ai appris qu'il était exécuté par à la fois le fils et père...
int main() {
int tuyau[2]; // permet au père d'écrire au fils.
pipe(tuyau); // écrire dans tuyau[1], lire dans tuyau[0]
pid_t pid_fils = fork();
if (pid_fils) {
// code du fils
close(tuyau[1]); // le fils ferme le bout qui sert à écrire dans le tuyau
action_fils(tuyau[0]);
exit(0);
}
// code du père
close(tuyau[0]); // le père ne lit pas dans le tuyau
action_pere(tuyau[1]);
kill(pid_fils, SIGTERM);
return 0;
}
D'accord mais tu m'as dis 2 messages plus haut que ca bloque uniquement le processus qui fait le read(), or c'est le fils qui devrait se bloquer, pas tout le programme...
Je vous envoie mon code, ca sera peut être plus simple :
AH DESOLE JE N AVAIS PAS VU TON NOUVEAU POSTE !
Il est plutôt propre, il manque juste les close et le faire fonctionner avec le read()...
#include <stdio.h> /* Pour printf, fprintf, perror */
#include <unistd.h> /* Pour exit, EXIT_FAILURE, EXIT_SUCCESS et pipe*/
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h> /* Pour pid_t */
#include <sys/wait.h> /* Pour wait */
#include <stdlib.h> /* Pour fork et malloc */
#define TUBE_LECTURE 0
#define TUBE_ECRTURE 1
int** creation_pipe(int nb_pipe);
void fermeture_pipe(int **pointeurPipe,int nb_pipe);
void fils(int num_fils, int nb_fils, int **pointeurPipe);
int main(int argc, char *argv[]){
int i;
pid_t pid;
int statut;
int nb_fils;
int** pointeurPipe=NULL;
pid_t* tab_pid=NULL;
/* Vérification arguments commande */
if( argc != 2 ){
fprintf(stderr, "Usage: %s n\n", argv[0]);
fprintf(stderr, "\tOu:\n");
fprintf(stderr, "\t\t n : nombre de processus fils à créer entre 1 et 5\n");
exit(EXIT_FAILURE);
}
if( ( atoi(argv[1]) < 1 ) || ( atoi(argv[1]) > 5 ) ){
fprintf(stderr, "\t\t n : nombre de processus fils à créer entre 1 et 5\n");
exit(EXIT_FAILURE);
}
/* Recuperation de l'argument nombre de fils */
nb_fils = atoi(argv[1]);
/* Création de n pipe */
pointeurPipe = creation_pipe(nb_fils);
/* Création d'un tableau de n pid */
tab_pid = creation_tab_pid(nb_fils);
for( i=0 ; i < nb_fils ; i++){
/* Création d'un fils */
if( (pid = fork()) < 0 ){
fprintf(stderr, "Erreur lors de la création du fils %d\n", i );
perror("Erreur : ");
exit(EXIT_FAILURE);
}
/* Programme du fils */
if( pid == 0 )
fils(i, nb_fils, pointeurPipe);
/* Programme père */
if( pid > 0) {
/* Verification de l'extinction de fils */
if( (waitpid(pid ,&statut, 0)) != pid) { perror("Erreur lors de l'attente du fils "); exit(EXIT_FAILURE);}
if( WIFEXITED(statut) )
printf("PERE : Le fils %d a termine ; valeur retournee = %d ; son PID : %d\n\n", i+1,WEXITSTATUS(statut), pid);
else
printf("Le fils %d a termine anormalement.\n", i+1);
}
}
fermeture_pipe(pointeurPipe, atoi(argv[1]));
free(pointeurPipe);
pointeurPipe=NULL;
}
void fils(int num_fils, int nb_fils, int **pointeurPipe){
int attributs, tmp, res;
/* FCNTL : pour rendre le read non bloquant */
/*attributs = fcntl(pointeurPipe[num_fils][TUBE_LECTURE], F_GETFL);
fcntl(pointeurPipe[num_fils][TUBE_LECTURE], F_SETFL, attributs | O_NONBLOCK);*/
printf("\nFILS : Je suis le fils n° %d \n", (num_fils+1) );
if( (read(pointeurPipe[num_fils][TUBE_LECTURE], &tmp, sizeof(int))) == -1 ){
perror("Erreur read ");
exit(EXIT_FAILURE);
}
printf("J'ai recu la valeur %d provenant du père \n", tmp);
exit(EXIT_SUCCESS);
}
int** creation_pipe(int nb_pipe){
int i,j;
int ** pointeurPipe;
/* initialisation de n pipe */
if( (pointeurPipe = (int**) malloc( nb_pipe * sizeof(int*) )) == NULL){
fprintf(stderr,"Erreur de malloc");
exit(EXIT_FAILURE);
}
/* Création des 2 entrées pour chaque pipe */
for( i=0 ; i < nb_pipe ; i++){
*(pointeurPipe + i) = (int*) malloc ( 2 * sizeof(int) );
}
/* Création des lecture et écrite pour chaque pipe */
/* pointeurPipe[i][0]
/* pointeurPipe[i][1]*/
for( i=0 ; i < nb_pipe ; i ++){
for( j=0 ; j < 2 ; j++ ){
pointeurPipe[i][j] = j;
}
}
return pointeurPipe;
}
void fermeture_pipe(int **pointeurPipe,int nb_pipe){
int i;
/* Liberation des n pipes */
for ( i=0 ; i < nb_pipe ; i++){
free(*(pointeurPipe + i));
*(pointeurPipe + i) =NULL;
}
}
Qu'est-ce qui te fait dire que TOUT le programme se bloque ?
Visiblement, il n'y a rien qui marche, parce que les read et les write se font dans des descripteurs non initialisés, probablement parce que t'embrouiller dans les malloc (avec les complications de tableau 2D) t'a fait oublier qu'ensuite il fallait ensuite appeler pipe() pour créer les tuyaux après avoir réservé de la place pour les descripteurs.
- Edité par michelbillaud 28 janvier 2021 à 17:41:32
Pourquoi je crois qu'il bloque tout mon programme...? Car si les autres fils se créeraient, je verrai à l'écran FILS n°2, n°3 ...
Or là je vois :
Je suis repartis sur de meilleurs base, le read() bloque toujours mon programme.
#include <stdio.h> /* Pour printf, fprintf, perror */
#include <unistd.h> /* Pour exit, EXIT_FAILURE, EXIT_SUCCESS et pipe*/
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h> /* Pour pid_t */
#include <sys/wait.h> /* Pour wait */
#include <stdlib.h> /* Pour fork et malloc */
#define TUBE_LECTURE 0
#define TUBE_ECRTURE 1
void fils(int num_fils, int nb_fils, int * tube){
int tmp;
printf("\nFILS : Je suis le fils n° %d \n", (num_fils+1) );
if( (read( tube[ 2 * nb_fils ] , &tmp, sizeof(int))) == -1 ){
perror("Erreur read ");
exit(EXIT_FAILURE);
}
printf("J'ai recu la valeur %d provenant du père \n", tmp);
exit(EXIT_SUCCESS);
}
int main(int argc, char *argv[]){
int i;
pid_t pid;
int statut;
int nb_fils;
int *tube=NULL;
/* Vérification arguments commande */
if( argc != 2 ){
fprintf(stderr, "Usage: %s n\n", argv[0]);
fprintf(stderr, "\tOu:\n");
fprintf(stderr, "\t\t n : nombre de processus fils à créer entre 1 et 5\n");
exit(EXIT_FAILURE);
}
if( ( atoi(argv[1]) < 1 ) || ( atoi(argv[1]) > 5 ) ){
fprintf(stderr, "\t\t n : nombre de processus fils à créer entre 1 et 5\n");
exit(EXIT_FAILURE);
}
/* Recuperation de l'argument nombre de fils */
nb_fils = atoi(argv[1]);
/* Initialisation nombre tube */
tube = (int*) malloc( 2 * nb_fils * sizeof(int));
for ( i=0 ; i < nb_fils ; i++ ) {
pipe( tube + 2 * i );
}
for( i=0 ; i < nb_fils ; i++){
/* Création d'un fils */
if( (pid = fork()) < 0 ){
fprintf(stderr, "Erreur lors de la création du fils %d\n", i );
perror("Erreur : ");
exit(EXIT_FAILURE);
}
if( pid == 0 ){
/* on est dans le fils */
fils(i, nb_fils, tube);
}
if( pid > 0) {
/* on est dans le père */
if( (waitpid(pid ,&statut, 0)) != pid) { perror("Erreur lors de l'attente du fils "); exit(EXIT_FAILURE);}
if( WIFEXITED(statut) )
printf("PERE : Le fils %d a termine ; valeur retournee = %d ; son PID : %d\n\n", i+1,WEXITSTATUS(statut), pid);
else
printf("Le fils %d a termine anormalement.\n", i+1);
}
}
free(tube);
tube=NULL;
}
Des problèmes de tyauterie?Voici un lien utile:
http://www.zeitoun.net/articles/communication-par-tuyau/start
Ici, je pense que c'est le père qui doit gérer les numéros de pipe (ou fd) de ses fils.
Chaque fils lit sur son fd=0 et écrit sur son fd=1 ou fd=2
Le père aura des fd=3,4 5,6, 7,8 etc.
Je n'ai pas vu de dup() ou dup2() qui me semble essentiel pour ces manipulartions.
Je pense que l'utilisateur n'a pas besoin de connaître le pid des fils. Un simple numéro de 1 à N suffit.
C'est le père qui doit connaître les pid de ses fils.
À chaque fils dans le tableau, le père aura le pid' le fd pour écrire, le fd pour lire.
Si rien n'est bloqué de l'extérieur, je ne vois pas pourquoi l'écriture du père vers ses fils serait bloquant.
Les fils se mettent dans une boucle avec un read qui est bloquant pour eux mais pas pour le père.
De même la lecture du père sur un fils ne sera bloquant que si le fils ne réagit pas (bug possible ...)
Le Tout est souvent plus grand que la somme de ses parties.
Chaque fils lit sur son fd=0 et écrit sur son fd=1 ou fd=2
Je crois que tu confonds avec le lancement de programmes par les fonctions de la famille exec.
Les programmes utilisent les descripteurs 0 1 et 2 pour l'entrée standard etc. et il faut effectivement rediriger les tuyaux pour connecter l'entrée de l'un avec la sortie de l'autre.
Il y a eu de l'amélioration mais ce n'est pas fonctionnel à 100%. En effet, le père crée les fils puis récupère leur PID à chaque création et les met dans un tableaux.
Ensuite, le père demande à l'utilisateur d'entrer le numéro du fils à sélectionner pour envoyer un message. Puis il fait un write(). Le fils sélectionner reçoit l'information et affiche un message, puis il s'arrête. Enfin le père vérifie l'arrêt de chaque fils. (Pour l'instant je n'ai pas gerer l'arrêt des fils qui ne reçoit pas de message, eux ils sont encore attente, bloquer avec leur read() ).
Cependant je souhaite envoyer au fils la valeur de la variable tmp soit "10". Toutefois, le fils affiche une valeur aléatoire...
#include <stdio.h> /* Pour printf, fprintf, perror */
#include <unistd.h> /* Pour exit, EXIT_FAILURE, EXIT_SUCCESS et pipe*/
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h> /* Pour pid_t */
#include <sys/wait.h> /* Pour wait */
#include <stdlib.h> /* Pour fork et malloc */
void fils(int num_fils, int nb_fils, int *tube){
int tmp=-2;
printf("\nFILS : Je suis le fils n° %d \n", (num_fils+1) );
if( (read( tube[ 2 * nb_fils ] , &tmp, sizeof(int))) == -1 ){
perror("Erreur read ");
exit(EXIT_FAILURE);
}
printf("J'ai recu la valeur %d provenant du père \n", tmp);
/*On ferme l'entrée lecture pour le fils */
close( tube[ 2*num_fils ]);
exit(EXIT_SUCCESS);
}
int main(int argc, char *argv[]){
int i;
pid_t pid;
int statut;
int tmp = 10;
int nb_fils;
int num_fils;
int *tab_pid=NULL;
int *tube=NULL;
/* Vérification arguments commande */
if( argc != 2 ){
fprintf(stderr, "Usage: %s n\n", argv[0]);
fprintf(stderr, "\tOu:\n");
fprintf(stderr, "\t\t n : nombre de processus fils à créer entre 1 et 5\n");
exit(EXIT_FAILURE);
}
if( ( atoi(argv[1]) < 1 ) || ( atoi(argv[1]) > 5 ) ){
fprintf(stderr, "\t\t n : nombre de processus fils à créer entre 1 et 5\n");
exit(EXIT_FAILURE);
}
/* Recuperation de l'argument nombre de fils */
nb_fils = atoi(argv[1]);
/* Initialisation nombre tube */
tube = (int*) malloc( 2 * nb_fils * sizeof(int));
if( tube == NULL){
fprintf(stderr, "Erreur malloc tube");
exit(EXIT_FAILURE);
}
for ( i=0 ; i < nb_fils ; i++ ) {
pipe( &tube[2*i] );
}
/* Creation d'un tableau de N pid */
tab_pid = (int*) malloc( nb_fils * sizeof(int));
if( tab_pid == NULL){
fprintf(stderr, "Erreur malloc tab_pid");
exit(EXIT_FAILURE);
}
/* On ferme les N tubes (entrées lecture) pour le père */
for( i=0 ; i < nb_fils ; i++){ close( tube[ 2 * i ] ); }
for( i=0 ; i < nb_fils ; i++){
/* Création d'un fils */
if( (pid = fork()) < 0 ){
fprintf(stderr, "Erreur lors de la création du fils %d\n", i );
perror("Erreur : ");
exit(EXIT_FAILURE);
}
/* on est dans le fils */
if( pid == 0 ){
/* On ferme l'entrée ecriture pour le fils */
close( tube[ (2 * i) + 1 ] );
fils(i, nb_fils, tube);
}
/* on est dans le père */
if( pid > 0) {
/* On recupere le pid de chaque fils */
tab_pid[i]=pid;
}
}
/* On attend la création de tous les fils */
sleep(1);
/*On demande à l'utilisateur le numero du fils*/
printf("\nTapez le numero du fils à selectionner : ");
scanf("%d",&num_fils);
/* Ecriture dans le tube choisi par l'utilisateur de l'entier "10" (pour test) */
if( (write( tube[ (2 * (num_fils-1)) + 1 ] , &tmp, sizeof(int))) == -1 ){
perror("Erreur write ");
exit(EXIT_FAILURE);
}
printf("\n Message envoyé au fils %d \n",num_fils);
/* On verifie la fin de chaque fils*/
for( i=0 ; i < nb_fils ; i++){
if( (waitpid(tab_pid[i] ,&statut, 0)) != tab_pid[i] ) { perror("Erreur lors de l'attente du fils "); exit(EXIT_FAILURE);}
if( WIFEXITED(statut) )
printf("PERE : Le fils %d a termine ; valeur retournee = %d ; son PID : %d\n\n", i+1,WEXITSTATUS(statut), tab_pid[i]);
else
printf("Le fils %d a termine anormalement.\n", i+1);
}
/* On ferme les N tubes (entrées ecriture) pour le père */
for( i=0 ; i < nb_fils ; i++){ close( tube[ (2 * i) + 1 ] ); }
free(tube);
tube=NULL;
}
Ligne 73 tu ferme un bout des tuyaux avant d'avoir créé les processus fils. Qui héritent donc de descripteurs fermés.
J'ai modifié mon code à partir de tes conseils. J'y suis presque mais je n'ai pas le résultat attendu...
Mon code, j'ai fermé le bout des tuyaux de lecture pour le père uniquement à chaque création de fils.
#include <stdio.h> /* Pour printf, fprintf, perror */
#include <unistd.h> /* Pour exit, EXIT_FAILURE, EXIT_SUCCESS et pipe*/
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h> /* Pour pid_t */
#include <sys/wait.h> /* Pour wait */
#include <stdlib.h> /* Pour fork et malloc */
void fils(int num_fils, int nb_fils, int *tube){
int tmp=-2;
printf("\nFILS : fils n° %d créé, son PID est %d\n", (num_fils+1), getpid() );
if( (read( tube[ 2 * nb_fils ] , &tmp, sizeof(int))) == -1 ){
perror("Erreur read ");
exit(EXIT_FAILURE);
}
printf("Je suis fils n° %d. J'ai recu la valeur %d provenant du père \n", (num_fils+1), tmp);
/*On ferme l'entrée lecture pour le fils */
close( tube[ 2*num_fils ]);
exit(EXIT_SUCCESS);
}
int main(int argc, char *argv[]){
int i;
pid_t pid;
int statut;
int tmp = 10;
int nb_fils;
int num_fils;
int *tab_pid=NULL;
int *tube=NULL;
/* Vérification arguments commande */
if( argc != 2 ){
fprintf(stderr, "Usage: %s n\n", argv[0]);
fprintf(stderr, "\tOu:\n");
fprintf(stderr, "\t\t n : nombre de processus fils à créer entre 1 et 5\n");
exit(EXIT_FAILURE);
}
if( ( atoi(argv[1]) < 1 ) || ( atoi(argv[1]) > 5 ) ){
fprintf(stderr, "\t\t n : nombre de processus fils à créer entre 1 et 5\n");
exit(EXIT_FAILURE);
}
/* Recuperation de l'argument nombre de fils */
nb_fils = atoi(argv[1]);
/* Initialisation nombre tube */
tube = (int*) malloc( 2 * nb_fils * sizeof(int));
if( tube == NULL){
fprintf(stderr, "Erreur malloc tube");
exit(EXIT_FAILURE);
}
for ( i=0 ; i < nb_fils ; i++ ) {
pipe( &tube[2*i] );
}
/* Creation d'un tableau de N pid */
tab_pid = (int*) malloc( nb_fils * sizeof(int));
if( tab_pid == NULL){
fprintf(stderr, "Erreur malloc tab_pid");
exit(EXIT_FAILURE);
}
for( i=0 ; i < nb_fils ; i++){
/* Création d'un fils */
if( (pid = fork()) < 0 ){
fprintf(stderr, "Erreur lors de la création du fils %d\n", i );
perror("Erreur : ");
exit(EXIT_FAILURE);
}
/* on est dans le fils */
if( pid == 0 ){
/* On ferme l'entrée ecriture pour le fils */
close( tube[ (2 * i) + 1 ] );
fils(i, nb_fils, tube);
}
/* on est dans le père */
if( pid > 0) {
/* On ferme les N tubes (entrées lecture) pour le père */
//close( tube[ 2 * i ] );
/* On recupere le pid de chaque fils */
tab_pid[i]=pid;
}
}
/* On attend la création de tous les fils */
sleep(1);
/*On demande à l'utilisateur le numero du fils*/
printf("\nTapez le numero du fils à selectionner : ");
scanf("%d",&num_fils);
/* Ecriture dans le tube choisi par l'utilisateur de l'entier "10" (pour test) */
if( (write( tube[ (2 * (num_fils-1)) + 1 ] , &tmp, sizeof(int))) == -1 ){
perror("Erreur write ");
exit(EXIT_FAILURE);
}
printf("\nMessage envoyé au fils n°%d \n",num_fils);
/* On verifie la fin de chaque fils*/
for( i=0 ; i < nb_fils ; i++){
if( (waitpid(tab_pid[i] ,&statut, 0)) != tab_pid[i] ) { perror("Erreur lors de l'attente du fils "); exit(EXIT_FAILURE);}
if( WIFEXITED(statut) )
printf("PERE : Le fils %d a termine ; valeur retournee = %d ; son PID : %d\n\n", i+1,WEXITSTATUS(statut), tab_pid[i]);
else
printf("Le fils %d a termine anormalement.\n", i+1);
}
/* On ferme les N tubes (entrées ecriture) pour le père */
for( i=0 ; i < nb_fils ; i++){ close( tube[ (2 * i) + 1 ] ); }
free(tube);
tube=NULL;
}
Un problème avec un numéro de descripteur incorrect ?
Look
void fils(int num_fils, int nb_fils, int *tube){
read( tube[ 2 * nb_fils ] , ..., ....);
> J'ai modifié mon code à partir de tes conseils.
Dans le genre "et c'est donc à cause de tes conseils à la noix que ça ne marche pas" :-)
hum, en tout cas pas celui-là :
Il ne faut passer à la fonction "fils"
que les informations qui lui sont absolument nécessaires.
Ca évitera les accidents comme celui-ci
[...]
Les accidents se reproduisent, pour les mêmes raisons. Le processus fils a besoin de SON numéro, et d'UN (seulement un, ONLY ONE, NUR EINS) descripteur.
- Edité par michelbillaud 29 janvier 2021 à 12:44:42
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
git is great because Linus did it, mercurial is better because he didn't.
git is great because Linus did it, mercurial is better because he didn't.
git is great because Linus did it, mercurial is better because he didn't.
Le Tout est souvent plus grand que la somme de ses parties.
Le Tout est souvent plus grand que la somme de ses parties.