2017-07-23 2 views
0

Je veux faire un programme simple, où un processus père crée des processus fils; avant la pause de l'enfant(), ils notifient le processus père. Les processus enfants s'exécutent correctement, mais le père attend sur select, sinon l'enfant a écrit sur socket; où est l'erreur?Programmation de socket, processus bloqué sur select?

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 
#include <sys/select.h> 
#include <sys/socket.h> 
#include <sys/time.h> 
#include <sys/types.h> 
#include <sys/wait.h> 
#include <unistd.h> 
#include <errno.h> 

typedef struct{ 
    pid_t pid; 
    int sockfd; 
}Child; 


void err_exit(char* str) 
{ 
    perror(str); 
    exit(EXIT_FAILURE); 
} 


int convert_int(char* str) 
{ 
    int v; 
    char*p; 

    errno = 0; 
    v = strtol(str,&p,0); 
    if(errno != 0 || *p != '\0') 
     err_exit("errno"); 
    return v; 
} 



void child_job(pid_t pid,int sockfd) 
{ 
    int v = write(sockfd,"1",1); 
    if(v == -1) 
     err_exit("write"); 
    printf("process %d in pause()\n",pid); 
    pause(); 
} 


int main(int argc, char* argv[]) 
{ 
    int nsel; 
    fd_set masterset; 
    int n_child,i; 
    int sockfd[2]; 
    pid_t pid; 
    Child* c = NULL; 

    if(argc != 2) 
     err_exit("usage: <awake2> #children\n"); 
    FD_ZERO(&masterset); 
    n_child = convert_int(argv[1]); 
    c = malloc(n_child*sizeof(Child)); 
    if(c == NULL) 
     err_exit("malloc"); 


    for(i = 0; i <n_child; i++){ 
     if ((socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd)) < 0) {  //create socket between child and father 
       perror("errore in socketpair"); 
       exit(1); 
     } 
     if ((pid = fork()) > 0) { 
      if (close(sockfd[1]) == -1) { //father process closes sockfd[1] 
       perror("errore in close"); 
       exit(1); 
      } 
      c[i].pid = pid; 
      c[i].sockfd = sockfd[0]; 
      FD_SET(c[i].sockfd, &masterset); 

     } 
     else if(!pid) 
      child_job(getpid(),c[i].sockfd); 
    } 

    for(;;){ 
     if ((nsel = select(n_child+1, &masterset, NULL, NULL, NULL)) < 0) { 
       perror("errore in bind"); 
       exit(1); 
      } 
     int i; 
     for(i = 0; i <n_child; i++){ 
      if(FD_ISSET(c[i].sockfd, &masterset)) { 
       printf("changed fd\n"); 
      } 

     } 
    } 
} 

Répondre

1

Une chose qui ne va pas est vous passez c[i].sockfd-child_job(). Dans le processus parent, il a été défini sur le premier socket fd de la paire, mais child_job() est appelé dans le processus fils, où c n'est jamais défini sur rien. Vous transmettez le contenu original de la mémoire malloc. Changez cela en child_job(getpid(), sockfd[1]); et vous vous rapprocherez.

Une autre chose est que le premier argument à select est probablement trop faible. n_child est le nombre d'enfants, mais vous devez indiquer ici un nombre supérieur au descripteur de fichier le plus élevé de votre ensemble. Par exemple, exécutez le programme avec l'argument 1 afin qu'il crée 1 enfant. Il est probable qu'il commencera avec les descripteurs de fichiers 0, 1 et 2 ouverts, donc la paire de socket sera les descripteurs de fichier 3 et 4. Le 3 va dans le fd_set, mais le premier argument à sélectionner est 1 + 1 = 2. select ignore votre fd 3 car c'est au dessus de la limite.

Pour corriger cela, créez une nouvelle variable int maxfd; près de votre fd_set, l'initialiser à -1 lorsque vous FD_ZERO l'ensemble, et après chaque appel à FD_SET, mettre à jour:

if([whatever fd you just gave to FD_SET] > maxfd) 
    maxfd = [whatever fd you just gave to FD_SET]; 

et appelez sélectionner avec maxfd+1 comme le premier argument.

(Ou peut-être passer à poll)

Cela devrait vous obtenir assez loin pour que vos premiers travaux de sélection d'appel. Après cela, vous trouverez plus de problèmes.

Le fd_set que vous passez pour sélectionner sera modifié (c'est pourquoi vous pouvez faire des tests FD_ISSET par la suite). Si vous retournez en haut de la boucle et que vous la passez à nouveau sans la réinitialiser, sélectionnez ne regardera plus tous les descripteurs de fichiers, seulement ceux qui étaient prêts dans le premier appel. Pour résoudre ce problème, faites une seconde fd_set et copiez le master juste avant l'appel select, et ne passez jamais le master à sélectionner. Si vous obtenez un fd lisible de select, vous devriez le lire avant d'appeler select again, sinon vous êtes juste dans un "eat CPU". appeler sélectionner "boucle".

+0

La raison pour laquelle select choisit le maximum fd plus 1 est que (dans l'implémentation d'origine au moins) le noyau traite le 'fd_set' comme un tableau de bits sans limite de taille inhérente. Il y a quelques implémentations où vous pouvez agrandir la définition 'fd_set' dans votre programme C si la taille par défaut n'est pas assez grande pour tous vos fd. Donc le" max fd plus 1 "indique juste où le tableau se termine. C'est exactement la même raison que 'read' prend un pointeur et une taille. Les implémentations qui n'utilisent pas un tableau de bits, ou ne permettent pas de le redimensionner, ne devraient pas vraiment avoir besoin du maxfd + 1 mais elles font partie de l'API –