Linux программирование в примерах - Роббинс Арнольд
Шрифт:
Интервал:
Закладка:
Когда левый потомок завершает работу, он заканчивается. Система после этого закрывает все его дескрипторы файлов. Когда это случается, правый потомок получает в конечном счете уведомление конца файла и тоже может завершить работу и выйти.
Следующая программа, ch09-pipeline.c, создает эквивалент следующего конвейера оболочки:
$ <b>echo hi there | sed s/hi/hello/g</b>
hello there
Вот программа:
1 /* ch09-pipeline.c --- ответвляет два процесса в их собственный конвейер.
2 Для краткости проверка ошибок сведена к минимуму. */
3
4 #include <stdio.h>
5 #include <errno.h>
6 #include <sys/types.h>
7 #include <sys/wait.h>
8 #include <unistd.h>
9
10 int pipefd[2];
11
12 extern void left_child(void), right_child(void);
13
14 /* main --- порождение процессов и ожидание их завершения */
15
16 int main(int argc, char **argv)
17 {
18 pid_t left_pid, right_pid;
19 pid_t ret;
20 int status;
21
22 if (pipe(pipefd) < 0) { /* создать канал в самом начале */
23 perror("pipe");
24 exit(1);
25 }
26
27 if ((left_pid = fork()) < 0) { /* порождение левого потомка */
28 perror("fork");
29 exit(1);
30 } else if (left_pid == 0)
31 left_child();
32
33 if ((right_pid = fork()) < 0) { /* порождение правого потомка */
34 perror("fork");
35 exit(1);
36 } else if (right_pid == 0)
37 right_child();
38
39 close(pipefd[0])); /* закрыть родительские копии канала */
40 close(pipefd[1]);
41
42 while ((ret = wait(&status)) > 0) { /* wait for children */
43 if (ret == left_pid)
44 printf("left child terminated, status: %xn", status);
45 else if (ret == right_pid)
46 printf("right child terminated, status: %xn", status);
47 else
48 printf("yow! unknown child %d terminated, status %xn",
49 ret, status);
50 }
51
52 return 0;
53 }
Строки 22–25 создают канал. Это должно быть сделано в самом начале.
Строки 27–31 создают левого потомка, а строки 33–37 создают правого потомка. В обоих случаях родитель продолжает линейное исполнение ветви main() до тех пор, пока порожденный процесс не вызовет соответствующую функцию для манипулирования дескрипторами файла и осуществления exec.
Строки 39–40 закрывают родительскую копию канала.
Строки 42–50 в цикле ожидают потомков, пока wait() не вернет ошибку.
55 /* left_child --- осуществляет работу левого потомка */
56
57 void left_child(void)
58 {
59 static char *left_argv[] = { "echo", "hi", "there", NULL };
60
61 close(pipefd[0]);
62 close(1);
63 dup(pipefd[1]);
64 close(pipefd[1]);
65
66 execvp("echo", left_argv);
67 _exit(errno == ENOENT ? 127 : 126);
68 }
69
70 /* right_child --- осуществляет работу правого потомка */
71
72 void right_child(void)
73 {
74 static char *right_argv[] = { "sed", "s/hi/hello/g", NULL };
75
76 close(pipefd[1]);
77 close(0);
78 dup(pipefd[0]);
79 close(pipefd[0]));
80
81 execvp("sed", right_argv);
82 _exit(errno == ENOENT ? 127 : 126);
83 }
Строки 57–68 являются кодом для левого потомка. Процедура следует приведенным выше шагам, закрывая ненужный конец канала, закрывая первоначальный стандартный вывод, помещая с помощью dup() записываемый конец канала на номер 1 и закрывая затем первоначальный записываемый конец. В этот момент строка 66 вызывает execvp(), и если она завершается неудачей, строка 67 вызывает _exit(). (Помните, что строка 67 никогда не выполняется, если execvp() завершается удачно.)