Механизм управления сигналами в программах с несколькими нитями предназначен для предотвращения конфликтов. При этом сохраняется совместимость с обычными программами: сигналы в программах с несколькими нитями представляют собой расширения сигналов, применяемых в традиционных программах с одной нитью. Программы обработки сигналов, написанные для систем с одной нитью, будут правильно работать в AIX версии 4.
Управление сигналами в процессах с несколькими нитями осуществляется совместно процессом и нитями. Ниже перечислены элементы управления сигналами:
Кроме того, библиотека нитей предоставляет новую функцию для ожидания асинхронно поступающих сигналов и вводит новый набор приемов реализации такого ожидания.
Более подробно управление сигналами рассматривается в следующих разделах:
Обработчики сигналов относятся к уровню процесса. Настоятельно рекомендуется считывать и устанавливать значения обработчиков сигналов только с помощью функции sigaction. Другие функции не будут поддерживаться в будущих версиях.
Так как список обработчиков сигналов обслуживается на уровне процесса, любая нить процесса может изменять его. Если две нити указали один и тот же сигнал в обработчике сигналов, то вторая нить, вызвавшая функцию sigaction, переопределит параметры, заданные первой нитью; а предсказать порядок, в котором будут выполняться нити, в общем случае невозможно.
Маски сигналов обслуживаются на уровне нитей. У каждой нити может быть свой набор сигналов, доставку которых она заблокирует. Для работы с маской сигналов вызывающей нити следует применять функцию sigthreadmask. Функцию sigprocmask не следует применять в программах с несколькими нитями - это может привести к непредсказуемым результатам.
Функция sigthreadmask очень похожа на sigprocmask. Параметры и способы применения обеих функций одинаковы. При переработке уже существующей программы в программу, работающую с библиотекой нитей, следует просто заменить вызовы sigprocmask на sigthreadmask.
Сигналы, относящиеся к конкретной нити, например, сигнал аппаратной неполадки, передаются в ту нить, которая послужила причиной их генерации. Сигналы, связанные с ИД процесса, ИД группы процессов или асинхронным событием (например, рабочие сигналы терминала), передаются в процесс.
Функция pthread_kill передает сигнал в нить. Поскольку ИД нитей идентифицируют их только в рамках процесса, эта функция может передавать сигналы в нити только данного процесса.
Функция kill (а следовательно, и команда kill) передает сигнал в процесс. Нить может передать сигнал Signal в процесс, запустивший ее, посредством следующего вызова:
kill(getpid(), Signal);
Функция raise не может применяться для передачи сигнала в процесс, запустивший вызывающую нить. Функция raise передает сигнал в вызывающую нить, как в следующем случае:
pthread_kill(pthread_self(), Signal);
Таким образом обеспечивается передача сигнала нити, вызвавшей функцию raise. Следовательно, библиотечные функции, предназначенные для применения в программах с одной нитью, можно легко перенести в систему с несколькими нитями - функция raise обычно применяется для передачи сигнала инициатору вызова.
Функция alarm запрашивает последующую передачу сигнала в процесс; при этом аварийные состояния обрабатываются на уровне процесса. Следовательно, последняя нить, вызвавшая функцию alarm, уничтожает соответствующие параметры для других нитей процесса. В программе с несколькими нитями сигнал SIGALRM не всегда передается в нить, вызвавшую функцию alarm. Может случиться, что вызывающая нить уже завершена и поэтому не может принять сигнал.
Обработчики сигналов вызываются нитями, в которые поступили сигналы. Для определения ИД нити обработчик сигнала может вызвать функцию pthread_self. В библиотеке нитей реализован ряд ограничений для обработчиков сигналов:
Как правило, для организации ожидания сигнала в программе вызывается обработчик сигналов, который вызывает функцию longjmp для продолжения работы в момент вызова соответствующей функции setjmp. В программах с несколькими нитями такой подход неприменим, так как сигнал может быть доставлен не в ту нить, которая вызвала функцию setjmp: в этом случае обработчик сигналов был бы вызван из неправильной нити.
Для того чтобы нить могла ожидать поступления асинхронного сигнала, в библиотеке нитей предусмотрена функция sigwait. Функция sigwait блокирует вызывающую нить до тех пор, пока из процесса в эту нить не поступит один из ожидаемых сигналов. Для сигналов, поступления которых ожидает функция sigwait, нельзя вызывать обработчик.
Обычно в программе создается отдельная нить для ожидания поступления асинхронных сигналов. Такая нить просто вызывает функцию sigwait в цикле и обрабатывает поступающие сигналы. Приведенный ниже фрагмент кода дает пример такой нити:
sigset_t set; int sig;
sigemptyset(&set); sigaddset(&set, SIGINT); sigaddset(&set, SIGQUIT); sigaddset(&set, SIGTERM); sigthreadmask(SIG_BLOCK, &set, NULL);
while (1) { sigwait(&set, &sig); switch (sig) { case SIGINT: /* обработка прерываний */ break; case SIGQUIT: /* обработка выхода */ break; case SIGTERM: /* обработка завершения */ break; default: /* сигнал, отличный от ожидаемого */ pthread_exit((void *)-1); } }
Если функция sigwait вызвана из нескольких нитей, то при поступлении ожидаемого сигнала происходит возврат ровно из одной функции sigwait. При этом невозможно сказать заранее, какая нить будет возобновлена в этом случае. Заметим, что вызов функции sigwait образует точку завершения.
Поскольку выделенная нить в действительности не является обработчиком сигналов, она может передать сигнал о выполнении условия (Использование условных переменных) в любую другую нить. Можно создать функцию sigwait_multiple, которая будет возобновлять выполнение всех нитей, ожидающих данного сигнала. Каждая нить, вызывающая функцию sigwait_multiple, может зарегистрировать свой набор сигналов. После этого нить будет ожидать изменения переменной условия. Одна нить вызывает функцию sigwait для всех зарегистрированных сигналов. При возврате из функции sigwait устанавливается соответствующее состояние и генерируются сигналы о выполнении условий. При повторном вызове функции sigwait_multiple ожидающий вызов функции sigwait будет отменен и потом возобновлен с новым набором сигналов.
Сигнал поступает в нить, если он не должен игнорироваться. Доставка сигналов в процессах с несколькими нитями подчиняется следующим правилам:
Рассмотрим пользовательскую команду с несколькими нитями, например, команду grep. Пользователь может запустить эту команду из оболочки и затем попытаться прервать ее выполнение, передав соответствующий сигнал командой kill. Очевидно, что этот сигнал прервет весь процесс, в котором выполняется команда grep.
Если ожидающий сигнал (на уровне нити или процесса) должен игнорироваться, то он игнорируется.
Обзор взаимодействия между нитями и процессами.
Порождение и завершение процессов.
Список функций для взаимодействия между нитями и процессами.