• функцию sigpending()
для получения набора ожидающих сигналов;
• API sigaction()
и struct sigaction
во всем их великолепии.
Все эти возможности вместе используют блокирование сигналов и маску сигналов процесса для предоставления надежных сигналов. Более того, через различные флаги можно получить повторно запускаемые системные вызовы и более подходящие обработчики сигналов, которые получают большую информацию о причине, вызвавшей определенный сигнал (структура siginfo_t
).
• Механизмами POSIX для посылки сигналов являются kill()
и killpg()
. Они отличаются от raise()
в двух отношениях: (1) одни процесс может послать сигнал другому процессу или целой группе процессов (конечно, с проверкой прав доступа), и (2) посылка сигнала 0 ничего не посылает, но осуществляет проверку. Таким образом, эти функции предоставляют способ проверки наличия определенного процесса или группы процессов и возможность посылки ему (им) сигнала.
• Сигналы могут использоваться в качестве механизма IPC, хотя такой способ является плохим способом структурирования приложения, подверженным состояниям гонок. Если кто-то держит приставленным к вашей голове ружье, чтобы заставить вас работать таким способом, для правильной работы используйте тщательное блокирование сигналов и интерфейс sigaction()
.
• SIGALARM
и системный вызов alarm()
предоставляют низкоуровневый механизм для уведомления о прошествии определенного числа секунд, pause()
приостанавливает процесс, пока не появятся какие-нибудь сигналы, sleep()
использует их для помещения процесса в спящее состояние на заданный период времени: sleep()
и alarm()
не должны использоваться вместе. Сама pause()
создает состояние гонки; вместо этого нужно использовать блокирование сигналов и sigsuspend()
.
• Сигналы управления заданиями реализуют управление заданиями для оболочки. Большую часть времени следует оставлять их с установленными действиями по умолчанию, но полезно понимать, что иногда имеет смысл их перехватывать.
• Перехват SIGCHLD
позволяет родителю узнать, что делает порожденный им процесс. Использование 'signal(SIGCHLD, SIG_IGN)
' (или sigaction()
с SA_NOCLDWAIT
) вообще игнорирует потомков. Использование sigaction()
с SA_NOCLDSTOP
предоставляет уведомления лишь о завершении. В последнем случае, независимо от того, заблокирован SIGCHLD
или нет, обработчики сигналов для SIGCHLD
должны быть готовы немедленно обработать несколько потомков. Наконец, использование sigaction()
без SA_NOCLDSTOP
с обработчиком сигналов с тремя аргументами дает вам причину получения сигнала.
• После fork()
положение сигналов в порожденном процессе остается тем же самым, за исключением сброса ожидающих сигналов и установленных интервалов таймера. После exec()
положение несколько более сложно — в сущности, все, что может быть оставлено, остается; для всего остального восстанавливаются значения по умолчанию.
Упражнения
1. Реализуйте bsd_signal()
с использованием sigaction()
.
2. Если у вас не установлен GNU/Linux, запустите на своей системе ch10-catchint
. Является ли ваша система традиционной или BSD?
3. Реализуйте функции System V Release 3 sighold()
, sigrelse()
, sigignore()
, sigpause()
и sigset()
, использовав sigaction()
и другие подходящие функции из POSIX API.
4. Потренируйте свои навыки в жонглировании битами. В предположении, что сигнал 0 отсутствует и что имеется не более 31 сигналов, предусмотрите typedef
для sigset_t
и напишите sigemptyset()
, sigfillset()
, sigaddset()
, sigdelset()
и sigismember()
.
5. Еще немного потренируйте свои навыки жонглирования битами. Повторите предыдущее упражнение, на этот раз предположив, что наибольшим сигналом является 42.
6. Теперь, когда вы сделали предыдущие два упражнения, найдите sigemptyset()
и др. в своем заголовочном файле
. (Может потребоваться поискать их; они могут быть в #include
файлах, указанных в
.) Являются ли они макросами или функциями?
7. В разделе 10.7 «Сигналы для межпроцессного взаимодействия» мы упомянули, что код изделия должен работать с начальной маской сигналов процесса, добавляя и удаляя блокируемые сигналы в вызове sigsuspend()
. Перепишите пример, используя для этого соответствующие вызовы.
8. Напишите свою собственную версию команды kill
. Интерфейс должен быть таким:
kill [-s
Если сигнал не указан, программа должна посылать SIGTERM
.
9. Как вы думаете, почему в современных оболочках, таких, как Bash и ksh93, kill
является встроенной командой?
10. (Трудное) Реализуйте sleep()
, используя alarm()
, signal()
и pause()
. Что случится, если обработчик сигнала для SIGALRM
уже установлен?