C UNIX 中的叉子和管道

发布于 2024-08-11 08:22:44 字数 2009 浏览 8 评论 0原文

我不确定我是否在这里吠叫了正确的树……但就这样吧。

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

在父进程中,首先我设置管道并将它们设置为非阻塞(当管道中没有任何新数据可用时)。与客户端建立连接后,条目变量的数量会增加以反映此新连接。然后,我将一个子进程分叉到一个新函数,并使用新的表条目数更新我的管道数组(目前我有 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);
}

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

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

连接的第一个客户端告诉我 numentries = 1,第二个客户端告诉我 numentries = 2 等等。我的意思是,我真的没有看到管道的意义,因为似乎无论我在管道中放入什么,我都可以将它传递到我在叉子上调用的函数中。我是否以错误的方式处理这个问题?试图弄清楚这一点非常令人沮丧。如何使所有子进程同时从父进程更新?

预先非常感谢您。

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

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

谢谢你们忍受我!我很抱歉没有发布整个程序......但确实有很多。我绝对应该提到我将它置于无限循环中。

I'm not sure if I am even barking up the right tree here... but here goes.

I'm trying to pass data from my parent process to all children. It's a simple server program that basically will keep a list of connected clients and then send the routing table of connected clients to every client. This is eventually going to include a struct of information about each client... but for right now I just want to get every forked process to get the same information from the parent.

In the parent process, first I set up my pipes and set them to nonblocking (for when there isn't any new data available in the pipe). After a connection is made with a client the number of entries variable is increased to reflect this new connection. I then fork a child process to a new function and update my array of pipes with the new number of table entries (I have 10 pipes at the moment to see if I needed to keep a separate pipe for each child).

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

And then I try to read whats in the pipe in the recievecmds() function:

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

The first client connected tells me numentries = 1, the second client tells me numentries = 2 and so on. I mean I really don't even see the point for a pipe since it seems that whatever I put in the pipe I could just pass it in the function I called on the fork. Am I going about this the wrong way? It's been very frustrating trying to figure this out. How can I keep all of my child processes updated concurrently from my parent process?

Thank you so much in advance.

edit - My main problem was that I was redeclaring the pipe everytime in an infinite while loop. Very dumb mistake, immediately realized that was probably the root of my problem. However, while now the first child/pipe combo contains the correct data... the second does not. I'll see if I can figure this out on my own, thanks for the advice!

Of course now I'm running into problems because I manually select an option to get the data off the pipe. I'm going to have to think up a way to maybe either get the data for all pipes every time it's updated or make sure to get just the newest data (probably just one char at a time).

Thanks for putting up with me guys! And I apologize about not posting the whole program... but there's quite a bit. I definitely should have mentioned that I have it in an infinite loop.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

他不在意 2024-08-18 08:22:44

各种观察

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

工作代码

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

Various observations

  1. Don't make the pipes non-blocking; you want the children to block when there's no data. At least, in the early phases of the design; later, you may want to let them get on with work when there's no data waiting.
  2. You need to be careful with your plumbing. The parent needs 10 pipes, one for each child. But it only needs the write end of the pipe, not the read end.
  3. The children each need one pipe, for reading. Any superfluous pipes (for example, the write ends of the pipes that the parent had already opened before forking the Nth child) need to be closed.
  4. You could consider using threads - in which case you could perhaps pass the data to the children. But in the long term, it appears that you will be periodically passing data to the children, and then you need a mechanism to get the data to them (other than the function call).
  5. Pipes are 'easy' as long as you pay meticulous attention to which file descriptors are in use. Close all the descriptors you do not need.
  6. The parent will have to loop around all ten pipes writing the same data to each.
  7. It will also need to consider what to do if a child exits. It should close the pipe (no use any more), and decide whether to start a new child (but how will it ensure that the new child has all the accumulated information it needs?).
  8. Watch out for SIGPIPE - maybe install a handler, or maybe use SIG_IGN and detect write errors instead of signals.

Working code

#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;
}
墨小墨 2024-08-18 08:22:44

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

I'd suggest using a shared memory segment. Your parent and child processes can mmap the same file, and read/write state to it. This can be an actual file, or an anonymous memory segment. This is exactly what Apache does with their ScoreBoardFile.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文