Вилы и трубы в C UNIX - PullRequest
       32

Вилы и трубы в C UNIX

4 голосов
/ 10 ноября 2009

Я не уверен, что я даже лаю на правильное дерево здесь ... но здесь идет.

Я пытаюсь передать данные из родительского процесса всем детям. Это простая серверная программа, которая в основном ведет список подключенных клиентов, а затем отправляет таблицу маршрутизации подключенных клиентов каждому клиенту. В конечном итоге это будет включать в себя структуру информации о каждом клиенте ... но сейчас я просто хочу, чтобы каждый разветвленный процесс получал ту же информацию от родителя.

В родительском процессе сначала я настраивал свои каналы и устанавливал их в неблокирующую (для тех случаев, когда в канале нет новых данных). После того, как соединение установлено с клиентом, переменная количества записей увеличивается, чтобы отразить это новое соединение. Затем я перевожу дочерний процесс в новую функцию и обновляю свой массив каналов новым количеством записей в таблице (на данный момент у меня есть 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 и так далее. Я имею в виду, что на самом деле я даже не вижу смысла для канала, так как кажется, что, что бы я ни вставил в канал, я мог бы просто передать его в функцию, которую я вызвал на развилке. Я поступаю об этом неправильно? Это было очень сложно пытаться понять это. Как я могу сохранить все мои дочерние процессы обновленными одновременно от моего родительского процесса?

Заранее большое спасибо.

edit - Моя главная проблема заключалась в том, что я каждый раз переопределял канал в бесконечном цикле while. Очень глупая ошибка, сразу понял, что, наверное, корень моей проблемы. Тем не менее, в то время как теперь первая комбинация child / pipe содержит правильные данные ... вторая - нет. Я посмотрю, смогу ли я понять это самостоятельно, спасибо за совет!

Конечно, сейчас у меня возникают проблемы, потому что я вручную выбираю опцию, чтобы получить данные с конвейера. Мне нужно будет придумать способ, чтобы, возможно, либо получать данные для всех каналов каждый раз, когда они обновляются, либо обязательно получать только самые новые данные (вероятно, только один символ за раз).

Спасибо, что терпите меня, ребята! И я прошу прощения за то, что не выложил всю программу ... но есть немало. Я определенно должен был упомянуть, что у меня это в бесконечном цикле.

Ответы [ 2 ]

6 голосов
/ 10 ноября 2009

Различные наблюдения

  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;
}
1 голос
/ 10 ноября 2009

Я бы предложил использовать сегмент совместно используемой памяти. Ваш родительский и дочерний процессы могут отображать один и тот же файл и читать и записывать в него состояние. Это может быть реальный файл или анонимный сегмент памяти. Это именно то, что Apache делает со своими ScoreBoardFile .

...