Utilisation de canaux en C pour IPC parent-enfant permet de bloquer le programme

J’écris un serveur dont fork () est hors processus enfant lorsqu’il accepte une connexion socket.

Lorsque l’enfant communique avec un client, il doit renvoyer une partie de ces informations au parent. J’utilise un tuyau pour y parvenir.

Le problème est que lorsque j’essaie de faire l’IPC parent-enfant, le parent bloque lors de la lecture de l’entrée de l’enfant. Ce qui signifie que même si les enfants s’exécutent simultanément, ils ne peuvent être traités que l’un après l’autre, car ils attendent tous le parent.

Mon code ressemble à quelque chose comme ceci (vérification des erreurs supprimée pour des raisons de brièveté):

/* loop: wait for clients to connect */ for(;;) { pipe(write_to_parent); /* create pipe between parent and child */ pipe(write_to_child); /* create pipe between parent and child */ newsockfd = accept(...); pid = fork(); if (pid == 0) { /* child process */ close(write_to_parent[0]); close(write_to_child[1]); printf("Connected!\n"); while ((rc = recv(newsockfd, ...)) > 0) { /* process socket request */ /* write stuff to parent that is related to what we recv'd from client */ rc = write(write_to_parent[1], &buf, 1024); } printf("Disconnected!\n"); exit(0); } else { /* parent */ close(write_to_parent[1]); close(write_to_child[0]); while (read(write_to_parent[0], &buf, 1024) > 0) { /* accept data from child process, and do some processing */ } } } 

Donc ma question est, comment puis-je remédier à cela? Comment faire en sorte que le parent communique avec un enfant en utilisant des pipes de manière à ne pas bloquer?

Est-ce même possible avec des tubes ou devrais-je utiliser de la mémoire partagée (avec un sémaphore, je suppose) ou une queue de messages? (J’ai lu ce post: Comparaison d’IPC Unix / Linux, mais il est vraiment difficile de trouver des exemples montrant comment ces tâches sont réellement accomplies.)

Plus de détails:

J’ai un programme de test qui effectue les opérations suivantes: 1. Connectez-vous au serveur. 2. Veille (5). 3. Déconnectez-vous du serveur.

Lorsque j’exécute 2 instances de ce programme, le serveur génère ceci:

 connected! // pause for 5 seconds disconnected! connected! // pause for 5 seconds disconnected! 

Évidemment, traiter chaque client un à la fois.

Lorsque je supprime l’IPC – lorsque je supprime le tuyau read () et write () du parent et de l’enfant, je reçois ceci:

 connected! connected! // pause for 5ish seconds disconnected! disconnected! 

C’est ce que je veux!

Des idées sur la façon dont je peux accomplir cela? (ou des changements que je devrais faire dans la façon dont je vais résoudre ce problème?)

(edit: Cela fait partie d’une tâche assignée à une classe de réseau. Nous implémentons un protocole P2P qui utilise un serveur centralisé pour gérer les pairs. Nous pourrions utiliser n’importe quel langage, et je pensais que je l’essayerais en C.)

J’ai écrit la réponse ci-dessous et après avoir relu votre question, j’ai remarqué que j’avais complètement manqué votre question. Cependant, cela peut être utile dans tous les cas.

Pour répondre à votre question parent simultanée, vous devez demander au parent de configurer une boucle select() pour gérer les réponses possibles de ses enfants à tout moment. Tu devras:

  • garder une trace du nombre d’enfants ouverts
  • garder un ensemble de pipes pour chaque enfant
  • utilisez une boucle avec select() pour accepter les connexions entrantes (car elles peuvent survenir à tout moment) et les données entrantes de tout enfant
  • utilisez waitpid() pour waitpid() enfants qui ont terminé

Cette technique est puissante, mais elle nécessite beaucoup de comptabilité pour être correctement configurée.


Les descripteurs de fichier ouverts dans le parent sont hérités par défaut dans les processus enfants. Le problème est peut-être que le parent et l’enfant ont toujours le bout d’écriture du canal en écriture; ainsi, lorsque le parent lit le canal, il n’en verra jamais la fin. Essayez d’utiliser une close(write_to_parent[1]) dans le parent immédiatement avant de commencer à lire write_to_parent[0] . Alors:

 } else { /* parent */ close(write_to_parent[1]); while (read(write_to_parent[0], &buf, 1024) > 0) { /* accept data from child process, and do some processing */ } }