Роббинс Арнольд
Шрифт:
107 if ((kids[nkids] = fork) == 0) {
108 sleep(3);
109 _exit(0);
110 }
111 kidsleft++; /* <<< Added */
112 }
113
114 /* sleep(5); /* дать потомкам шанс завершиться */
115
116 while (kidsleft > 0) { /* <<< Добавлено */
117 printf("waiting for signals\n");
118 sigsuspend(&emptyset);
119 } /* <<< Добавлено */
120
121 return 0;
122 }
Здесь код также почти идентичен. Строки 104 и 114 закомментированы из предыдущей версии, а строки 111, 116 и 119 добавлены. Удивительно, при запуске поведение меняется в зависимости от версии ядра!
$ uname -a /* Отобразить версию системы */
Linux example1 2.4.20-8 #1 Thu Mar 13 17:54:28 EST 2003 i686 i686 i386 GNU/Linux
$ ch10-reap2 /* Запустить программу */
waiting for signals
Entered childhandler /* Опрос одного потомка */
reaped process 2702
pid 2703 not available yet
pid 2704 not available yet
pid 2705 not available yet
pid 27 06 not available yet
Exited childhandler
waiting for signals
Entered childhandler /* И следующего */
reaped process 2703
pid 2704 not available yet
pid 2705 not available yet
pid 2706 not available yet
Exited childhandler
waiting for signals
Entered childhandler /* И так далее */
reaped process 2704
pid 2705 not available yet
pid 2706 not available yet
Exited childhandler
waiting for signals
Entered childhandler
reaped process 2705
pid 2706 not available yet
Exited childhandler
waiting for signals
Entered childhandler
reaped process 2706
Exited childhandler
В данном примере на каждый процесс поступает ровно один
SIGCHLD
! Хотя это прекрасно и полностью воспроизводимо на этой системе, это также необычно. Как на более раннем, так и на более позднем ядре и на Solaris программа получает один сигнал для более чем одного потомка: $ uname -a /* Отобразить версию системы */
Linux example2 2.4.22-1.2115.npt1 #1 Wed Oct 29 15:42:51 EST 2003 i686 i686 i386 GNU/Linux
$ ch10-reap2 /* Запуск программы */
waiting for signals
Entered childhandler /* Обработчик сигнала вызван лишь однажды */
reaped process 9564
reaped process 9565
reaped process 9566
reaped process 9567
reaped process 9568
Exited childhandler
ЗАМЕЧАНИЕ. В коде для
ch10-reap2.c
есть один важный дефект — состояние гонки. Взгляните еще раз на строки 106–112 в ch10-reap2.c
. Что случится, если SIGCHLD
появится при исполнении этого кода? Массив kids
и переменные nkids
и kidsleft
могут оказаться разрушенными: код в main
добавляет новый процесс, но обработчик сигнала вычитает один. Этот пример кода является отличным примером критического раздела; он не должен прерываться при исполнении. Правильным способом работы с этим кодом является заключение его между вызовами, которые сначала блокируют, а затем разблокируют
SIGCHLD
. 10.8.3.3. Строгий родительский контроль
Структура
siginfo_t
и перехватчик сигнала с тремя аргументами дают возможность узнать, что случилось с потомком. Для SIGCHLD поле si_code
структуры siginfo_t
указывает причину посылки сигнала (остановка, возобновление, завершение порожденного процесса и т.д.). В табл. 10.5 представлен полный список значений. Все они определены в качестве расширения XSI стандарта POSIX. Следующая программа,
ch10-status.c
, демонстрирует использование структуры siginfo_t
.