Если вы хотите сохранить обработчик сигнала и продолжать реагировать на комбинацию клавиш <Ctrl>+<C>, вам придется восстановить его, вызвав функцию
signal
еще раз. Это приведет к возникновению короткого промежутка времени, начиная с запуска функции прерывания и до момента восстановления обработчика сигнала, в течение которого сигнал не будет обрабатываться. Если второй сигнал будет получен в этот период, вопреки вашим желаниям программа может завершиться.
Примечание
Мы не рекомендуем вам пользоваться функцией
signal
для перехвата сигналов. Мы включили ее в книгу, потому что она будет часто встречаться в более старых программах. Позже вы увидите
sigaction
, более четко определенный и надежный интерфейс, который следует применять в новых программах.
Функция
signal
возвращает предыдущее значение обработчика для заданного типа сигнала, если таковой есть, или в противном случае
SIG_ERR
с установкой положительного значения в переменной
errno
. Если задан неверный сигнал или делается попытка обработать сигнал, который не может быть перехвачен или игнорироваться, например
SIGKILL
, переменной
errno
присваивается значение
EINVAL
.
Отправка сигналов
Процесс может отправить сигнал другому процессу, включая себя самого, с помощью вызова функции
kill
. Вызов завершится аварийно, если у программы нет полномочий на отправку сигнала, часто потому что процесс-получатель принадлежит другому пользователю. Эта функция эквивалентна команде оболочки с тем же именем.
<b>#include <sys/types.h></b>
<b>#include <signal.h></b>
<b>int kill(pid_t pid, int sig);</b>
Функция
kill
посылает заданный сигнал
sig
процессу с идентификатором, заданным в аргументе
pid
. В случае успеха она возвращает 0. Для отправки сигнала посылающий процесс должен иметь право на выполнение этого действия. Обычно это означает, что у обоих процессов должен быть один и тот же идентификатор пользователя ID (т.е. вы можете отправить сигнал только одному из собственных процессов, хотя суперпользователь может отправлять сигналы любому процессу).
Функция
kill
завершится аварийно, вернет -1 и установит значение переменной
errno
, если задан неверный сигнал, (
errno
равна
EINVAL
), у процесса нет полномочий (
EPERM
) или заданный процесс не существует (
ESRCH
).
Сигналы предоставляют полезное средство, именуемое будильником или сигналом тревоги. Вызов функции
alarm
может применяться для формирования сигнала
SIGALRM
в определенное время в будущем.
<b>#include <unistd.h></b>
<b>unsigned int alarm(unsigned int seconds);</b>
Вызов
alarm
намечает доставку сигнала
SIGALRM
через
seconds
секунд. В действительности сигнал будильника будет доставлен чуть позже из-за обработки задержек и учета неопределенностей. Значение 0 отменяет любой невыполненный запрос на сигнал будильника. Вызов функции
alarm
до получения сигнала может вызвать сброс графика доставки. У каждого процесса может быть только один невыполненный сигнал будильника. Функция
alarm
возвращает количество секунд, оставшихся до отправки любого невыполненного вызова,
alarm
, или -1 в случае аварийного завершения.
Для того чтобы увидеть как работает функция
alarm
, можно сымитировать ее действие, используя вызовы
fork
,
sleep
и
signal
(упражнение 11.8). Программа сможет запустить новый процесс с единственной целью — отправить сигнал спустя какое- то время.
Упражнение 11.8 Будильник
В программе alarm.c первая функция,
ding
, имитирует будильник.
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
static int alarm_fired = 0;
void ding(int sig) {
alarm_fired = 1;
}
В функции
main
вы заставляете дочерний процесс ждать пять секунд перед отправкой сигнала
SIGALRM
в свой родительский процесс:
int main() {
pid_t pid;
printf("alarm application starting\n");
pid = fork();
switch(pid) {
case -1:
/* Аварийное завершение */
perror("fork failed");
exit(1);
case 0:
/* Дочерний процесс */
sleep(5);
kill(getppid(), SIGALRM);
exit(0);
}
Родительский процесс устроен так, что перехватывает сигнал
SIGALRM
с помощью вызова
signal
и затем ждет неизбежности:
/* Если мы оказались здесь, то мы — родительский процесс */
printf("waiting for alarm to go off\n");
(void)signal(SIGALRM, ding);
pause();
if (alarm_fired) printf("Ding!\n");
printf("done\n");
exit(0);
}
Когда вы выполните программу, то увидите, что она делает паузу на пять секунд, в течение которых ждет имитации будильника: