Возможно ли иметь канал между двумя дочерними процессами, созданными одним и тем же родителем (LINUX, POSIX) - PullRequest
8 голосов
/ 08 марта 2011

У меня есть несколько дочерних элементов, «разветвленных» одним и тем же родителем, и я пытаюсь построить pipe связь между всеми этими дочерними процессами, как структуру связанного списка. Ребенок 1 отправляет данные ребенку 2, ребенок 2 ребенку 3 .... ребенок N ребенку 1. Есть ли какой-нибудь правильный способ сделать это?

Кроме того, если я создаю и сообщаю между процессами, как я заставляю родителя «ждать» весь процесс, чтобы завершить свою работу, так как wait() или waitpid() ждет первого завершенного процесса, но мне нужно ждать их всех. Это еще один вопрос, который возникает.

Спасибо ...

Ответы [ 3 ]

18 голосов
/ 08 марта 2011

По сути, это то, что делает оболочка, если создает цепочку перенаправления, то есть что-то вроде

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.

3 голосов
/ 08 марта 2011

Да, это довольно просто, вам просто нужно создать все каналы в родительском элементе, и не забудьте закрыть каналы / концы каналов в дочернем элементе (ren), в которых они вам не нужны.

Оставлять FD открытых каналов у детей, которые их не используют, - НЕДОСТАТОК, поскольку это может заставить других вечно ждать конца канала. Все авторы должны закрыть, прежде чем читатель получит EOF.

2 голосов
/ 08 марта 2011

Сначала создайте все каналы, затем породите все дочерние элементы с соответствующими концами каналов в FD 0 и 1.

Что касается ожидания, просто продолжайте ждать, пока он не вернет -1.

...