• функцию 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 уже установлен?