Сигналы - общие сведения
Сигнал или виртуальное прерывание является сообщением, которое система посылает
процессу или один процесс посылает другому. Когда процесс получает сигнал,
выполнение программы процесса прерывается, и управление передается на
подпрограмму (функцию) - обработчик сигнала. После выполнения обработчика
сигнала выполнение прерванной программы возобновляется с той точки, на которой
она была прервана.
В операционной системе предусмотрено большое число типов сигналов, но
большинство из этих типов зарезервировано для системных целей - это сигналы,
которые операционная система посылает процессу. Однако есть и сигналы, которыми
процессы могут обмениваться между собой.
По умолчанию реакция на большинство сигналов - прекращение процесса,
получившего сигнал, то есть, если процесс получает сигнал, обработка которого в
нем не предусмотрена, то процесс-получатель сигнала завершается. Однако для
большинства типов сигналов процесс может установить обработчик данного сигнала
или установить игнорирование данного сигнала.
Если процесс находится в состоянии "добровольного" приостанова (вызванного,
например, выполнением системного вызова sleep),
то получение сигнала "пробуждает процесс от сна", независимо от того, в чем
состояла обработка сигнала, системный вызов sleep
заканчивается немедленно.
Обработчик сигнала в процессе имеет вид функции с прототипом:
void имя_функции(int sigtype);
Параметром данной функции является тип сигнала (один и тот же обработчик может
быть установлен для обработки сигналов разных типов).
Для установки своего обработчика сигнала, для его отмены или для установки
игнорирования сигнала используется системный вызов signal
Функции-обработчики сигналов – это обычные функции Си, они имеют доступ ко всем глобально видимым переменным и функциям. Однако, поскольку мы не знаем, в какой момент выполнения
программы будет вызвана функция-обработчик, мы должны проявлять особую
осторожность при обращении к глобальным структурам данных из этой функции.
Для функций, обрабатывающих потоки, существует и еще одно важное требование
– реентерабильность. Поскольку обработчик сигнала может быть вызван в любой
точке выполнения программы (а при не кототорых условиях во время обработки
одного сигнала может быть вызван другой обработчик сигнала) в обработчиках
додлжны использоваться функции, которые удовлетворяют требованию
реентерабельности, то есть, могут быть вызваны в то время, когда они уже
вызваны где-то в другой точке программы. Фактически, требование
реентерабельности сводится к тому, чтобы функция не использовала никаких
глобальных ресурсов, не позаботившись о синхронизации доступа к этим
ресурсам. Некоторые функции ввода-вывода, в том числе, функция printf(),
реентерабельными не являются. Это значит, что выводу одной функции printf()
может помешать вывод другой функции. Ниже приводится список
реентерабельных функций, которые безопасно вызвать из обработчиков сигналов.
Список реентерабельных функций
accept() | access() | aio_error() | aio_return() |
aio_suspend() | alarm() | bind() | cfgetispeed() |
cfgetospeed() | cfsetispeed() | fsetospeed() | chdir() |
chmod() | chown() | clock_gettime() | close() |
connect() | creat() | dup() | dup2() |
execle() | execve() | _Exit() | _exit() |
fchmod() | fchown() | fcntl() | fdatasync() |
fork() | fpathconf() | fstat() | fsync() |
ftruncate() | getegid() | geteuid() | getgid() |
getgroups() | getpeername() | getpgrp() | getpid() |
getppid() | getsockname() | getsockopt() | getuid() |
kill() | link() | listen() | lseek() |
lstat() | mkdir() | mkfifo() | open() |
posix_trace_event() | pselect() | raise() | read() |
readlink() | recv() | recvfrom() | recvmsg() |
rename() | sendto() | setgid() | setpgid() |
setsid() | setsockopt() | setuid() | shutdown() |
sigaction() | sigaddset() | sigdelset() | sigemptyset() |
sigfillset() | sigismember() | signal() | sigpause() |
sigpending() | sigprocmask() | sigqueue() | sigset() |
sigsuspend() | sleep() | socket() | socketpair() |
stat() | symlink() | sysconf() | tcdrain() |
tcflow() | tcflush() | tcgetattr() | tcgetpgrp() |
tcsendbreak() | tcsetattr() | tcsetpgrp() | time() |
timer_getoverrun() | timer_gettime() | timer_settime() | times() |
umask() | uname() | unlink() | utime() |
wait() | waitpid() | write() | |
Процесс может послать сигнал любому другому процессу, PID которого ему известен,
при помощи системного вызова kill (несмотря
на грозное название, этот системный вызов не обязательно убивает тот процесс,
которому он адресован). В некоторых случаях процессу бывает нужно послать
сигнал самому себе, это можно сделать при помощи системного вызова
raise.
Некоторые типы сигналов
Типы сигналов идентифицируются числовыми номерами, но при программировании
часто используются символьные имена сигналов, определенные в системных
включаемых файлах. Ниже приведены некоторые наиболее часто употребляемые имена
сигналов:
SIGKILL |
|
Этот сигнал приводит к завершению получившего его процесса. Это единственный
сигнал, который не может игнорироваться и для которого нельзя назначить
собственный обработчик |
SIGTERM |
|
Этот сигнал - запрос на завершение процесса. Выдачу этого сигнала, например,
включает в себя команда (не системный вызов!) kill.
Подразумевается, что процесс, получивший этот сигнал, должен завершиться,
однако процесс может установить игнорирование этого сигнала или назначить для
него собственный обработчик. |
SIGCHLD |
|
Этот сигнал система посылает родительскому процессу при завершении любого его
дочернего процесса. Реакция на этот сигнал, установленная по умолчанию, -
игнорирование. Родительский процесс может не заботиться об обработке этого
сигнала, если только он не хочет использовать его для синхронизации своего
выполнения с дочерним процессом. |
SIGALRM |
|
Этот сигнал используется для отсчета временных интервалов. Процесс может
установить некоторый временной интервал при помощи системных вызовов
alarm или setitimer,
и по истечении заданного интервала система пошлет ему сигнал SIGALRM. |
SIGUSR1 и SIGUSR2 |
|
За этими сигналами не зарезенвированы никакие системные назначения. Процессы
могут посылать эти сигналы друг другу и интерпретировать их по своему
усмотрению. |
Более подробно про типы сигналов см. в описании функции signal.
Пример выполнения приведен здесь.
Справочный материал
Избранные системные вызовы Linux/Unix. Краткое описание.
Cправочник библиотечных функция языка С: часть 1,
часть 2 (кодировка кириллица ibm866).