Реализация конвееров
Подскажите, пожалуйста, как можно организовать конвеер для нескольких программ.
Для двух понятно:
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.? Надо синхронизировать? Или есть путь более элегантный?
Последние комментарии
- OlegL, 17 декабря в 15:00 → Перекличка 21
- REDkiy, 8 июня 2023 года в 9:09 → Как «замокать» файл для юниттеста в Python? 2
- fhunter, 29 ноября 2022 года в 2:09 → Проблема с NO_PUBKEY: как получить GPG-ключ и добавить его в базу apt? 6
- Иванн, 9 апреля 2022 года в 8:31 → Ассоциация РАСПО провела первое учредительное собрание 1
- Kiri11.ADV1, 7 марта 2021 года в 12:01 → Логи catalina.out в TomCat 9 в формате JSON 1
Что то не совсем понятно в чем проблема? Разве нельзя для 4 процессов создать 3 конвейера, т.е. через первый общается первый процесс со вторым, через второй — второй процесс с третьим, и т.д.
Нельзя, потому что если я перенаправлю ввод/вывод в pipe то нормально общаться будут только 2 процесса, так как ввод и вывод один на все процессы. А если будут общаться несколько процессов, то ни будут писать на один и тот же поток ввода и вывода. Т.е. получиться абра кадабра. Один поток пишет в трубу, другой должен прочитать, а на самом деле будет читать не один, а несколько — раз конвееров болше чем один и поток один на всех. И здесь неизвестно кто получить эту порцию информации.
Может я что-то не допонимаю….
Вообще говоря я ошибся, когда мы реализуем конвееры мы создаем pipe, а он может быть для каждого свой
/* Подскажите, если кто знает, почему так не работает, а если убрать восклицательный занк при проверки 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;
}
Это вроде работает, но есть странности не пойму в чем.
#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;
}
То есть она выводит broken pipe как и у меня, без восклицательного знака.
Я убрал close(0) и close(1), поэтому программа не выдает broken pipe, она работает, но во-первых нет гарантии, что сначала выполнится родитель, а потом ребенок. во-вторых перестает корректно работать консоль после запуска программы. Как я понимаю — это результат некорректной работы с потоками ввода/вывода.
Именно, я такое тоже замечал, но хотелось бы узнать почему?
Вроде разобрался 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 секунду. Вообще конвейер — это последовательное выполнение процессов, а здесь получается паралельное.
Проблема с выводом возникает из-за того что bash ожидает вывода от родительского процесса, а он при завершении передает свой вывод потомку о котором bash ничего не знает. На мой взгдляд это глюк работоспособность shell не должна зависеть от того, что из под него запускается.
Конвеер — это паралельное выполнение. Когда запусается первая программа она выводит на экран, а мы перенаправляем ее на стандартный ввод другой программы, то есть другая программа обязана запуститься вместе с первой, иначе не кому передавать данные, а в буфер — это просто не эффективно: буфер всегда имеет ограничения, и приходится копировать сначало в буфер, а потом запускать другую проги и копировать из буфера в другую программу. И так далее по цепочки, все программы в конвеере должны работать паралельно.
Конечно не должно. Shell запускает пру fork + exec. И после этого формируются всякие вещи относящиеся к программе именно ядром и к этому shell не имеет никакого отношения.
Я запускаю через tcsh
Если бы работало с ошибкой из-за, того что more запускается раньше, тогда бы можно было бы решить эту проблему, поставив sleep() перед execl, который вызовает more. Но это не проходит.
Вот что я нашел в исходниках по 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
*/
Проблемы могут быть, если bash работает не верно с каналами, но bash работает верно с каналами
Второй процесс ждет вывода первого поэтому получается последовательная работа процессов. Согласен, буфер не должен использоваться — это не эффективно и может привести к проблемам.