簡體   English   中英

C UNIX中的叉子和管道

[英]Forks and Pipes in C UNIX

我不確定我是不是在這里咆哮着正確的樹......但是這里有。

我正在嘗試將數據從父進程傳遞給所有子進程。 這是一個簡單的服務器程序,基本上將保留連接的客戶端列表,然后將連接的客戶端的路由表發送到每個客戶端。 這最終將包括關於每個客戶端的信息結構......但是現在我只想讓每個分叉進程從父進程獲得相同的信息。

在父進程中,首先我設置我的管道並將它們設置為非阻塞(當管道中沒有任何新數據時)。 與客戶端建立連接后,將增加條目數變量以反映此新連接。 然后我將子進程分叉到一個新函數並用新的表條目數更新我的管道數組(我現在有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,依此類推。 我的意思是我真的甚至沒有看到管道的重點,因為看起來無論我放入管道,我都可以在我調用叉子的函數中傳遞它。 我是以錯誤的方式來做這件事的嗎? 試圖解決這個問題一直非常令人沮喪。 如何從父進程中同時更新所有子進程?

非常感謝你提前。

編輯 - 我的主要問題是我每次在無限循環中重新聲明管道。 非常愚蠢的錯誤,立刻意識到這可能是我問題的根源。 但是,現在第一個子/管道組合包含正確的數據......第二個不包含正確的數據。 我會看看我是否可以自己解決這個問題,謝謝你的建議!

當然,現在我遇到了問題,因為我手動選擇一個選項來從管道中獲取數據。 我將不得不想辦法在每次更新時獲取所有管道的數據,或者確保只獲取最新數據(可能一次只有一個字符)。

謝謝你忍住我們! 我為不發布整個程序而道歉......但是有很多。 我絕對應該提到我在無限循環中擁有它。

各種觀察

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

我建議使用共享內存段。 您的父進程和子進程可以對同一文件進行mmap,並對其進行讀/寫狀態。 這可以是實際文件,也可以是匿名內存段。 這正是Apache用ScoreBoardFile做的事情。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM