Signal function in linux

Signal function in linux

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum , sighandler_t handler );

ОПИСАНИЕ

Системный вызов signal () устанавливает новый обработчик сигнала с номером signum в соответствии с параметром sighandler , который может быть функцией пользователя, SIG_IGN или SIG_DFL . При получении процессом сигнала с номером signum происходит следующее: если устанавливаемое значение обработчика равно SIG_IGN , то сигнал игнорируется; если оно равно SIG_DFL , то выполняются стандартные действия, связанные с сигналом (см. signal (7)). Наконец, если обработчик установлен в функцию sighandler , то сначала устанавливает значение обработчика в SIG_DFL или выполняется зависимая от реализации блокировка сигнала, а затем вызывается функция sighandler с параметром signum .

Использование функции-обработчика сигнала называется «перехватом сигнала». Сигналы SIGKILL и SIGSTOP не могут быть «перехвачены» или игнорированы.

ВОЗВРАЩАЕМЫЕ ЗНАЧЕНИЯ


ПОРТИРУЕМОСТЬ

Стандартная функция signal () в UNIX устанавливает значение обработчика равным SIG_DFL; System V (а также ядро Linux и libc4,5) выполняют то же самое. С другой стороны, BSD не перезагружает обработчик, а блокирует новые сигналы на время вызова обработчика. Библиотека glibc2 следует поведению BSD. В системе libc5 включение вместо , приводит к переопределению signal в __bsd_signal , и эта функция начинает работать, как в BSD. Но это нежелательно.

В системе glibc2 при определении тестового макроса типа _XOPEN_SOURCE или при использовании отдельной функции sysv_signal поведение функции будет стандартным. Это тоже нежалательно. Попытаться изменить семантику этой функции при помощи определений и включений — не очень хорошая идея. Лучше избегать использования функции signal вообще, и использовать вместо нее sigaction (2).

ЗАМЕЧАНИЯ

Согласно POSIX, поведение процесса после игнорирования сигналов SIGFPE , SIGILL или SIGSEGV , не созданных при помощи функций kill (2) или raise (3), не определено. Деление на ноль имеет непредсказуемый характер. На некоторых машинах это приведет к получению сигнала SIGFPE . Более того, деление самого большого по модулю отрицательного числа на -1 приведет к появлению SIGFPE . Игнорирование этого сигнала может привести к появлению бесконечнного цикла.

POSIX (3.3.1.3) не определяет, что случается при SIGCHLD , установленном в SIG_IGN . Поведение BSD и SYSV в этом случае различно. Это приводит к тому, что BSD-программы, устанавливающие поведение SIGCHLD равным SIG_IGN , в Linux не работают.

Использование sighandler_t является расширением GNU. Разные версии libc определяют этот тип; libc4 и libc5 определяют SignalHandler , glibc определяет sig_t и _GNU_SOURCE , а также sighandler_t .

Источник

Signal function in linux

void (*signal(int sig , void (* func )(int)))(int);

DESCRIPTION

The functionality described on this reference page is aligned with the ISO C standard. Any conflict between the requirements described here and the ISO C standard is unintentional. This volume of IEEE Std 1003.1-2001 defers to the ISO C standard.

Use of this function is unspecified in a multi-threaded process.

The signal () function chooses one of three ways in which receipt of the signal number sig is to be subsequently handled. If the value of func is SIG_DFL, default handling for that signal shall occur. If the value of func is SIG_IGN, the signal shall be ignored. Otherwise, the application shall ensure that func points to a function to be called when that signal occurs. An invocation of such a function because of a signal, or (recursively) of any further functions called by that invocation (other than functions in the standard library), is called a «signal handler».

Читайте также:  Подбор пароля ssh linux

When a signal occurs, and func points to a function, it is implementation-defined whether the equivalent of a:

is executed or the implementation prevents some implementation-defined set of signals (at least including sig ) from occurring until the current signal handling has completed. (If the value of sig is SIGILL, the implementation may alternatively define that no action is taken.) Next the equivalent of:

is executed. If and when the function returns, if the value of sig was SIGFPE, SIGILL, or SIGSEGV or any other implementation-defined value corresponding to a computational exception, the behavior is undefined. Otherwise, the program shall resume execution at the point it was interrupted. If the signal occurs as the result of calling the abort (), raise (), kill (), pthread_kill (), or sigqueue () function, the signal handler shall not call the raise () function.

If the signal occurs other than as the result of calling abort (), raise (), kill (), pthread_kill (), or sigqueue (), the behavior is undefined if the signal handler refers to any object with static storage duration other than by assigning a value to an object declared as volatile sig_atomic_t , or if the signal handler calls any function in the standard library other than one of the functions listed in Signal Concepts . Furthermore, if such a call fails, the value of errno is unspecified.

At program start-up, the equivalent of:

is executed for some signals, and the equivalent of:

is executed for all other signals (see exec ).

RETURN VALUE

If the request can be honored, signal () shall return the value of func for the most recent call to signal () for the specified signal sig . Otherwise, SIG_ERR shall be returned and a positive value shall be stored in errno .

ERRORS

The signal () function shall fail if: EINVAL The sig argument is not a valid signal number or an attempt is made to catch a signal that cannot be caught or ignore a signal that cannot be ignored.

The signal () function may fail if: EINVAL An attempt was made to set the action to SIG_DFL for a signal that cannot be caught or ignored (or both).

The following sections are informative.

EXAMPLES


APPLICATION USAGE

The sigaction () function provides a more comprehensive and reliable mechanism for controlling signals; new applications should use sigaction () rather than signal ().

RATIONALE


FUTURE DIRECTIONS


SEE ALSO

Signal Concepts , exec () , pause () , sigaction () , sigsuspend () , waitid () , the Base Definitions volume of IEEE Std 1003.1-2001,

Читайте также:  Linux windows recovery tools

Источник

Функция обработчик сигналов

Данная функция вызывается, когда процесс (или нить) получает неблокируемый сигнал. Дефолтный обработчик завершает наш процесс (нить). Но мы можем сами определить обработчики для интересующих нас сигналов. Следует очень осторожно относится к написанию обработчика сигналов, это не просто функция, выполняющаяся по коллбеку, происходит прерывание текущего потока выполнения без какой либо подготовительной работы, таким образом глобальные объекты могут находится в неконсистентном состоянии. Автор не берется приводить свод правил, так как сам их не знает, и призывает последовать совету Kobolog (надеюсь он не против, что я ссылаюсь на него) и изучить хотя бы вот этот материал FAQ.

sighandler_t signal(int signum, sighandler_t handler); 
  • функция не блокирует получение других сигналов пока выполняется текущий обработчик, он будет прерван и начнет выполняться новый обработчик
  • после первого получения сигнала (для которого мы установили свой обработчик), его обработчик будет сброшен на SIG_DFL
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); 
  • sa_handler — аналогичен sighandler_t в функции signal
  • sa_mask — маска сигналов который будут блокированы пока выполняется наш обработчик. + по дефолту блокируется и сам полученный сигнал
  • sa_flags — позволяет задать дополнительные действия при обработке сигнала о которых лучше почитать тут
struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler = hdl; sigset_t set; sigemptyset(&set); sigaddset(&set, SIGUSR1); sigaddset(&set, SIGUSR2); act.sa_mask = set; sigaction(SIGUSR1, &act, 0); sigaction(SIGUSR2, &act, 0); 

Здесь мы установили наш обработчик для сигналов SIGUSR1 и SUGUSR2, а также указали, что необходимо блокировать эти же сигналы пока выполняется обработчик.
С обработчиком сигналов есть один не очень удобный момент, он устанавливается на весь процесс и все порожденные нити сразу. Мы не имеет возможность для каждой нити установить свой обработчик сигналов.
Но при этом следует понимать что когда сигнал адресуется процессу, обработчик вызывается именно для главной нити (представляющей процесс). Если же сигнал адресуется для нити, то обработчик вызывается из контекста этой нити. См пример 1.

Блокирование сигналов

Для того, чтобы заблокировать некоторый сигналы для процесса, необходимо добавить их в маску сигналов данного процесса. Для этого используется функция

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 

Мы можем к уже существующей маске сигналов добавить новые сигналы (SIG_BLOCK), можем из этой маски убрать часть сигналов (SIG_UNBLOCK), а так же установить полностью нашу маску сигналов (SIG_SETMASK).
Для работы с маской сигналов внутри нити используется функция

