7 pid = wait(&stat);
8 printf("child terrmnated\n", pid);
9 return;
10 }
В обработчике сигналов не рекомендуется вызов стандартных функций ввода-вывода, таких как printf, по причинам, изложенным в разделе 11.18. В данном случае мы вызываем функцию printf как средство диагностики, чтобы увидеть, когда завершается дочерний процесс.
В системах System V и Unix 98 дочерний процесс не становится зомби, если процесс задает действие SIG_IGN для SIGCHLD. К сожалению, это верно только для System V и Unix 98. В POSIX прямо сказано, что такое поведение этим стандартом не предусмотрено. Переносимый способ обработки зомби состоит в том, чтобы перехватывать сигнал SIGCHLD и вызывать функцию wait или waitpid.
Если мы откомпилируем в Solaris 9 программу, представленную в листинге 5.1, вызывая функцию
Signal
с нашим обработчиком
sig_chld
, и будем использовать функцию
signal
из системной библиотеки (вместо нашей версии, показанной в листинге 5.5), то получим следующее:
solaris %
tcpserv02 &
[2] 16939
solaris %
tcpcli01 127.0.0.1
hi there
hi there
^D
child 16942 terminated
accept error: Interrupted system call
Последовательность шагов в этом примере такова:
1. Мы завершаем работу клиента, вводя символ EOF. TCP клиента посылает сегмент FIN серверу, и сервер отвечает сегментом ACK.
2. Получение сегмента FIN доставляет EOF ожидающей функции
readline
дочернего процесса. Дочерний процесс завершается.
3. Родительский процесс блокирован в вызове функции
accept
, когда доставляется сигнал
SIGCHLD
. Функция
sig_chld
(наш обработчик сигнала) выполняется, функция
wait
получает PID дочернего процесса и статус завершения, после чего из обработчика сигнала вызывается функция
printf
. Обработчик сигнала возвращает управление.
4. Поскольку сигнал был перехвачен родительским процессом, в то время как родительский процесс был блокирован в
accept
), ядро заставляет функцию
accept
возвратить ошибку
EINTR
(прерванный системный вызов). Родительский процесс не обрабатывает эту ошибку корректно (см. листинг 5.1), поэтому функция
main
преждевременно завершается.
Цель данного примера — показать, что при написании сетевых программ, перехватывающих сигналы, необходимо получать информацию о прерванных системных вызовах и обрабатывать их. В этом специфичном для Solaris 2.5 примере функция
signal
из стандартной библиотеки С не осуществляет автоматический перезапуск прерванного вызова, то есть флаг
SA_RESTART
, установленный нами в листинге 5.5, не устанавливается функцией signal из системной библиотеки. Некоторые другие системы автоматически перезапускают прерванный системный вызов. Если мы запустим тот же пример в 4.4BSD, используя ее библиотечную версию функции
signal
, ядро перезапустит прерванный системный вызов и функция
accept
не возвратит ошибки. Одна из причин, по которой мы определяем нашу собственную версию функции
signal
и используем ее далее, — решение этой потенциальной проблемы, возникающей в различных операционных системах (см. листинг 5.5).
Кроме того, мы всегда программируем явную функцию
return
для наших обработчиков сигналов (см. листинг 5.6), даже если функция ничего не возвращает (
void
), чтобы этот оператор напоминал нам о возможности прерывания системного вызова при возврате из обработчика.
Обработка прерванных системных вызовов