Deux enfants du même parent ne communiquent pas par canal si le parent n’appelle pas wait ()

Veuillez consulter le code ci-dessous:

#include main(){ int pid, fds[2], pid1; char buf[200]; pipe(fds); pid = fork(); if(pid==0) { close(fds[0]); scanf("%s", &buf); write(fds[1], buf, sizeof(buf)+1); } else { pid1 = fork(); if(pid1==0) { close(fds[1]); read(fds[0], buf, sizeof(buf)+1); printf("%s\n", buf); } else { Line1: wait(); } } } 

Si je ne commente pas Line1, cela fonctionne bien. S’il vous plaît voir ci-dessous:

 hduser@pc4:~/codes/c/os$ ./a.out hello //*Entry from keyboard* hello //Output hduser@pc4:~/codes/c/os$ 

Mais si je commente Line1, deux processus enfants ne communiquent pas:

 hduser@pc4:~/codes/c/os$ ./a.out hduser@pc4:~/codes/c/os$ hi //*Entry from keyboard* hi: command not found hduser@pc4:~/codes/c/os$ 

Je ne peux pas comprendre la signification de wait () ici.

Ce qui se passe ici, c’est que le processus parent termine son exécution avant les processus enfants. Causer les enfants à perdre l’access au terminal.

Laissez-nous regarder de plus près tout cela.

Qu’est-ce que wait() fait?

L’appel système wait () suspend l’exécution du processus d’appel jusqu’à ce que l’un de ses enfants se termine.

Votre programme est comme ça

Votre main Process composé de 2 processus enfants. Le premier écrit dans un tuyau tandis que le second lit dans un tuyau. Tout cela se produit pendant que le main process continue de s’exécuter.

Que se passe-t-il lorsque le processus principal a exécuté son code? Il se termine. Quand il se termine, il abandonne son contrôle sur le terminal. Ce qui provoque la perte d’access des enfants au terminal.

Ceci explique pourquoi vous obtenez une command not found – ce que vous avez tapé ne se trouve pas sur le stdin de votre programme mais sur l’invite du shell elle-même.


Votre code présentait également quelques problèmes,

1) Dans cette partie de votre code,

  scanf("%s", &buf); 

C’est faux. Tu n’as pas eu de chance et tu n’as pas eu de faute de segmentation. buf étant déjà une adresse, il aurait dû être

  scanf("%s", buf); 

2) Remarquez ceci,

  read(fds[0], buf, sizeof(buf)+1); 

Ce comportement est indéfini, comme indiqué dans la section commentaires. Vous essayez de lire plus de données et de les stocker dans un espace mémoire réduit. Cela aurait dû être,

  read(fds[0], buf, sizeof(buf)); 

3) appeler wait() . Vous avez créé deux processus enfants, vous devez attendre leur fin, vous devez donc appeler wait() deux fois .

Après avoir corrigé quelques problèmes dans le code, je suis arrivé à une version semi-instrumentée de votre programme comme celle-ci:

 #include  #include  #include  int main(void) { int pid, fds[2], pid1; char buf[200]; pipe(fds); pid = fork(); if (pid == 0) { close(fds[0]); printf("Prompt: "); fflush(0); if (scanf("%199s", buf) != 1) fprintf(stderr, "scanf() failed\n"); else write(fds[1], buf, strlen(buf) + 1); } else { pid1 = fork(); if (pid1 == 0) { close(fds[1]); if (read(fds[0], buf, sizeof(buf)) > 0) printf("%s\n", buf); else fprintf(stderr, "read() failed\n"); } else { /*Line1: wait();*/ } } return 0; } 

Cela se comstack proprement avec des options ssortingctes (GCC 5.1.0 sur Mac OS X 10.10.5):

 gcc -O3 -g -std=c11 -Wall -Wextra -Werror p11.c -o p11 

Quand je le lance, le résultat est:

 $ ./p11 Prompt: scanf() failed read() failed $ 

Le problème est clair. le scanf() échoue. En cause: pourquoi ?

La version wait() nécessite un en-tête supplémentaire #include et la séquence d’appel correcte. J’ai utilisé le paragraphe:

  else { printf("Kids are %d and %d\n", pid, pid1); int status; int corpse = wait(&status); printf("Parent gets PID %d status 0x%.4X\n", corpse, status); } 

Une fois compilé et exécuté, le résultat est maintenant:

 $ ./p11 Kids are 20461 and 20462 Prompt: Albatross Albatross Parent gets PID 20461 status 0x0000 $ 

La question devient alors: comment ou pourquoi l’entrée standard du processus enfant est-elle fermée lorsque le parent n’attend pas? C’est Bash qui contrôle le travail qui fait des ravages.

J’ai mis à jour le programme une fois de plus, en utilisant int main(int argc, char **argv) et en vérifiant si la commande avait été transmise à un argument:

  else if (argc > 1 && argv != 0) // Avoid compilation warning for unused argv { printf("Kids are %d and %d\n", pid, pid1); int status; int corpse = wait(&status); printf("Parent gets PID %d status 0x%.4X\n", corpse, status); } 

J’ai un shell Heirloom, qui est proche d’un shell Bourne d’origine. J’ai dirigé le programme en vertu de cela, et il s’est comporté comme je m’y attendais:

 $ ./p11 Prompt: $ Albatross Albatross $ ./p11 1 Kids are 20483 and 20484 Prompt: Albatross Albatross Parent gets PID 20483 status 0x0000 $ 

Notez le $ après l’ Prompt: lors de la première exécution; c’est l’invite du shell, mais lorsque je tape Albatross , il est (heureusement) lu par l’enfant du processus p11 . Ce n’est pas garanti cela aurait pu être le shell qui a lu l’entrée. Lors de la deuxième exécution, nous voyons la sortie du parent, puis les enfants au travail, puis le message sortant des parents.

Ainsi, sous les shells classiques, votre code fonctionnerait comme prévu. Bash interfère en quelque sorte avec le fonctionnement normal des processus enfants. Korn shell se comporte comme Bash. Il en va de même pour C shell ( tcsh ). Tentative de dash , j’ai eu un comportement intéressant (3 points):

 $ ./p11 Prompt: $ Albatross scanf() failed read() failed dash: 2: Albatross: not found $ ./p11 Prompt: $ Albatross scanf() failed dash: 4: Albatross: not found $ read() failed $ ./p11 Prompt: scanf() failed $ read() failed $ 

Notez que les deux premières exécutions indiquent que le dash lit l’entrée, mais les enfants n’ont détecté aucun problème tant que je n’ai pas tapé la dash Entrée après avoir tapé Albatross. La dernière fois, les enfants ont détecté des problèmes avant que je ne tape quoi que ce soit.

Et, de retour avec Bash, redirect les entrées standard fonctionne «proprement»:

 $ ./p11 <<< Albatross Prompt: Albatross $ ./p11 1 <<< Albatross Kids are 20555 and 20556 Prompt: Albatross Parent gets PID 20555 status 0x0000 $ 

La sortie Albatross vient bien sûr du deuxième enfant.

La réponse va se cacher quelque part dans le comportement des obus de contrôle du travail, mais c'est suffisant pour me donner envie de retourner à la vie avant cela.