Linux программирование в примерах
• Временной интервал, остающийся для
, сохраняется на своем месте. (Другими словами, если процесс устанавливаетalarm(), а затем непосредственно вызываетalarm, новый образ в конечном счете получитexec(). Если он сначала вызываетSIGALARM, родитель сохраняет установкиfork(), тогда как потомок, вызывающийalarm, не сохраняет.exec()ЗАМЕЧАНИЕ. Многие, если не все. программы предполагают, что сигналы инициализированы действиями по умолчанию и что заблокированных сигналов нет. Таким образом, особенно если не вы писали программу, запускаемую с помощью
, можно разблокировать перед вызовамexec()все сигналыexec()10.10. Резюме
«Наша история до настоящего времени, эпизод III»
• Интерфейсы обработки сигналов развились от простых, но подверженных состояниям гонок, до сложных, но надежных. К сожалению, множественность интерфейсов затрудняет их изучение по сравнению с другими API Linux/Unix.
• У каждого сигнала есть связанное с ним действие. Действие может быть одним из следующих: игнорирование сигнала; выполнение действия системы по умолчанию или вызов предоставленного пользователем обработчика. Действие системы по умолчанию, в свою очередь, является одним из следующих: игнорирование сигнала, завершение процесса; завершение процесса с созданием его образа; остановка процесса или возобновление процесса, если он остановлен.
•
иsignal()стандартизованы ISO С.raise()управляет действиями для определенных сигналов;signal()посылает сигнал текущему процессу. Остаются ли обработчики сигналов установленными после вызова или сбрасываются для действия по умолчанию, зависит от реализации,raise()иsignal()являются простейшими интерфейсами, для многих приложений их достаточно.raise()• POSIX определяет функцию
, которая подобнаbsd_signal(), но гарантирует, что обработчик остается установленным.signal()• Действия, происходящие после возвращения из обработчика, варьируют в зависимости от системы. Традиционные системы (V7, Solaris, возможно, и другие) восстанавливают действие сигнала по умолчанию. На этих системах прерванный системный вызов возвращает -1, устанавливая в
значениеerrno. Системы BSD оставляют обработчик установленным и возвращают -1 сEINTR, содержащимerrno, лишь в случае, когда не было перемещения данных; в противном случае, системный вызов запускается повторно.EINTR• GNU/Linux придерживается POSIX, который похож, но не идентичен с BSD. Если не было перемещения данных, системный вызов возвращает -1/
. В противном случае он возвращает объем перемещенных данных. Поведение BSD «всегда повторный запуск» доступно через интерфейсEINTR, но он не является действием по умолчанию.sigaction()• Обработчики сигналов, используемые с
, подвержены состояниям гонок. Внутри обработчиков сигналов должны использоваться исключительно переменные типаsignal(). (В целях упрощения в некоторых из наших примеров мы не всегда следовали этому правилу.) Таким же образом, для вызова из обработчика сигналов безопасными являются лишь функции из табл. 10.2.volatile sig_atomic_t• Первоначальной попыткой создания надежных сигналов был 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()в двух отношениях: (1) одни процесс может послать сигнал другому процессу или целой группе процессов (конечно, с проверкой прав доступа), и (2) посылка сигнала 0 ничего не посылает, но осуществляет проверку. Таким образом, эти функции предоставляют способ проверки наличия определенного процесса или группы процессов и возможность посылки ему (им) сигнала.raise()• Сигналы могут использоваться в качестве механизма 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