1. Распознайте сигнал
Сигналы — это ограниченный способ связи между процессами в Unix, Unix-подобных и других POSIX-совместимых операционных системах. Это асинхронный механизм уведомления, используемый для оповещения процесса о том, что произошло событие. Когда процессу посылается сигнал, операционная система прерывает нормальный поток управления процессом, и в этот момент любая неатомарная операция будет прервана. Если процесс определяет обработчик сигнала, то он будет выполнен, в противном случае будет выполнен обработчик по умолчанию.
Сигнал является единственным механизмом асинхронной связи в механизме межпроцессного взаимодействия, его можно рассматривать как асинхронное уведомление, которое информирует процесс, получивший сигнал, о том, что произошло. Также можно просто понять, что сигнал представляет собой мягкое прерывание в той или иной форме.
2. Источник сигнала
В целом источники сигналов можно разделить на следующие три типа:
- Аппаратный режим: деление на ноль, неверный доступ к хранилищу и другие аппаратные исключения генерируют сигналы. Эти события обычно обнаруживаются аппаратным обеспечением (например, ЦП) и уведомляются ядру операционной системы Linux, которое затем генерирует соответствующие сигналы и отправляет сигналы программе, которая выполнялась в момент возникновения события.
- Программный режим: пользователь звонит под терминалом
kill
Команды посылают сигналы задач процессам, вызовы процессовkill
илиsigqueue
Функция отправляет сигнал, когда обнаруживает, что определенное условие программного обеспечения было выполнено, например,alarm
илиsettimer
будет сгенерировано, когда установленное время таймера истечетSIGALRM
Сигналы могут генерироваться по множеству сценариев, включая сигналы. - Ввод с клавиатуры: когда пользователь нажимает клавишу на терминале, генерируется сигнал. как комбинация клавиш
Ctrl+C
произведетSIGINT
сигнал,Ctrl+\
сформировал одинSIGQUIT
сигнал и т. д.
3. Типы сигналов
работоспособныйkill -l
Ознакомьтесь со списком поддерживаемых Linux сигналов:
sl@Li:~/Works$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
Видно, что система в Linux поддерживает в общей сложности 64 сигнала, из которых сигналы с 1 по 31 являются обычными сигналами (также ненадежными сигналами), а с 34 по 64 являются сигналами реального времени (надежными сигналами).
Разница между надежными и ненадежными сигналами:
- Ненадежность здесь в основном из-за того, что не поддерживается очередь сигналов, то есть при возникновении в процессе множественных сигналов (когда скорость приема сигналов превышает скорость обработки процесса) эти сигналы, не пришедшие и не обработанные, будут отбрасываться Остался всего один сигнал.
- Надежный сигнал — это когда в процесс посылается несколько сигналов (когда скорость получения сигнала превышает скорость, с которой процесс может обработать сигнал), эти не пришедшие и не обработанные сигналы будут поставлены в очередь в процессе. Когда у процесса появится возможность обработки, он будет обработан по очереди, и сигнал не будет потерян.
Вот несколько часто используемых сигналов:
Сигнал | описывать |
---|---|
SIGHUP | Когда пользователь выходит из терминала, все процессы, запущенные терминалом, получат этот сигнал, и действие по умолчанию — завершить процесс. |
SIGINT | Программа завершает (прерывает) сигнал после того, как пользователь вводит символ INTR (обычноCtrl+C ) выдается, чтобы уведомить группу процессов переднего плана о завершении процесса. |
SIGQUIT | а такжеSIGINT похож, но состоит из символа ВЫХОД (обычноCtrl+\ ) контролировать.SIGQUIT будет сгенерировано при выходеcore файл, в этом смысле подобен сигналу ошибки программы. |
SIGKILL | Используется для немедленного завершения выполнения программы.Этот сигнал нельзя заблокировать, обработать или проигнорировать. |
SIGTERM | сигнал окончания (завершения) программы иSIGKILL Разница в том, что сигнал можно блокировать и обрабатывать. Обычно используется, чтобы попросить программу завершиться самостоятельно. |
SIGSTOP | Остановить (остановить) выполнение процесса.Обратите внимание на разницу между ним и терминацией и прерыванием: процесс не завершился, просто приостановлено выполнение.Этот сигнал нельзя заблокировать, обработать или проигнорировать. |
4. Реентерабельные функции
Обратите внимание на повторный вход функции обработки сигнала, потому что выполнение функции обработки сигнала может быть снова прервано другими сигналами.В это время программа перейдет к функции обработки другого сигнала и вернется к текущей функции обработки. после завершения обработки Write Вы должны обратить на это внимание, когда будете определять свои собственные функции обработки сигналов. Сигналы можно понимать как «мягкие прерывания», поэтому реентерабельные функции хорошо понятны.
5. Захват сигнала
Момент, когда ядро обрабатывает сигнал, полученный процессом, — это когда процесс возвращается из режима ядра в пользовательский режим. Следовательно, когда процесс выполняется в режиме ядра, сигнал мягкого прерывания не срабатывает немедленно и не будет обрабатываться до тех пор, пока он не вернется в пользовательский режим. Процесс вернется в пользовательский режим только после обработки сигнала, а необработанных сигналов в пользовательском режиме у процесса не будет.
Ядро обрабатывает сигнал softirq, полученный процессом, в контексте процесса, поэтому процесс должен быть запущен. Когда процесс получает сигнал, который он игнорирует, он отбрасывает этот сигнал и продолжает работать, как если бы он не получил сигнала.
Если процесс получает сигнал для перехвата, определяемая пользователем функция выполняется, когда процесс возвращается из режима ядра в пользовательский режим.И метод выполнения пользовательских функций очень хитрый.Ядро создает новый слой в пользовательском стеке.В этом слое значение адреса возврата устанавливается в адрес пользовательской функции-обработчика, так что когда процесс возвращается из ядра и выталкивает вершину стека, Возврат в пользовательскую функцию, при возврате из функции и выталкивании вершины стека он возвращается в то место, где изначально попал в ядро.Причина этого в том, что пользовательские функции-обработчики не могут и не могут выполняться в режиме ядра (если пользовательская функция работает в режиме ядра, пользователь может получить любые привилегии).
Вот пример, иллюстрирующий этот процесс:
- Пользовательская программа зарегистрирована
SIGQUIT
обработчик сигналовsighandler
. - в настоящее время выполняется
main
функция, когда происходит прерывание или исключение, она переключается в режим ядра. - Возврат в пользовательский режим после обработки прерывания
main
Сигнал был обнаружен до того, как функцияSIGQUIT
доставлен. - Ядро решает не возобновлять работу после возврата в пользовательский режим.
main
Контекст функции продолжает выполнение, но вместо этого выполняетсяsighandler
функция,sighandler
а такжеmain
Функции используют разные стековые пространства, между ними нет отношений вызова и вызова, и они представляют собой два независимых потока управления. -
sighandler
Автоматически выполнять специальный системный вызов после возврата из функцииsigreturn
Войдите в режим ядра снова. - Если нет нового сигнала для доставки, возврат в пользовательский режим на этот раз является восстановлением.
main
Контекст функции продолжает выполнение.
6. Пример кода 1
Следующий код выполнит пользовательский обработчик сигнала, чтобы заменить системный обработчик по умолчанию после получения сигнала выхода из программы.
#include<stdlib.h>
#include<stdio.h>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>
void sig_handle(int sig) {
printf("received signal: %d, quit.\n", sig);
exit(0);
}
int main () {
signal(SIGINT, sig_handle);
signal(SIGKILL, sig_handle);
signal(SIGSEGV, sig_handle);
signal(SIGTERM, sig_handle);
int i = 0;
while (1) {
printf("%d\n", ++i);
sleep(2);
}
printf("main quit.");
return 0;
}
результат операции:
1
2
received signal: 15, quit.
Семь, пример кода 2
Функция этого кода аналогична приведенному выше примеру.Сигнал также может передавать параметры, но это больше функция уведомления.Информация, которая может быть передана, очень ограничена.Если необходимо передать большое количество информации, другие можно рассмотреть методы межпроцессного взаимодействия.
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
void new_op(int, siginfo_t*, void*);
int main() {
if (NULL == freopen("sigproc.log", "w", stdout)) {
fprintf(stderr, "error redirecting stdout\n");
}
struct sigaction act;
sigemptyset(&act.sa_mask); //sa_mask指定在信号处理程序执行过程中,哪些信号应当被阻塞。缺省情况下当前信号本身被阻塞,防止信号的嵌套发送
sigaddset(&act.sa_mask, SIGTERM);
sigaddset(&act.sa_mask, SIGINT);
act.sa_flags = SA_SIGINFO; //SA_SIGINFO,当设定了该标志位时,表示信号附带的参数可以被传递到信号处理函数中
act.sa_sigaction = new_op;
if (sigaction(SIGINT, &act, NULL) < 0) {
printf("install sigal error\n");
}
if (sigaction(SIGTERM, &act, NULL) < 0) {
printf("install sigal error\n");
}
if (sigaction(SIGHUP, &act, NULL) < 0) {
printf("install sigal error\n");
}
int i = 0;
while (1) {
printf("%d\n", ++i);
sleep(1);
}
printf("end.");
return 0;
}
void new_op(int signum, siginfo_t *info, void *myact) {
printf("receive signal %d\n", signum);
for (int i = 0; i < 5; ++i) {
printf("signal processing: %d\n", i);
sleep(1);
}
printf("process quit.");
exit(0);
}
результат операции:
1
2
3
receive signal 15
signal processing: 0
signal processing: 1
signal processing: 2
signal processing: 3
signal processing: 4
process quit.
Справочная документация:
Сигнал
Межпроцессное взаимодействие в среде Linux (2) Сигналы (1)
Межпроцессное взаимодействие в среде Linux (2) Сигналы (2)
Наконец, добро пожаловать, чтобы обратить внимание на публичный аккаунт WeChat, Поехали!