2017-01-10 9 views
1

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

#include <unistd.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <sys/wait.h> 

int main(int argc, char**argv) { 
    int fd[2]; 

    pipe(fd); 
    for(int i = 0; i < 2; i++) { 
     if (fork() == 0) { 
      printf("\nloop %d\n", i); 
      if (i == 0) { 
       dup2(fd[1], 1); 
       close(fd[0]); 
       close(fd[1]); 
       printf("\nbefore exec %s\n", argv[1]); 
       execlp(argv[1], argv[1], NULL); 
      } 
      else if(i == 1) { 
       dup2(fd[0], 0); 
       close(fd[0]); 
       close(fd[1]); 
       printf("\nbefore exec %s\n", argv[2]); 
       execlp(argv[2], argv[2], NULL); 
      } 
      exit(0); 
     } 
     wait(NULL); 
    } 
    return(0); 
} 
+0

Если вы проверите возвращаемые значения своих вызовов функций, как вы должны это сделать, вы узнаете, где произошел сбой, если действительно существует какой-либо фактический сбой вообще. –

+0

Первый ребенок умирает после «dup2 (fd [1], 1)», но я не понимаю, почему .. –

+0

Что такое возвращаемое значение от 'dup2'? – Tim

ответ

3

Помимо отказа проверки возвращаемых значений вызовов функций и обработки ошибок, которые могут быть указаны те возвращаемые значения, ваш код выглядит довольно разумно. Однако я вижу две проблемы:

  1. Наиболее значительная проблема, которую я вижу, заключается в том, что основной процесс сохраняет свои собственные копии концов труб. До тех пор, пока существует хотя бы одно открытое описание файла, относящееся к концу записи в канале, в любом процессе конец файла не будет передаваться на чтение. Если процесс, запущенный на считываемом конце, является тем, который считывает свой ввод до завершения, например cat, grep или sed, тогда этот процесс будет ждать навсегда, чтобы конец записи был закрыт, поскольку основной процесс не будет завершен до тех пор, пока ребенок заканчивается (из-за wait()).

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

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