Linux программирование в примерах - Роббинс Арнольд
Шрифт:
Интервал:
Закладка:
117 }
118
119 return 0;
120 }
Программа main() все устанавливает. Строки 100–103 помещают на место обработчик. Строка 100 устанавливает флаг SA_SIGINFO таким образом, что используется обработчик с тремя аргументами. Строки 105–108 блокируют SIGCHLD.
Строка 110 создает порожденный процесс. Строки 113–117 продолжаются в родителе, используя для ожидания входящих сигналов sigsuspend().
123 /* manage --- разрешение различных событий, которые могут случиться с потомком */
124
125 void manage(siginfo_t *si)
126 {
127 char buf[100];
128
129 switch (si->si_code) {
130 case CLD_STOPPED:
131 write(1, "tchild stopped, restartingn", 27);
132 kill(si->si_pid, SIGCONT);
133 break;
134
135 case CLD_CONTINUED: /* not sent on Linux */
136 write(1, "tchild continuedn", 17);
137 break;
138
139 case CLD_EXITED:
140 strcpy(buf, "tchild exited with status ");
141 strcat(buf, format_num(si->si_status));
142 strcat(buf, "n");
143 write(1, buf, strlen(buf));
144 exit(0); /* we're done */
145 break;
146
147 case CLD_DUMPED:
148 write(1, "tchild dumpedn", 14);
149 break;
150
151 case CLD_KILLED:
152 write(1, " tchild killedn", 14);
153 break;
154
155 case CLD_TRAPPED:
156 write(1, "tchild trappedn", 15);
157 break;
158 }
159 }
Посредством функции manage() родитель обрабатывает изменение состояния в порожденном процессе, manage() вызывается, когда изменяется состояние и когда порожденный процесс завершился.
Строки 130–133 обрабатывают случай, когда потомок остановился; родитель возобновляет его, посылая SIGCONT.
Строки 135–137 выводят уведомление о возобновлении потомка. Это событие на системах GNU/Linux не происходит, и стандарт POSIX использует в этом случае невыразительный язык, просто говоря, что это событие может появиться, а не появится.
Строки 139–145 обрабатывают случай, когда порожденный процесс завершается, выводя статус завершения. Для этой программы родитель также все сделал, поэтому код завершается, хотя в более крупной программе это не то действие, которое должно быть сделано.
Другие случаи более специализированные. В случае события CLD_KILLED для получения дополнительных сведений было бы полезным значение status, заполненной функцией waitpid().
Вот что происходит при запуске:
$ <b>ch10-status</b> /* Запуск программы */
waiting for signals
Entered childhandler /* Вход в обработчик сигнала */
pid 24279 changed status
child stopped, restarting /* Обработчик действует */
Exited childhandler
waiting for signals
---> child restarted <--- /* Из потомка */
Entered childhandler
reaped process 24279 /* Обработчик родителя опрашивает потомка */
child exited with status 42
К сожалению, поскольку нет способа гарантировать доставку по одному SIGCHLD на каждый процесс, ваша программа должна быть готова восстановить несколько потомков за один проход.
10.9. Сигналы, передающиеся через fork() и exec()
Когда программа вызывает fork(), ситуация с сигналами в порожденном процессе почти идентична ситуации в родительском процессе. Установленные обработчики остаются на месте, заблокированные сигналы остаются заблокированными и т.д. Однако, любые ожидающие в родителе сигналы в потомке сбрасываются, включая установленный с помощью alarm() временной интервал. Это просто, и это имеет смысл.
Когда процесс вызывает одну из функций exec(), положение в новой программе следующее:
• Сигналы с установленным действием по умолчанию остаются с этим действием по умолчанию.
• Все перехваченные сигналы сбрасываются в состояние с действием по умолчанию.
• Сигналы, которые игнорируются, продолжают игнорироваться. Особым случаем является SIGCHLD. Если SIGCHLD до вызова exec() игнорировался, он может игнорироваться также и после вызова. В качестве альтернативы для него может быть восстановлено действие по умолчанию. То, что происходит на самом деле, стандартом POSIX намеренно не определяется. (Справочные страницы GNU/Linux не определяют, что делает Linux, и поскольку POSIX оставляет это не определенным, любой код, который вы пишете для использования SIGCHLD, должен быть подготовлен для обработки любого случая.)
• Сигналы, заблокированные до вызова exec(), остаются заблокированными и после вызова. Другими словами, новая программа наследует маску сигналов существующего процесса.
• Любые ожидающие сигналы (те, которые появились, но были заблокированы) сбрасываются. Новая программа не может их получить.