Внедрение конвейерной обработки в c. Какой был бы лучший способ сделать это? (Собственная shell linux)

Я не могу придумать какой-либо способ реализации конвейерной обработки в c, который бы действительно работал. Вот почему я решил написать здесь. Должен сказать, что я понимаю, как работает pipe / fork / mkfifo. Я видел много примеров реализации 2-3-х трубопроводов. Это просто. Моя проблема начинается, когда мне нужно реализовать оболочку, а количество конвейеров неизвестно.

Что у меня есть сейчас: например.

ls -al | tr az AZ | tr AZ az | tr az AZ 

Я трансформирую такую ​​строку в нечто подобное:

 array[0] = {"ls", "-al", NULL"} array[1] = {"tr", "az", "AZ", NULL"} array[2] = {"tr", "AZ", "az", NULL"} array[3] = {"tr", "az", "AZ", NULL"} 

Поэтому я могу использовать

 execvp(array[0],array) 

позже.

Не сейчас, я считаю, все в порядке. Проблема начинается, когда я пытаюсь перенаправить эти функции ввода / вывода в eachother.

Вот как я это делаю:

  mkfifo("queue", 0777); for (i = 0; i 0 && i  pdesc[1] close(desc_write); } if (i == pipelines_count) // last pipeline (changing stdin only) { int desc_read = open("queue", O_RDONLY); dup2(desc_read, 0); // changing stdin -> pdesc[0] close(desc_read); } if (i > 0 && i  pdesc[1] int desc_read = open("queue", O_RDONLY); dup2(desc_read, 0); // changing stdin -> pdesc[0] close(desc_write); close(desc_read); } wait(NULL); // it wait's until, process c is death execvp(array[0],array); } } else // parent (waits for 1 sub command to be finished) { wait(NULL); } } 

Благодарю.

    Патрик, почему вы используете фиолетовый, и, кроме того, тот же самый fifo для каждого этапа трубопровода?

    Мне кажется, что вам нужна труба между каждым этапом. Таким образом, stream будет примерно таким:

     Shell ls tr tr ----- ---- ---- ---- pipe(fds); fork(); close(fds[0]); close(fds[1]); dup2(fds[0],0); pipe(fds); fork(); close(fds[0]); close(fds[1]); dup2(fds[1],1); dup2(fds[0],0); exex(...); pipe(fds); fork(); close(fds[0]); etc dup2(fds[1],1); exex(...); 

    Последовательность, которая выполняется в каждой разветвленной оболочке (close, dup2, pipe и т. Д.), Будет казаться функцией (с указанием имени и параметров желаемого процесса). Обратите внимание, что до вызова exec в каждом выполняется разветвленная копия оболочки.

    Редактировать:

    Patryk:

     Also, is my thinking correct? Shall it work like that? (pseudocode): start_fork(ls) -> end_fork(ls) -> start_fork(tr) -> end_fork(tr) -> start_fork(tr) -> end_fork(tr) 

    Я не уверен, что вы подразумеваете под start_fork и end_fork. Вы подразумеваете, что ls заканчивается до начала tr ? На самом деле это не так, как показано на диаграмме выше. Ваша shell не будет ждать завершения ls перед запуском tr . Он запускает все процессы в трубе последовательно, настраивая stdin и stdout для каждого из них, так что процессы связаны друг с другом, stdout от ls до stdin из tr ; stdout от tr до stdin следующего tr . Это то, что делают звонки dup2.

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

    Надеюсь, что немного прояснит ситуацию 🙂

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

    Проблема в том, что вы пытаетесь сделать все сразу. Вместо этого перейдите на более мелкие шаги.

    1) Разберите свой ввод, чтобы получить ls -al | из него. 1a) Из этого вы знаете, что вам нужно создать канал, перенести его на stdout и запустить ls -al . Затем переместите трубу в stdin. Конечно, есть еще много, но вы еще не беспокоитесь об этом в коде.

    2) Разберите следующий сегмент, чтобы получить tr az AZ | , Вернитесь к шагу 1a до тех пор, пока ваш вывод команды next-to-spawn куда-нибудь будет передан.