Категории
Самые читаемые
PochitayKnigi » Компьютеры и Интернет » Программное обеспечение » Linux программирование в примерах - Арнольд Роббинс

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

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

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 94 95 96 97 98 99 100 101 102 ... 165
Перейти на страницу:

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().

Вот что происходит при запуске:

$ ch10-status /* Запуск программы */

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(), остаются заблокированными и после вызова. Другими словами, новая программа наследует маску сигналов существующего процесса.

• Любые ожидающие сигналы (те, которые появились, но были заблокированы) сбрасываются. Новая программа не может их получить.

• Временной интервал, остающийся для alarm(), сохраняется на своем месте. (Другими словами, если процесс устанавливает alarm, а затем непосредственно вызывает exec(), новый образ в конечном счете получит SIGALARM. Если он сначала вызывает fork(), родитель сохраняет установки alarm, тогда как потомок, вызывающий exec(), не сохраняет.

ЗАМЕЧАНИЕ. Многие, если не все. программы предполагают, что сигналы инициализированы действиями по умолчанию и что заблокированных сигналов нет. Таким образом, особенно если не вы писали программу, запускаемую с помощью exec(), можно разблокировать перед вызовам exec() все сигналы

10.10. Резюме

«Наша история до настоящего времени, эпизод III»

- Арнольд Роббинс (Arnold Robbins) -

• Интерфейсы обработки сигналов развились от простых, но подверженных состояниям гонок, до сложных, но надежных. К сожалению, множественность интерфейсов затрудняет их изучение по сравнению с другими API Linux/Unix.

• У каждого сигнала есть связанное с ним действие. Действие может быть одним из следующих: игнорирование сигнала; выполнение действия системы по умолчанию или вызов предоставленного пользователем обработчика. Действие системы по умолчанию, в свою очередь, является одним из следующих: игнорирование сигнала, завершение процесса; завершение процесса с созданием его образа; остановка процесса или возобновление процесса, если он остановлен.

• signal() и raise() стандартизованы ISO С. signal() управляет действиями для определенных сигналов; raise() посылает сигнал текущему процессу. Остаются ли обработчики сигналов установленными после вызова или сбрасываются для действия по умолчанию, зависит от реализации, signal() и raise() являются простейшими интерфейсами, для многих приложений их достаточно.

• POSIX определяет функцию bsd_signal(), которая подобна signal(), но гарантирует, что обработчик остается установленным.

• Действия, происходящие после возвращения из обработчика, варьируют в зависимости от системы. Традиционные системы (V7, Solaris, возможно, и другие) восстанавливают действие сигнала по умолчанию. На этих системах прерванный системный вызов возвращает -1, устанавливая в errno значение EINTR. Системы BSD оставляют обработчик установленным и возвращают -1 с errno, содержащим EINTR, лишь в случае, когда не было перемещения данных; в противном случае, системный вызов запускается повторно.

• GNU/Linux придерживается POSIX, который похож, но не идентичен с BSD. Если не было перемещения данных, системный вызов возвращает -1/EINTR. В противном случае он возвращает объем перемещенных данных. Поведение BSD «всегда повторный запуск» доступно через интерфейс sigaction(), но он не является действием по умолчанию.

• Обработчики сигналов, используемые с signal(), подвержены состояниям гонок. Внутри обработчиков сигналов должны использоваться исключительно переменные типа volatile sig_atomic_t. (В целях упрощения в некоторых из наших примеров мы не всегда следовали этому правилу.) Таким же образом, для вызова из обработчика сигналов безопасными являются лишь функции из табл. 10.2.

• Первоначальной попыткой создания надежных сигналов был API сигналов System V Release 3 (скопированный из BSD 4.0). Не используйте его в новом коде.

• POSIX API содержит множество компонентов:

  • маску сигналов процесса, перечисляющую текущие заблокированные сигналы;

  • тип sigset_t для представления масок сигналов, и функции sigfillset(), sigemptyset(), sigaddset(), sigdelset() и sigismember() для работы с ними;

  • функцию sigprocmask() для установки и получения маски сигналов процесса,

  • функцию sigpending() для получения набора ожидающих сигналов;

  • API sigaction() и struct sigaction во всем их великолепии.

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

• Механизмами POSIX для посылки сигналов являются kill() и killpg(). Они отличаются от raise() в двух отношениях: (1) одни процесс может послать сигнал другому процессу или целой группе процессов (конечно, с проверкой прав доступа), и (2) посылка сигнала 0 ничего не посылает, но осуществляет проверку. Таким образом, эти функции предоставляют способ проверки наличия определенного процесса или группы процессов и возможность посылки ему (им) сигнала.

1 ... 94 95 96 97 98 99 100 101 102 ... 165
Перейти на страницу:
Тут вы можете бесплатно читать книгу Linux программирование в примерах - Арнольд Роббинс.
Комментарии