Использование каналов в C для родительского-дочернего IPC делает программный блок - PullRequest
1 голос
/ 22 февраля 2009

Я пишу сервер, который fork () отключает дочерний процесс, когда он принимает соединение с сокетом.

Когда ребенок общается с клиентом, он должен отправить часть этой информации обратно родителю. Для этого я использую трубу.

Проблема в том, что когда я пытаюсь сделать IPC родитель-потомок, родительский блок блокирует чтение входных данных от дочернего. Это означает, что, хотя дети работают одновременно, они могут обрабатываться только по одному, потому что все они ожидают родителя.

Мой код выглядит примерно так (проверка ошибок удалена для краткости):

/* loop: wait for clients to connect */
for(;;) {

  pipe(write_to_parent); /* create pipe between parent and child */
  pipe(write_to_child);  /* create pipe between parent and child */

  newsockfd = accept(...);

  pid = fork();

  if (pid == 0) { /* child process */
      close(write_to_parent[0]);
      close(write_to_child[1]);

     printf("Connected!\n");

     while ((rc = recv(newsockfd, ...)) > 0) {
         /* process socket request */

         /* write stuff to parent that is related to what we recv'd from client */
         rc = write(write_to_parent[1], &buf, 1024);
     }

     printf("Disconnected!\n");

     exit(0);
  } else { /* parent */

      close(write_to_parent[1]);
      close(write_to_child[0]);

       while (read(write_to_parent[0], &buf, 1024) > 0) {
           /* accept data from child process, and do some processing */
       }
  }
}

Итак, мой вопрос, как мне это исправить? Как я могу, чтобы родитель общался с детьми, используя каналы таким образом, чтобы он не блокировал?

Возможно ли это даже с помощью каналов, или я должен использовать совместно используемую память (с семафором, я полагаю) или вместо очереди сообщений? (Я читал этот пост: Сравнение Unix / Linux IPC , но действительно трудно найти примеры того, как эти задачи действительно выполняются.)

Подробнее:

У меня есть тестовая программа, которая выполняет следующие действия: 1. Подключитесь к серверу 2. Сон (5) 3. Отключиться от сервера

Когда я запускаю 2 экземпляра этой программы, сервер выводит это:

connected!
  // pause for 5 seconds
disconnected!
connected!
  // pause for 5 seconds
disconnected!

Очевидно, обрабатывает каждого клиента по одному.

Когда я удаляю IPC - когда я избавляюсь от конвейера read () и write () от родительского и дочернего, я получаю это:

connected!
connected!
  // pause for 5ish seconds
disconnected!
disconnected!

Что я и хочу!

Есть мысли о том, как мне это сделать? (или изменения, которые я должен внести в решение этой проблемы?)

(edit: это часть задания для сетевого класса. Мы внедряем протокол P2P, который использует централизованный сервер для управления одноранговыми узлами. Мы могли бы использовать любой язык, и я решил, что это вызовет бурную реакцию C.)

1 Ответ

2 голосов
/ 22 февраля 2009

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

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

  • отслеживать, сколько детей открыто
  • сохранить набор труб для каждого ребенка
  • использовать цикл с select() для принятия входящих соединений (поскольку они могут произойти в любое время), а также входящих данных от любого дочернего элемента
  • используйте waitpid(), чтобы пожинать детей, которые закончили

Этот метод является мощным, но для правильной настройки требуется много бухгалтерии.


Файловые дескрипторы, открытые в родительском элементе, по умолчанию наследуются в дочерних процессах. Проблема может заключаться в том, что и родитель и дочерний элемент имеют конец записи канала, который еще открыт, поэтому, когда родитель читает из канала, он никогда не увидит его конец. Попробуйте использовать close(write_to_parent[1]) в родительском элементе непосредственно перед началом чтения с write_to_parent[0]. Итак:

} else { /* parent */
     close(write_to_parent[1]);
     while (read(write_to_parent[0], &buf, 1024) > 0) {
         /* accept data from child process, and do some processing */
     }
}
...