Kniga-Online.club
» » » » Арнольд Роббинс - Linux программирование в примерах

Арнольд Роббинс - Linux программирование в примерах

Читать бесплатно Арнольд Роббинс - Linux программирование в примерах. Жанр: Программное обеспечение издательство -, год 2004. Так же читаем полные версии (весь текст) онлайн без регистрации и SMS на сайте kniga-online.club или прочесть краткое содержание, предисловие (аннотацию), описание и ознакомиться с отзывами (комментариями) о произведении.
Перейти на страницу:

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

Следующая программа, ch09-pipeline.c, создает эквивалент следующего конвейера оболочки:

$ echo hi there | sed s/hi/hello/g

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() завершается удачно.)

Строки 72–83 делают подобные же шаги для правого потомка. Вот что происходит при запуске:

$ ch09-pipeline /* Запуск программы */

left child terminated, status: 0 /* Левый потомок завершается до вывода (!) */

hello there /* Вывод от правого потомка */

right child terminated, status: 0

$ ch09-pipeline /* Повторный запуск программы */

hello there /* Вывод от правого потомка и ... */

right child terminated, status: 0 /* Правый потомок завершается до левого */

left child terminated, status: 0

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

Весь процесс показан на рис. 9.5.

Рис. 9.5. Создание конвейера родителем

На рис. 9.5 (а) изображена ситуация после создания родителем канала (строки 22–25) и двух порожденных процессов (строки 27–37).

На рис. 9.5 (b) показана ситуация после закрытия родителем канала (строки 39–40) и начала ожидания порожденных процессов (строки 42–50). Каждый порожденный процесс поместил канал на место стандартного вывода (левый потомок, строки 61–63) и стандартного ввода (строки 76–78).

Наконец, рис. 9.5 (с) изображает ситуацию после закрытия потомками первоначального канала (строки 64 и 79) и вызова execvp() (строки 66 и 81).

9.4.2. Создание нелинейных конвейеров: /dev/fd/XX

Многие современные системы Unix, включая GNU/Linux, поддерживают в каталоге /dev/fd[98] специальные файлы. Эти файлы представляют дескрипторы открытых файлов с именами /dev/fd/0, /dev/fd/1 и т.д. Передача такого имени функции open() возвращает новый дескриптор файла, что в сущности является тем же самым, что и вызов dup() для данного номера дескриптора.

Эти специальные файлы находят свое применение на уровне оболочки: Bash, ksh88 (некоторые версии) и ksh93 предоставляют возможность замещения процесса (process substitution), что позволяет создавать нелинейные конвейеры. На уровне оболочки для входного конвейера используется запись '<(...)', а для выходного конвейера запись '>(...)'. Например, предположим, вам нужно применить команду diff к выводу двух команд. Обычно вам пришлось бы использовать временные файлы:

command1 > /tmp/out.$$.1

command2 > /tmp/out.$$.2

diff /tmp/out.$$.1 /tmp/out.$$.2

rm /tmp/out.$$.1 /tmp/out.$$.2

С замещением процессов это выглядит следующим образом:

diff <(command1) <(command2)

Не надо никаких беспорядочных файлов для временного запоминания и удаления. Например, следующая команда показывает, что наш домашний каталог является ссылкой на другой каталог:

$ diff <(pwd) <(/bin/pwd)

1c1

< /home/arnold/work/prenhall/progex

---

> /d/home/arnold/work/prenhall/progex

Незамысловатая команда pwd является встроенной в оболочку: она выводит текущий логический путь, который управляется оболочкой с помощью команды cd. Программа /bin/pwd осуществляет обход физической файловой системы для вывода имени пути.

Как выглядит замещение процессов? Оболочка создает вспомогательные команды[99] ('pwd' и '/bin/pwd'). Выход каждой из них подсоединяется к каналу, причем читаемый конец открыт в дескрипторе нового файла для главного процесса ('diff'). Затем оболочка передает главному процессу имена файлов в /dev/fd в качестве аргументов командной строки. Мы можем увидеть это, включив в оболочке трассировку исполнения.

$ set -х /* Включить трассировку исполнения */

$ diff <(pwd) <(/bin/pwd) /* Запустить команду */

+ diff /dev/fd/63 /dev/fd/62 /* Трассировка оболочки: главная,

 программа, обратите внимание на аргументы */

++ pwd /* Трассировка оболочки: вспомогательные программы */

++ /bin/pwd

1c1 /* Вывод diff */

< /home/arnold/work/prenhall/progex

---

> /d/home/arnold/work/prenhall/progex

Это показано на рис. 9.6.

Рис. 9.6. Замещение процесса

Если на вашей системе есть /dev/fd, вы также можете использовать преимущества этой возможности. Однако, будьте осторожны и задокументируйте то, что вы делаете. Манипуляции с дескриптором файла на уровне С значительно менее прозрачны, чем соответствующие записи оболочки!

9.4.3. Управление атрибутами файла: fcntl()

Перейти на страницу:

Арнольд Роббинс читать все книги автора по порядку

Арнольд Роббинс - все книги автора в одном месте читать по порядку полные версии на сайте онлайн библиотеки kniga-online.club.


Linux программирование в примерах отзывы

Отзывы читателей о книге Linux программирование в примерах, автор: Арнольд Роббинс. Читайте комментарии и мнения людей о произведении.


Уважаемые читатели и просто посетители нашей библиотеки! Просим Вас придерживаться определенных правил при комментировании литературных произведений.

  • 1. Просьба отказаться от дискриминационных высказываний. Мы защищаем право наших читателей свободно выражать свою точку зрения. Вместе с тем мы не терпим агрессии. На сайте запрещено оставлять комментарий, который содержит унизительные высказывания или призывы к насилию по отношению к отдельным лицам или группам людей на основании их расы, этнического происхождения, вероисповедания, недееспособности, пола, возраста, статуса ветерана, касты или сексуальной ориентации.
  • 2. Просьба отказаться от оскорблений, угроз и запугиваний.
  • 3. Просьба отказаться от нецензурной лексики.
  • 4. Просьба вести себя максимально корректно как по отношению к авторам, так и по отношению к другим читателям и их комментариям.

Надеемся на Ваше понимание и благоразумие. С уважением, администратор kniga-online.


Прокомментировать
Подтвердите что вы не робот:*
Подтвердите что вы не робот:*