Вход/Регистрация
Разработка приложений в среде Linux. Второе издание
вернуться

Троан Эрик В.

Шрифт:

Очевидно, что должно сделать ядро — так это прервать процесс и запустить обработчик сигнала сначала. Это порождает две проблемы. Первая — обработчик сигнала должен работать правильно, если он вызван тогда, когда уже сам работает. Хотя само по себе это и не сложно, но обработчики сигналов, которые манипулируют общепрограммными ресурсами, такими как глобальные структуры данных или файлы, должны быть написаны очень аккуратно. Функции, которые ведут себя правильно, когда вызваны подобным образом, называются реентерабельными функциями [54] .

54

Необходимость в реентерабельных функциях не ограничивается обработчиками сигналов. Многопоточные приложения должны с большой осторожностью относиться к обеспечению реентерабельности и блокировкам.

Простая техника блокировки, которая достаточна для координации доступа к данным между конкурирующими процессами, не обеспечивает реентерабельности. Например, техника блокировки файлов, представленная в главе 13, не может использоваться для того, чтобы позволить обработчику сигналов, манипулирующему файлами данных, быть реентерабельным. Когда обработчик сигналов вызывается первый раз, он может просто изумительно заблокировать файл данных и начать запись в него. Если же этот обработчик будет прерван другим сигналом, в то время пока он удерживает блокировку, второй вызов обработчика не сможет блокировать файл, поскольку его блокировал первый вызов. К сожалению, вызов, который удерживает блокировку, приостанавливается до тех пор, пока второй вызов, который будет ожидать разблокировки, завершит работу.

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

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

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

К сожалению, именно такая модель сигналов используется в ANSI/ISO-стандарте С [55] . Хотя программные интерфейсы надежных сигналов, в которых исправлен этот недостаток, уже широко распространены, стандартизация ненадежных сигналов в ANSI/ISO, видимо, останется навсегда.

12.1.3. Надежные сигналы

Реализация BSD для решения проблемы множества сигналов полагается на простое ожидание завершения работы каждого обработчика сигналов в процессе перед доставкой следующего. Это гарантирует то, что каждый сигнал будет рано или поздно обработан, а также исключает риск переполнения стека. Вспомним, что когда ядро удерживает сигнал для отложенной доставки, сигнал называется ожидающим (pending).

55

Вообще-то, не совсем так. Модель обработки сигналов ANSI/ISO С специфицирована немного иначе, чем мы ее представили. Однако она допускает сброс обработчика в значение

SIG_DFL
перед доставкой сигнала, что делает функции
signal
в ASNI/ISO С ненадежными.

Однако если процессу отправлен сигнал

SIGЧТО-ТО
, в то время, как
SIGЧТО
– ТО уже находится в состоянии ожидания, то в этом случае процессу доставляется только первый из них. У процесса нет никакой возможности узнать, сколько раз один и тот же сигнал был отправлен ему, поскольку множество одинаковых сигналов подряд воспринимаются как один. Обычно это не представляет собой проблему. Поскольку сигнал не несет в себе никакой информации помимо собственно номера сигнала, двойная посылка сигнала за очень короткий отрезок времени может быть воспринята как одиночная, потому если программа примет сигнал только однажды, это не имеет особого значения. Это отличается от варианта с обработкой второго сигнала по умолчанию (что делается при ненадежной схеме обработки сигналов) [56] .

56

Спецификация POSIX Real Time Signal позволяет некоторые сигналам ставить в очередь, и для сигналов, работающих с ограниченными объемами данных, существенно изменяет эту модель. Сигналы реального времени обсуждаются ближе к концу этой главы.

Идея об автоматической блокировке сигналов была расширена для того, чтобы позволить процессам блокировать сигналы явным образом. Это облегчает защиту критичных участков кода, в то же время гарантируя обработку всех отправленных сигналов. Такая защита позволяет обработчикам сигналов манипулировать структурами данных, которые поддерживаются другими участками кода, за счет простой синхронизации.

Хотя BSD представляет адаптированную версию модели сигналов POSIX, комитет по стандартизации POSIX упростил ее для системных вызовов, с тем чтобы модифицировать диспозицию групп сигналов, предлагая новые системные вызовы для оперирования наборами сигналов. Наборы сигналов представлены типом данных

sigset_t
, и для манипулирования ими предусмотрен набор макросов [57] .

57

Это аналогично типу

fd_set
, который используется системным вызовом
select
, описанным в главе 13.

12.1.4. Сигналы и системные вызовы

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

read
, чтобы возвратить ввод терминала. Когда системный администратор посылает процессу сигнал
SIGTERM
(нормальный сигнал, посылаемый командой
kill
, позволяющий процессу завершиться чисто), то процесс может обработать его, как описано ниже.

1. Не пытаться перехватить сигнал и быть прерванным ядром (обработка

SIGTERM
по умолчанию). Это оставляет пользовательский терминал в нестандартной конфигурации, затрудняя ему продолжение работы.

2. Перехватить сигнал, очистить терминал с помощью обработчика этого сигнала, затем выйти. Хотя это кажется привлекательным, в сложных программах трудно написать такой обработчик, который знал бы достаточно о том, что делает программа в момент прерывания, чтобы правильно выполнить очистку.

  • Читать дальше
  • 1
  • ...
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: