Основы программирования в Linux
Сигнал
может быть полезен для управления дочерними процессами. По умолчанию он игнорируется. Остальные сигналы заставляют процессы, получившие их, остановиться, за исключением сигналаSIGCHLD, который вызывает возобновление процесса. Они применяются программами командной оболочки для контроля работы и редко используются в пользовательских программах.SIGCONTЧуть позже мы рассмотрим более подробно первую группу сигналов. Пока же достаточно знать, что если командная оболочка и драйвер терминала нормально настроены, ввод символа прерывания (обычно от нажатия комбинации клавиш <Ctrl>+<C>) с клавиатуры приведет к отправке сигнала
приоритетному процессу, т.е. программе, выполняющейся в данный момент. Это вызовет завершение программы, если в ней не предусмотрен перехват сигнала,SIGINTЕсли вы хотите отправить сигнал не текущей приоритетной задаче, а другому процессу, используйте команду
. Она принимает для отправки процессу в качестве необязательного параметра имя сигнала или его номер и PID (который, как правило, можно определить с помощью команды ps). Например, для отправки сигнала "останов или разъединение" командной оболочке, выполняющейся на другом терминале с PID 512, вы должны применить следующую команду:kill$ <b>kill -HUP 512</b>Удобный вариант команды
— командаkill, которая позволяет отправить сигнал всем процессам, выполняющим конкретную команду. Не все системы UNIX поддерживают ее, но ОС Linux, как правило, поддерживает. Этот вариант полезен, когда вы не знаете PID процесса или хотите отправить сигнал нескольким разным процессам, выполняющим одну и ту же команду. Обычное применение — заставить программуkillallперечитать параметры настройки. Для этого можно воспользоваться следующей командой:inetd$ <b>killall -HUP inetd</b>Программы могут обрабатывать сигналы с помощью библиотечной функции
.signal<b>#include <signal.h></b><b>void (*signal(int sig, void (*func)(int)))(int);</b>Это довольно сложное объявление говорит о том, что
— это функция, принимающая два параметра,signalиsig. Сигнал, который нужно перехватить или игнорировать, задается аргументомfunc. Функция, которую следует вызвать при получении заданного сигнала, содержится в аргументеsig. Эта функция должна принимать единственный аргумент типаfunc(принятый сигнал) и иметь типint. Функция сигнала возвращает функцию того же типа, которая является предыдущим значением функции, заданной для обработки сигнала, или одно из двух специальных значений:void□
— игнорировать сигнал;SIG_IGN□
— восстановить поведение по умолчанию.SIG_DFLПример сделает все понятным. В упражнении 11.7 вы напишете программу ctrlc.c, которая реагирует на нажатие комбинации клавиш <Ctrl>+<C> вместо обычного завершения выводом соответствующего сообщения. Повторное нажатие <Ctrl>+<C> завершает программу.
Упражнение 11.7. Обработка сигналаФункция
реагирует на сигнал, передаваемый в параметреouch. Эта функция будет вызываться, когда возникнет сигнал. Она выводит сообщение и затем восстанавливает обработку сигнала по умолчанию для сигнала SIGINT (генерируется при нажатии комбинации клавиш <Ctrl>+<C>).sig#include <signal.h>#include <stdio.h>#include <unistd.h>void ouch(int sig) {printf("OUCH! - I got signal %d\n", sig);(void)signal(SIGINT, SIG_DFL);}Функция
должна взаимодействовать с сигналомmain, генерируемым при нажатии комбинации клавиш <Ctrl>+<C>. В остальное время она находится в бесконечном цикле, выводя один раз в секунду сообщение.SIGINTint main() {(void)signal(SIGINT, ouch);while(1) {printf("Hello World!\n");sleep(1);}}Ввод комбинации клавиш <Ctrl>+<C> (отображается как
в следующем далее выводе) в первый раз заставляет программу отреагировать и продолжиться. Когда вы нажимаете <Ctrl>+<C> снова, программа завершается, т.к. сигнал^Cвернул программе стандартное поведение, заставляющее ее завершиться.SIGINT$ <b>./ctrlcl</b>Hello World!Hello World!Hello World!Hello World!<b>^C</b>OUCH! - I got signal 2Hello World!Hello World!Hello World!Hello World!<b>^C</b>$Как видно из данного примера, функция обработки сигнала принимает один целочисленный параметр — номер сигнала, приводящий к вызову функции. Это удобно, если одна и та же функция применяется для обработки нескольких сигналов. В данном случае вы выводите значение
, которое в этой системе оказывается равным 2. Не стоит полагаться на стандартные числовые значения сигналов, в новых программах всегда пользуйтесь именами сигналов.SIGINTПримечаниеВызывать из обработчика сигнала все функции, например,
, небезопасно. Удобный метод — использовать флаг, устанавливаемый в обработчике сигнала, и затем проверять этот флаг в функцииprintfи выводить сообщение, если нужно. В конце этой главы вы найдете список вызовов, которые можно безопасно применять в теле обработчиков сигналов.mainКак это работает
Программа устроена так, что, когда вы задаете сигнал
, нажимая комбинацию клавиш <Ctrl>+<C>, вызывает функциюSIGINT. После того как функция прерыванияouchзавершится, программа продолжает выполняться, но восстанавливает реакцию на сигнал, принятую по умолчанию. (У разных версий UNIX, в особенности у потомков системы Berkeley UNIX, в течение многих лет сложилось разное поведение при получении сигналов. Если вы хотите восстановить поведение по умолчанию после возникновения сигнала, лучше всего запрограммировать его на конкретные действия.) Когда программа получает второй сигналouch, она выполняет стандартное действие, приводящее к завершению программы.SIGINT