Реализация итеративного конвейера для Linux в c

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

      a = 0;
      while (a < cmdnum)
      {
      pid[a] = fork();
      if( pid[a] == 0)
      {
      if( a == 0)
        {

          close(1);
          dup(p1_to_pn[a][1]);
          for( k = 0; k < nump; k++)
            {
              close(p1_to_pn[k][0]);
              close(p1_to_pn[k][1]);
            }  

          args = tokenize(cmd[a]);
          execv(args[0], args);
        }
      else if ( a == (cmdnum-1))
        {

          close(0);
          dup(p1_to_pn[a][0]);
          for( k = 0; k < nump; k++)
            {
              close(p1_to_pn[k][0]);
              close(p1_to_pn[k][1]);
            }
          args = tokenize(cmd[a]);

          i = execv(args[0], args);
        }
      else
        {

          close(0);
          dup(p1_to_pn[a][0]);
          close(1);
          dup(p1_to_pn[a][1]);
          for( k = 0; k < nump; k ++)
            {
              close(p1_to_pn[k][0]);
              close(p1_to_pn[k][1]);
           }
          args = tokenize(cmd[a]);

          execv(args[0], args);
        }
    }
  a++;
}

person GFXGunblade    schedule 29.09.2011    source источник
comment
Просто чтобы уточнить, вы пишете оболочку (или что-то, что работает как оболочка)?   -  person Greg Hewgill    schedule 29.09.2011


Ответы (1)


Ваша ошибка состоит в том, что вы думаете, что вам нужно перейти к «следующей» команде. Запустите их все.

Вот основная идея для конвейера, такого как A | B | C

Родительский процесс читает командную строку. Для каждой новой команды он будет создавать новый процесс и в конечном итоге выполнять его. Родительский процесс уже имеет открытые fd 0,1,2 (STDIN, STDOUT и STDERR). Когда вы нажмете на канал, используйте системные вызовы pipe(2) и dup(2) для создания нового fd; разветвить следующий процесс.

Все манипуляции с fd происходят в родительском процессе.

person Charlie Martin    schedule 29.09.2011
comment
О, я могу. Я провел большую часть ночи в аспирантуре на курсе системного программирования, пытаясь понять это. - person Charlie Martin; 29.09.2011
comment
Одна вещь, которую я не совсем понимаю в функции pipe, заключается в том, что когда вы вызываете pipe(2), разве она не создает два новых файловых дескриптора? как бы вы получили, скажем, двухканальный вызов для чтения вывода второй команды в качестве ввода третьей? Не будет ли дескриптор входного файла третьей команды указывать на другое место? - person GFXGunblade; 29.09.2011
comment
@GFXGunblade, родительский процесс поддерживает два канала. Родительский процесс изначально создает исходный входной канал, скажем, in[]. Прежде чем разветвить дочерний элемент, он создает новый канал out[]. Дочерний процесс перемещает in[0] в STDIN_FILENO и out[1] в STDOUT_FILENO и закрывает in[1] и out[0]. Родительский процесс закрывает in[0] и out[1], поскольку они использовались дочерним процессом, и копирует in[0] = out[0] для следующего дочернего процесса. После создания всех дочерних процессов in[0] является концом чтения последней команды в списке каналов. Всего у вас будет (commands+1) каналов, по 2 на процесс. - person Nominal Animal; 01.10.2012