Мультитрубка в C (дети не перестают читать)

Я пытаюсь реализовать несколько каналов в C для запуска нескольких команд, таких как оболочка. Я сделал связанный список (называемый в моем коде t_launch), который выглядит так, если вы наберете «ls | grep src | wc»:

wc -- PIPE -- grep src -- PIPE -- ls

Каждый узел PIPE содержит int tab[2] из функции pipe() (конечно, для каждого узла PIPE был один вызов pipe())

Теперь я пытаюсь выполнить эти команды:

int     execute_launch_list(t_shell *shell, t_launch *launchs)
{
   pid_t pid;
   int   status;
   int   firstpid;

   firstpid = 0;
   while (launchs != NULL)
   {
      if ((pid = fork()) == -1)
         return (my_error("Unable to fork\n"));
      if (pid == 0)
      {
         if (launchs->prev != NULL)
         {
            close(1);
            dup2(launchs->prev->pipefd[1], 1);
            close(launchs->prev->pipefd[0]);
         }
         if (launchs->next != NULL)
         {
            close(0);
            dup2(launchs->next->pipefd[0], 0);
            close(launchs->next->pipefd[1]);
         }
        execve(launchs->cmdpath, launchs->words, shell->environ);
      }
      else if (firstpid == 0)
        firstpid = pid;
      launchs = launchs->next == NULL ? launchs->next : launchs->next->next;
   }
   waitpid(firstpid, &status, 0);
   return (SUCCESS);
}

Но это не работает: похоже, что команды не перестают читать. Например, если я наберу «ls | grep src», «src» будет напечатан из команды grep, но grep продолжит чтение и никогда не остановится. Если я наберу «ls | grep источник | wc", ничего не печатается. Что не так с моим кодом? Спасибо.


person Algo    schedule 11.07.2013    source источник
comment
Не могли бы вы объяснить launchs = launchs->next == NULL ? launchs->next : launchs->next->next;, пожалуйста? Я пропустил sonethin или вы пропускаете каждый второй дочерний процесс?   -  person Ingo Leonhardt    schedule 11.07.2013
comment
Я пропускаю узел PIPE : связанный список выглядит как grep -- PIPE -- ls, поэтому после того, как я сделал grep, я перехожу к ls, так что это ->next->next (один следующий приведет меня к узлу канала )   -  person Algo    schedule 11.07.2013
comment
спасибо теперь понял. Думаю, Ричи прав: клиент должен получить stdin от launch->prev и перенаправить stdout на launchs->next. ... упс, его комментарий удалили, но тем не менее...   -  person Ingo Leonhardt    schedule 11.07.2013
comment
@IngoLeonhardt: я удалил его, потому что увидел, что порядок в списке справа налево, поэтому следующий и предыдущий верны.   -  person rici    schedule 11.07.2013
comment
@rici Я теперь излечился от слепоты, кстати. хороший ответ, алго, забудь мой последний комментарий и посмотри на ответ Ричи   -  person Ingo Leonhardt    schedule 11.07.2013


Ответы (1)


Если я правильно понимаю ваш код, вы сначала вызываете pipe в процессе оболочки для каждого PIPE. Затем вы переходите к fork каждому процессу.

Хотя вы закрываете неиспользуемый конец каждого из дочерних каналов в процессе child, эта процедура страдает от двух проблем:

  1. У каждого ребенка есть все трубы, и он не затыкает те, которые ему не принадлежат.

  2. У родительского процесса (оболочки) открыты все каналы.

Следовательно, все трубы открыты, и дети не получают EOF.

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

person rici    schedule 11.07.2013
comment
Спасибо всем, моя реализация была действительно плохой. Я сделал новый код, используя рекурсию, и он отлично работает. Спасибо еще раз. - person Algo; 11.07.2013