C UNIX中的叉子和管道

时间:2009-11-10 03:22:19

标签: c ipc fork pipe

我不确定我是否在这里咆哮着正确的树......但是这里也是。

我正在尝试将父进程中的数据传递给所有子进程。这是一个简单的服务器程序,基本上将保留连接的客户端列表,然后将连接的客户端的路由表发送到每个客户端。这最终将包含关于每个客户端的信息结构......但是现在我只想让每个分叉进程从父进程获得相同的信息。

在父进程中,首先我设置我的管道并将它们设置为非阻塞(当管道中没有任何新数据时)。与客户端建立连接后,将增加条目数变量以反映此新连接。然后我将子进程分叉到一个新函数并用新的表条目更新我的管道数组(我现在有10个管道,看看我是否需要为每个子进程保留一个单独的管道。)

            pid_t pid;
    int numchildren;

    int i, n;

    /* Create the pipes. */
    for(i = 0; i < 10; i++)
    {
        if (pipe (mypipe[i]))
        {
        fprintf (stderr, "Pipe failed.\n");
        return EXIT_FAILURE;
        }
    }

    for(i = 0; i < 10; i++)
    {
        for(n=0; n<2; n++)
        {
          // Get previous flags
          int f = fcntl(mypipe[i][n], F_GETFL, 0);
          // Set bit for non-blocking flag
          f |= O_NONBLOCK;
          // Change flags on fd
          fcntl(mypipe[i][n], F_SETFL, f);
        }
        //close(mypipe[i][0]);
    }

        pid = fork();

    if (pid == (pid_t) 0)
    {
        close (mypipe[numentries-1][1]);
        recievecmds(new_fd, mypipe[numentries-1][0]);
        close(new_fd);
        return EXIT_SUCCESS;
    }

else if (pid < (pid_t) 0)
{
    fprintf (stderr, "Fork failed.\n");
    return EXIT_FAILURE;
}
else
{
    sprintf (buf,"%d",numentries);
    for(i = 0; i < 10; i++)
        write(mypipe[i][1], buf, strlen(buf));
    memset(&buf, 0, sizeof buf);
}

然后我尝试在recievecmds()函数中读取管道中的内容:

nbytes = read(mypipe[childindex][0], buf, sizeof(buf));

连接的第一个客户端告诉我numentries = 1,第二个客户端告诉我numentries = 2,依此类推。我的意思是我真的甚至没有看到管道的重点,因为看起来无论我放入管道,我都可以在我调用叉子的函数中传递它。我是以错误的方式来做这件事的吗?试图解决这个问题一直非常令人沮丧。如何从父进程中同时更新所有子进程?

提前非常感谢你。

编辑 - 我的主要问题是我每次都在无限循环中重新声明管道。非常愚蠢的错误,立刻意识到这可能是我问题的根源。但是,现在第一个子/管道组合包含正确的数据......第二个不包含正确的数据。我会看看我是否可以自己解决这个问题,谢谢你的建议!

当然,现在我遇到了问题,因为我手动选择一个选项来从管道中获取数据。我将不得不想办法在每次更新时获取所有管道的数据,或者确保只获取最新数据(可能一次只有一个字符串)。

谢谢你们忍受我们!我为不发布整个程序而道歉......但是有很多。我绝对应该提到我在无限循环中拥有它。

2 个答案:

答案 0 :(得分:6)

各种观察

  1. 不要使管道无阻塞;你希望孩子在没有数据时阻止。至少,在设计的早期阶段;之后,当没有数据等待时,你可能想让他们继续工作。
  2. 你需要小心你的管道。父母需要10个管道,每个孩子一个。但它只需要管道的写端,而不是读端。
  3. 每个孩子都需要一根烟斗,供阅读。需要关闭任何多余的管道(例如,在分叉第N个孩子之前父母已经打开过的管道的写入端)。
  4. 您可以考虑使用线程 - 在这种情况下,您可以将数据传递给子节点。但从长远来看,您似乎会定期将数据传递给子级,然后您需要一种机制来获取数据(除了函数调用)。
  5. 只要您仔细关注正在使用的文件描述符,管道就会“简单”。关闭所有不需要的描述符。
  6. 父母必须遍历所有十个管道,并为每个管道写入相同的数据。
  7. 如果孩子退出,还需要考虑该怎么做。它应该关闭管道(不再使用),并决定是否开始一个新的孩子(但它将如何确保新孩子拥有所需的所有累积信息?)。
  8. 注意SIGPIPE - 也许安装处理程序,或者使用SIG_IGN并检测写入错误而不是信号。

  9. 工作代码

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    
    enum { NUM_CHILDREN = 10 };
    enum { NUM_MESSAGES = 10 };
    
    static int write_pipes[NUM_CHILDREN];
    static int n_pipes;
    
    static void be_childish(int *pipe)
    {
        int i;
        char buffer[32];
        int nbytes;
        int pid = getpid();
        close(pipe[1]);
        for (i = 0; i < n_pipes; i++)
            close(write_pipes[i]);
        printf("Child %d\n", pid);
        while ((nbytes = read(pipe[0], buffer, sizeof(buffer))) > 0)
        {
            printf("Child %d: %d %.*s\n", pid, nbytes, nbytes, buffer);
            fflush(0);
        }
        printf("Child %d: finished\n", pid);
        exit(0);
    }
    
    int main(void)
    {
        pid_t pid;
        int i, j;
    
        /* Create the pipes and the children. */
        for (i = 0; i < NUM_CHILDREN; i++)
        {
            int new_pipe[2];
            if (pipe(new_pipe))
            {
                int errnum = errno;
                fprintf(stderr, "Pipe failed (%d: %s)\n", errnum, strerror(errnum));
                return EXIT_FAILURE;
            }
            if ((pid = fork()) < 0)
            {
                int errnum = errno;
                fprintf(stderr, "Fork failed (%d: %s)\n", errnum, strerror(errnum));
                return EXIT_FAILURE;
            }
            else if (pid == 0)
            {
                be_childish(new_pipe);
            }
            else
            {
                close(new_pipe[0]);
                write_pipes[n_pipes++] = new_pipe[1];
            }
        }
    
        for (i = 0; i < NUM_MESSAGES; i++)
        {
            char message[30];
            int len;
            snprintf(message, sizeof(message), "Message %d", i);
            len = strlen(message);
            for (j = 0; j < n_pipes; j++)
            {
                if (write(write_pipes[j], message, len) != len)
                {
                    /* Inferior error handling; first failure causes termination */
                    fprintf(stderr, "Write failed (child %d)\n", j);
                    exit(1);
                }
            }
            sleep(1);
        }
        printf("Parent complete\n");
    
        return 0;
    }
    

答案 1 :(得分:1)

我建议使用共享内存段。您的父进程和子进程可以对同一文件进行mmap,并对其进行读/写状态。这可以是实际文件,也可以是匿名内存段。这正是Apache用ScoreBoardFile做的事情。