int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset); 

которая позволяет сделать все тоже, но уже для каждой нити в отдельности.
Невозможно заблокировать сигналы SIGKILL или SIGSTOP при помощи этих функций. Попытки это сделать будут игнорироваться.

sigwait

Данная функция позволяет приостановить выполнении процесса (или нити) до получения нужного сигнала (или одного из маски сигналов). Особенностью этой функции является то, что при получении сигнала не будет вызвана функции обработчик сигнала. См. пример 2.

Посыл сигнала

int kill(pid_t pid, int sig); int raise(int sig); 

С первой все понятно. Вторая нужна для того, чтобы послать сигнал самому себе, и по сути равносильна kill(getpid(), signal). Функция getpid() возвращает PID текущего процесса.
Для того, чтобы послать сигнал отдельной нити, используется функция

int pthread_kill(pthread_t thread, int sig); 

Пример использования сигналов

Все, что я описал выше, не дает ответа на вопрос «Зачем мне использовать сигналы». Теперь я хотел бы привести реальный пример использования сигналов и где без них попросту не обойтись.
Представьте, что вы хотите читать или писать какие-то данные в какое то устройство, но это может привести к блокированию. Ну например, чтение в случае работы с сокетами. Или может быть запись в пайп. Вы можете вынести это в отдельный поток, чтобы не блокировать основную работу. Но что делать когда вам нужно завершить приложение? Как корректно прервать блокирующую операцию IO? Можно было бы задавать таймаут, но это не очень хорошее решение. Для этого есть более удобные средства: функции pselect и ppoll. Разница между ними исключительно в юзабельности, поведение у них одинаковое. В первую очередь эти функции нужны для мультиплексирования работы с IO (select/poll). Префикс ‘p’ в начале функции указывает на то, что данная функция может быть корректно прервана сигналом.

Читайте также:  Linux hostname in bash

Итак, сформулируем требование:
Необходимо разработать приложение, открывающее сокет (для простоты UDP) и выполняющее в потоке операцию чтения. Данное приложение должно корректно без задержек завершаться по требованию пользователя.
Функция треда выглядит вот так

void* blocking_read(void* arg) < if(stop) < // не успели стартовать, а нас уже прикрыли ? std::cout // Блокируем сигнал SIGINT sigset_t set, orig; sigemptyset(&set); sigaddset(&set, SIGINT); sigemptyset(&orig); pthread_sigmask(SIG_BLOCK, &set, &orig); if(stop) < // пока мы устанавливали блокировку сигнала он уже произошол // возвращаем все как было и выходим std::cout // Здесь нас не могут прервать сигналом SIGINT std::cout // Мы либо считали данные, либо произошла какаято ошибка. Но мы не получали // сигнала о завершении работы и продолжаем работать "по плану" close(sockfd); pthread_exit((void *)0); > 
  • проверяем, что пока стартовал тред его еще не пожелали завершить
  • блокируем завершающий сигнал
  • проверяем, что пока блокировали, нас не пожелали завершить
  • вызываем ppoll передавая в качестве последнего параметра маску сигналов по которой ждется сигнал
  • после выхода из ppoll проверяем что вышли не из за сигнала о завершении

Устанавливаем наш обработчик для SIGINT, и когда нужно завершить дочерний поток шлем ему этот сигнал.
Полный листинг см. пример 3.

На мой взгляд, недостатком данного способа является то, что в случае нескольких потоков мы можем завершить их только все сразу. Нет возможности устанавливать свой обработчик сигналов для каждого треда. Таким образом, нет возможности реализовать полноценное межпоточное взаимодействие через сигналы. Linux way это не предусматривает.

PS. Исходные коды разместил на сервисе PasteBin (ссылку не даю, а то еще за рекламу посчитают).
PPS. Прошу простить за обилие ошибок. Язык, слабая моя сторона. Спасибо, всем кто помог их исправить.

Данная статья не претендует на полное (и глубокое) описание работы с сигналами и нацелена в первую очередь на тех, кто до этого момента не сталкивались с понятием «сигнал». Для более глубоко понимания работы сигналов автор призывает обратиться в более компетентные источники и ознакомиться с конструктивной критикой в комментариях.

Источник

Оцените статью
Adblock
detector