Основы программирования в Linux
$ <b>./alarm</b>alarm application startingwaiting for alarm to go off<5 second pause>Ding!done $В этой программе вводится новая функция
, которая просто приостанавливает выполнение программы до появления сигнала. Когда она получит сигнал, выполняется любой установленный обработчик, и выполнение продолжается как обычно. Она объявляется следующим образом:pause<b>#include <unistd.h></b><b>int pause(void);</b>Функция возвращает -1 (если следующий полученный сигнал не вызвал завершения программы) с переменной
, равнойerrno, в случае прерывания сигналом. Лучше для ожидания сигналов применять функциюEINTR, которую мы обсудим чуть позже в этой главе.sigsuspendКак это работает
Программа имитации будильника запускает новый процесс вызовом
. Этот дочерний процесс ожидает пять секунд и затем посылает сигналforkсвоему родителю. Родитель подготавливается к получению сигналаSIGALRMи затем делает паузу до тех пор, пока не будет получен сигнал. ФункцияSIGALRMне вызывается непосредственно в обработчике, вместо этого вы устанавливаете флаг, который проверяете позже.printfПрименение сигналов и приостановка выполнения — важные составляющие программирования в ОС Linux. Это означает, что программа необязательно должна выполняться все время. Вместо того чтобы долго работать в цикле, проверяя, не произошло ли событие, она может ждать его наступления. Это особенно важно в многопользовательской среде, где процессы совместно используют один процессор, и такой вид деятельного ожидания оказывает большое влияние на производительность системы. Особая проблема, связанная с сигналами, заключается в том, что вы никогда не знаете наверняка, что произойдет, если сигнал появится в середине системного вызова? (Ответ весьма неудовлетворительный: все зависит от ситуации.) Вообще следует беспокоиться только о "медленных" системных вызовах, таких как считывание с терминала, когда системный вызов может вернуться с ошибкой, если сигнал появится во время его пребывания в режиме ожидания. Если вы начнете применять сигналы в своих программах, нужно учитывать, что некоторые системные вызовы могут закончиться аварийно, если сигнал создаст ошибочную ситуацию, которую вы могли не принимать во внимание до того, как добавили обработку сигналов.
Нужно тщательно программировать сигналы, потому что существует ряд "состояний гонок", возникающих в программах, применяющих сигналы. Например, если вы намерены вызвать pause для ожидания сигнала и этот сигнал возникнет до вызова pause, ваша программа может ждать неопределенно долго события, которое не произойдет. Новоиспеченный программист сталкивается с множеством таких состояний гонок, важных проблем синхронизации или согласования времени. Всегда очень внимательно проверяйте программный код, использующий сигналы.
Надежный интерфейс сигналовМы рассмотрели подробно возбуждение и перехват сигналов с помощью
и родственных функций, поскольку они очень часто применяются в старых UNIX-программах. Тем не менее, стандарты X/Open и спецификации UNIX рекомендуют более современный программный интерфейс для сигналовsignal, который более надежен.sigaction<b>#include <signal.h></b><b>int sigaction<int sig, const struct sigaction *act, struct sigaction *oact);</b>Структура
, применяемая для определения действий, предпринимаемых при получении сигнала, заданного в аргументеsigaction, определена в файле signal.h и как минимум включает следующие элементы:sigvoid (*)(int)sa_handler /* функция, SIG_DFL или SIG_IGN */sigset_t sa_mask /* сигналы, заблокированные для sa_handler */int sa_flags /* модификаторы действий сигнала */Функция
задает действие, связанное с сигналомsigaction. Еслиsigнеoact,nullзаписывает предыдущее действие для сигнала в указанноеsigactionместо. Еслиoactравенact, это все, что делает функцияnull. Если указательsigactionнеact, задается действие для указанного сигнала.nullКак и функция
,signalвозвращает 0 в случае успешного выполнения и -1 в случае ошибки. Переменнаяsigactionполучит значениеerrno, если заданный сигнал некорректен или была предпринята попытка захватить или проигнорировать сигнал, который нельзя захватывать или игнорировать.EINVALВ структуре
, на которую указывает аргументsigaction,act— это указатель на функцию, вызываемую при получении сигналаsa_handler. Она очень похожа на функциюsig, которая, как вы видели раньше, передавалась функцииfunc. Вы можете применять специальные значенияsignalиSIG_IGNв полеSIG_DFLдля обозначения того, что сигнал должен игнорироваться или должно быть восстановлено действие по умолчанию, соответственно.sa_handlerПоле
описывает множество сигналов, которые будут добавлены в маску сигналов процесса перед вызовом функцииsa_mask. Это множество сигналов, которые блокируются и не должны доставляться процессу. Такое поведение мешает возникновению ситуации, описанной ранее, в которой сигнал был получен до того, как его обработчик дошел до завершения. Применение поляsa_handlerможет устранить это состояние гонок.sa_maskОднако сигналы, захватываемые обработчиками, заданными в структуре
, по умолчанию не восстанавливаются, и нужно задать в полеsigactionзначениеsa_flags, если хотите добиться поведения, виденного вами раньше при обсуждении функцииSA_RESETHAND. Прежде чем обсуждать подробнееsignal, давайте перепишем программу ctrlc.c, применяяsigactionвместо функцииsigaction(упражнение 11.9).signal