nixp.ru v3.0

18 января 2017,
среда,
17:07:12 MSK

DevOps с компанией «Флант»
Fatal написал 23 октября 2004 года в 23:17 (506 просмотров) Ведет себя как мужчина; открыл 123 темы в форуме, оставил 484 комментария на сайте.

Реализация конвееров

Подскажите, пожалуйста, как можно организовать конвеер для нескольких программ.

Для двух понятно:

1. закрываем input и output у обоих программ

2. открываем канал pipe()

3. затем dup2 изменяем значения дескрипторов на 1 и 0 (out/in)

Приблизительно так

int main(int argc,char**args){

int chpid,fd[2];

pipe(fd);

chpid=fork();

if(!chpid){

close(0);

dup2(fd[0],0);

close(fd[0]);

close(fd[1]);

execl(«/bin/more»,«more»,0);

}

else{

close(1);

dup2(fd[1],1);

close(fd[0]);

close(fd[1]);

execl(«/bin/ls»,«ls»,«-la»,«/dev»,0);

}

return 0;

}

А если таким макаром запустить несколько программ, то информация из каналов достанется кому попало, короче хаос будет, ведь ввод и вывод один на всех. И как быть если нужно prog1 | prog2 | prog3 | prog4 etc.? Надо синхронизировать? Или есть путь более элегантный?

KSK

Что то не совсем понятно в чем проблема? Разве нельзя для 4 процессов создать 3 конвейера, т.е. через первый общается первый процесс со вторым, через второй — второй процесс с третьим, и т.д.

Fatal

Нельзя, потому что если я перенаправлю ввод/вывод в pipe то нормально общаться будут только 2 процесса, так как ввод и вывод один на все процессы. А если будут общаться несколько процессов, то ни будут писать на один и тот же поток ввода и вывода. Т.е. получиться абра кадабра. Один поток пишет в трубу, другой должен прочитать, а на самом деле будет читать не один, а несколько — раз конвееров болше чем один и поток один на всех. И здесь неизвестно кто получить эту порцию информации.

Может я что-то не допонимаю….

Fatal

Вообще говоря я ошибся, когда мы реализуем конвееры мы создаем pipe, а он может быть для каждого свой

Fatal

/* Подскажите, если кто знает, почему так не работает, а если убрать восклицательный занк при проверки chpid в функции fun() */

void fun(){

int chpid,fd[2];

pipe(fd);

chpid=fork();

if(!chpid){ /*убрать восклицательный знак здесь */

close(0);

dup2(fd[0],0);

close(fd[0]);close(fd[1]);

execl(«/usr/bin/more»,«more»,0);

}

else{

close(1);

dup2(fd[1],1);

close(fd[0]);close(fd[1]);

execl(«/bin/ls»,«ls»,«-l»,«/etc»,0);

}

}

int main(int argc,char**args){

fun();

return 0;

}

metal

Это вроде работает, но есть странности не пойму в чем.

#include

void fun(){

int chpid,fd[2];

pipe(fd);

chpid=fork();

if(!chpid){ /*убрать восклицательный знак здесь */

dup2(fd[0],0);

close(fd[0]);close(fd[1]);

execl(«/usr/bin/more»,«more»,0);

}

else{

dup2(fd[1],1);

close(fd[0]);close(fd[1]);

execl(«/bin/ls»,«ls»,«-l»,«/etc»,0);

}

}

int main(int argc,char**args){

fun();

return 0;

}

Fatal

То есть она выводит broken pipe как и у меня, без восклицательного знака.

metal

Я убрал close(0) и close(1), поэтому программа не выдает broken pipe, она работает, но во-первых нет гарантии, что сначала выполнится родитель, а потом ребенок. во-вторых перестает корректно работать консоль после запуска программы. Как я понимаю — это результат некорректной работы с потоками ввода/вывода.

Fatal

Именно, я такое тоже замечал, но хотелось бы узнать почему?

metal

Вроде разобрался close(0), close(1) здесь ни причем, broken pipe и некоторые другие проблемы возникают если more выполняется раньше ls.

void fun(){

int chpid,fd[2];

pipe(fd);

chpid=fork();

if(chpid){ /*убрать восклицательный знак здесь */

close(0);

dup2(fd[0],0);

close(fd[0]);close(fd[1]);

sleep(1);

execl(«/usr/bin/more»,«more»,0);

}

Должно работать коректно если ls выполниться за 1 секунду. Вообще конвейер — это последовательное выполнение процессов, а здесь получается паралельное.

metal

Проблема с выводом возникает из-за того что bash ожидает вывода от родительского процесса, а он при завершении передает свой вывод потомку о котором bash ничего не знает. На мой взгдляд это глюк работоспособность shell не должна зависеть от того, что из под него запускается.

Fatal
metal
Вообще конвейер — это последовательное выполнение процессов, а здесь получается паралельное.

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

Fatal
metal
Проблема с выводом возникает из-за того что bash ожидает вывода от родительского процесса, а он при завершении передает свой вывод потомку о котором bash ничего не знает. На мой взгдляд это глюк работоспособность shell не должна зависеть от того, что из под него запускается.

Конечно не должно. Shell запускает пру fork + exec. И после этого формируются всякие вещи относящиеся к программе именно ядром и к этому shell не имеет никакого отношения.

Я запускаю через tcsh

Fatal
metal
некоторые другие проблемы возникают если more выполняется раньше ls.

Если бы работало с ошибкой из-за, того что more запускается раньше, тогда бы можно было бы решить эту проблему, поставив sleep() перед execl, который вызовает more. Но это не проходит.

Fatal

Вот что я нашел в исходниках по tcsh

/*

* C shell

*/

/*

* For SVR4, there are problems with pipelines having the first process as

* the group leader. The problem occurs when the first process exits before

* the others have a chance to setpgid(). This is because in SVR4 you can’t

* have a zombie as a group leader. The solution I have used is to reverse

* the order in which pipelines are started, making the last process the

* group leader. (Note I am not using 'pipeline' in the generic sense — I

* mean processes connected by '|’.) I don’t know yet if this causes other

* problems.

*

* All the changes for this are in execute(), and are enclosed in

* '#ifdef BACKPIPE'

*

* David Dawes (dawes@physics.su.oz.au) Oct 1991

*/

Fatal
Fatal
Конечно не должно. Shell запускает пру fork + exec. И после этого формируются всякие вещи относящиеся к программе именно ядром и к этому shell не имеет никакого отношения.

Я запускаю через tcsh

Проблемы могут быть, если bash работает не верно с каналами, но bash работает верно с каналами

metal

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

ecobeingecobeing.ru
Экология и вегетарианство на благо всем живым существам Планеты.