套接字编程,进程被阻止选择?

时间:2017-07-23 20:08:42

标签: sockets select fork

我想做一个简单的程序,父进程创建一些子进程;在孩子暂停()之前,他们通知父亲过程。 子进程运行正常,但是父进行select等,否则child已经写入套接字;错误在哪里?

#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");
             }

         }
     }
}

1 个答案:

答案 0 :(得分:1)

有一件事是错误的,你将c[i].sockfd传递给child_job()。在父进程中,它被设置为对中的第一个套接字fd,但在子进程中调用child_job(),其中c永远不会被设置为任何内容。您正在传递malloc内存的原始内容。将其更改为child_job(getpid(), sockfd[1]);,您就会越来越近。

另一件事是select的第一个参数可能太低了。 n_child是子项的数量,但您需要在此处传递一个数字,该数字大于集合中的最高文件描述符。例如,使用参数1运行程序,以便创建1个子项。它可能以文件描述符0,1和2打开​​,因此套接字对将是文件描述符3和4. 3进入fd_set,但是第一个选择的参数是1 + 1 = 2。选择忽略你的fd 3,因为它高于限制。

要解决此问题,请在int maxfd;附近创建一个新变量fd_set,并在-1集合时将其初始化为FD_ZERO,并在每次调用{{1}后将其初始化为FD_SET更新它:

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

并使用maxfd+1作为第一个参数调用select。

(或者可以切换到poll

这应该让你足够远,你的第一个选择呼叫工作。之后,您会发现更多问题。

您传递给选择的fd_set将被修改(这就是您之后可以对其进行FD_ISSET测试的原因)。如果你回到循环的顶部并再次传递它而不重新初始化它,select将不再查看所有文件描述符,只是在第一次调用中准备好的文件描述符。要解决此问题,请在选择调用之前再创建一个fd_set并将主服务器复制到其中,并且永远不要将主服务器传递给select。 (或者您可以通过扫描子表每次从头开始重建集。)

如果你从select中得到一个可读的fd,你应该在再次调用select之前阅读它,否则你只是在一个&#34;吃CPU调用select over over&#34;循环。