По сути, это то, что делает оболочка, если создает цепочку перенаправления, то есть что-то вроде
ls | grep foo | sort | uniq
Есть несколько превосходных вводных текстов по программированию Unix, в которых простая оболочка реализована посредством книги. И одной из задач оболочки является перенаправление. Одна из этих книг - «Программирование приложений Linux» Майкла К. Джонсона и Эрика У. Троана.
Домашняя страница книги: http://ladweb.net/
Чтобы построить цепочку перенаправлений для N процессов, вам нужны N-1 трубы. Для каждого перенаправления вы создаете канал, используя системный вызов pipe(int fds[2])
. После fork()
ing, но перед execv
ing, используйте dup2(int from, int to)
, чтобы «соединить» конец трубы со стандартным входом (0) или стандартным выходом каждого процесса. Вот слишком упрощенный код без проверки ошибок:
int pipe_A[2];
int pipe_B[2];
pipe(pipe_A);
pipe(pipe_B);
pid_t pid_A, pid_B, pid_C;
if( !(pid_A = fork()) ) {
dup2(pipe_A[1], 1); /* redirect standard output to pipe_A write end */
execv(...);
}
if( !(pid_B = fork()) ) {
dup2(pipe_A[0], 0); /* redirect standard input to pipe_A read end */
dup2(pipe_B[1], 1); /* redirect standard output to pipe_B write end */
execv(...);
}
if( !(pid_C = fork()) ) {
dup2(pipe_B[0], 0); /* redirect standard input to pipe_B read end */
execv(...);
}
Обратите внимание, что индексы массива канала были выбраны таким образом, что они отражают стандартные дескрипторы файлов ввода / вывода, если они используются для перенаправления stdio. Этот выбор не был произвольным.
Конечно, вы можете подключить каналы к любым файловым дескрипторам (например, есть некоторые приложения, которые ожидают, что их родительский элемент откроется, скажем, fd 3 и 4, подключенные к каналам), и большинство оболочек также поддерживают это напрямую (например, 1> & 3 перенаправит стандартный вывод в fd 3). Однако индексы массива для pipe(int fds[2])
равны 0 и 1, конечно. Я просто говорю это, потому что у меня было несколько студентов-программистов, которые бездумно брали целевые fds и для массива pipe syscall.
Чтобы дождаться, пока все дети закончат использовать waitpid(-1, NULL, 0)
- я думаю, что это -1 означает, что имел в виду мой предшествующий, что означает: дождаться завершения всех дочерних процессов. Другой вариант вызывал wait()
в цикле, который будет возвращать pid только что завершенного дочернего элемента. Если вызвать снова, и ребенок все еще работает, он снова заблокируется. Если ребенка не осталось, он вернет -1; Я предпочитаю решение waitpid
.