Что такое сигнал?
Сигнал (signal) — способ связи между процессами, используемый для уведомления процесса о том, что произошло событие. Он относится к асинхронной системе уведомлений. Процессу не нужно ждать поступления сигнала какой-либо операцией, и фактически процесс не знает, когда прибудет сигнал. Процессы могут посылать друг другу сигналы мягкого прерывания через системный вызов kill. Ядро также может посылать процессу сигналы о внутренних событиях, уведомляя процесс о том, что событие произошло.
Процесс приема и обработки сигналов
В системе Linux мы можем пройтиkill -l
Проверьте сигналы, поддерживаемые системой. Если в приложении прописана функция обработки сигнала, то при поступлении сигнала будет вызвана функция, иначе будет вызвано действие по умолчанию (action).
На самом деле прием сигнала осуществляет не пользовательский процесс, а агент ядра. Когда процесс P2 посылает сигнал другому процессу P1, ядро получает сигнал и помещает его в очередь сигналов P1. Когда P1 снова переходит в режим ядра (например, системный вызов, прерывание или исключение), он проверяет очередь сигналов и вызывает соответствующую функцию обработки сигналов в соответствии с соответствующим сигналом.
Уведомление:
-
Когда процесс входит в режим ядра из пользовательского режима, ему необходимо сохранить копию стека пользовательского режима в режиме ядра. Цель состоит в том, чтобы восстановить предыдущий контекст вызова пользовательского режима, когда процесс выходит из режима ядра.
-
После выполнения обработчика сигнала процесс будет активно вызывать системный вызов sigreturn(), чтобы снова вернуться в ядро, чтобы продолжить проверку наличия других сигналов, которые необходимо обработать.
Действия по умолчанию для обработки сигналов
Сигналы делятся на два типа: сигналы не в реальном времени (ненадежные сигналы) и сигналы в реальном времени (надежные сигналы).Значения сигналов, соответствующие Linux, равны 1-31 и 34-64. мы можем пройтиman 7 signal
Команда для просмотра действия по обработке сигнала по умолчанию, способа отправки сигнала и списка сигналов.
Когда приложение получает сигнал, процесс отвечает соответствующими действиями по удалению в соответствии с типом сигнала. Существуют следующие типы действий по обработке сигналов:
действие | Функции |
---|---|
Term | Default action is to terminate the process |
Ign | Default action is to ignore the signal |
Core | Default action is to terminate the process and dump core |
Stop | Default action is to stop the process |
Cont | Default action is to continue the process if it is currently stopped |
Как послать сигнал
Мы можем отправлять сигналы через системные вызовы или библиотечные функции:
Системный вызов/библиотечная функция | Функции |
---|---|
raise | Sends a signal to the calling thread. |
kill | Sends a signal to a specified process, to all members of a specified process group, or to all processes on the system. |
killpg | Sends a signal to all of the members of a specified process group. |
pthread_kill | Sends a signal to a specified POSIX thread in the same process as the caller |
Мы также можем напрямую передатьkill
илиpkill
Команда для отправки сигнала процессу:
kill process_id // 默认发送SIGTERM信号,用来终止进程
kill -1 process_id // 发送SIGHUP信号,等效于kill HUP process_id
kill -9 process_id // 强制终止进程,等效于kill KILL process_id
pkill process_name // 发送SIGTERM信号
Когда процесс запущен в терминале, мы можем посылать процессу сигналы через определенные комбинации клавиш:
- Ctrl-CОтправьте сигнал INT (SIGINT), который обычно приводит к завершению процесса.
- Ctrl-ZОтправка сигнала TSTP (SIGTSTP); обычно вызывает приостановку процесса (suspend)
- Ctrl-\Отправка сигнала QUIT (SIGQUIT); обычно приводит к завершению процесса и дампу ядра.
- Ctrl-T(Поддерживается не на всех UNIX) Отправить сигнал INFO (SIGINFO); заставляет операционную систему отображать сообщение для этой команды запуска
тип сигнала
Список стандартных сигналов POSIX.1-1990 выглядит следующим образом:
Сигнал | ценность | действие | иллюстрировать |
---|---|---|---|
SIGHUP | 1 | Term | Процесс управления терминалом завершен (терминальное соединение разорвано) |
SIGINT | 2 | Term | Инициируется отправкой пользователем символа INTR (Ctrl+C) |
SIGQUIT | 3 | Core | Инициируется пользователем, отправляющим символ QUIT (Ctrl+) |
SIGILL | 4 | Core | Недопустимая инструкция (ошибка программы, попытка выполнить сегмент данных, переполнение стека и т. д.) |
SIGABRT | 6 | Core | Вызовите функцию прерывания, чтобы вызвать |
SIGFPE | 8 | Core | Арифметические ошибки времени выполнения (арифметические ошибки с плавающей запятой, деление на ноль и т. д.) |
SIGKILL | 9 | Term | Завершить программу безоговорочно (не может быть перехвачено, заблокировано или проигнорировано), используется для принудительного завершения процесса |
SIGSEGV | 11 | Core | Неверная ссылка на память (попытка доступа к области памяти, которая не принадлежит самому себе, запись в область памяти только для чтения) |
SIGPIPE | 13 | Term | Канал сообщений поврежден (во время связи FIFO/Socket канал не открывается и выполняется операция записи) |
SIGALRM | 14 | Term | тактовый сигнал синхронизации |
SIGTERM | 15 | Term | Завершить программу (может быть перехвачена, заблокирована или проигнорирована), используется для корректного завершения процесса |
SIGUSR1 | 30,10,16 | Term | Пользовательский сигнал 1 |
SIGUSR2 | 31,12,17 | Term | Пользовательский сигнал 2 |
SIGCHLD | 20,17,18 | Ign | Завершение дочернего процесса (получено родительским процессом) |
SIGCONT | 19,18,25 | Cont | Продолжить выполнение остановленного процесса (нельзя заблокировать) |
SIGSTOP | 17,19,23 | Stop | Остановить процесс (нельзя поймать, заблокировать или проигнорировать) |
SIGTSTP | 18,20,24 | Stop | Остановить процесс (можно поймать, заблокировать или проигнорировать) |
SIGTTIN | 21,21,26 | Stop | Запускается, когда фоновая программа считывает данные с терминала |
SIGTTOU | 22,22,27 | Stop | Запускается, когда фоновая программа записывает данные в терминал |
Уведомление:
-
SIGKILL
а такжеSIGSTOP
Сигналы нельзя поймать, заблокировать или проигнорировать - Оконная система не поддерживается
SIGUSR1
а такжеSIGUSR2
сигнал
Благодаря приему и обработке сигнала сервер Nginx может выполнять перезагрузку конфигурации, плавный выход и другие функции. В программе мы также можем разработать механизм в соответствии с сигналом Nginx для выполнения нашей функции. Сигналы, обрабатываемые Nginx (мастер-процесс), и соответствующие функции перечислены ниже.
- ERM/INTБыстрый выход, выход из текущего запроса без завершения выполнения
- QUITВыйти изящно, выйти после выполнения текущего запроса
- HUPПерезагрузите файл конфигурации, запустите новый рабочий процесс с новым файлом конфигурации и аккуратно завершите старый рабочий процесс.
Процедура
- USR1Повторно открыть файл журнала
- USR2Плавное обновление бинарных файлов nginx
Сигналы в Голанге
Поведение программ Go по умолчанию для сигналов
Язык Go реализует собственную среду выполнения, и обработка сигналов по умолчанию несколько отличается от стандартных приложений Unix C:
- SIGBUS(ошибка шины),SIGFPE(арифметическая ошибка) иSIGSEGV(ошибки сегментации) называются синхронными сигналами, и они срабатывают при ошибках выполнения программы, а не через что-то вроде os.Process.Kill . Когда такой сигнал будет пойман, программа Go вызовет панику во время выполнения.
- SIGHUP(висит),SIGINT(прерывание) илиSIGTERM(завершить) по умолчанию приводит к завершению программы и выходу
- SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGSTKFLT, SIGEMT, SIGSYSПо умолчанию программа завершает работу и распечатывает информацию о трассировке стека для каждой горутины.
- SIGTSTP, SIGTTINилиSIGTTOU, который является сигналом управления заданием, используемым оболочкой, которая выполняет поведение системы по умолчанию.
- SIGPROFСреда выполнения Go использует этот сигнал для реализации runtime.CPUProfile (таймер анализа производительности, запись процессорного времени, включая пользовательский режим и режим ядра).
заSIGPIPE
сигнал, если программа Go переходит кbroken pipe
Запишите данные, ядро сгенерируетSIGPIPE
Сигнал. Если программа Go неSIGPIPE
сигнал вызоваNotify
, поскольку объект записи является стандартным выводом или стандартной ошибкой, сигнал приведет к завершению программы; но другие файловые дескрипторы (например, сетевые соединения) ничего не делают с сигналом, запись вернет ошибкуEPIPE
.
Если программа GoSIGPIPE
называетсяNotify
, независимо от файлового дескриптора,SIGPIPE
Сигналы будут передаваться в канал Notify, а запись по-прежнему будет возвращать EPIPE. Это означает, что программы командной строки Go ведут себя так же, как и традиционные программы командной строки Unix, но при записи данных в закрытое сетевое соединение традиционные программы Unix будут аварийно завершать работу, а программы Go — нет.
API в пакете сигналов
Голангos/signal
Пакеты реализуют такие функции, как отправка, получение и игнорирование сигналов.os/signal
API в пакете следующие:
Игнорировать функцию
Используется для игнорирования одного, нескольких или всех (сигнал отсутствует) сигналов. Сигнатура функции следующая:
func Ignore(sig ...os.Signal)
Для сигнала, если сначала вызывается Notify, затем Ignore, эффект Notify будет отменен; если сначала вызывается Ignore, затем вызывается Notify, а затем вызывается Reset/Stop, это будет иметь эффект Ingore.
Функция уведомления
Функция, аналогичная привязке функции обработки сигнала к сигналу, реализуется через канал.
func Notify(c chan<- os.Signal, sig ...os.Signal)
Направьте входной сигнал на chan c, если sig пуст, все входные сигналы будут переданы на c. Если c блокирует,siganl
Пакет отдаст сигнал напрямую, и все вызывающие должны убедиться, что c имеет достаточно места в кэше. Для каналов, использующих одиночную сигнализацию, достаточно буфера, равного 1.
Функция остановки
раньше позволялsignal
Пакет перестает передавать сигналы в канал.
func Stop(c chan<- os.Signal)
Он отменяет эффекты любых предыдущих вызовов Notify с помощью c. Когда Stop возвращается, гарантируется, что c больше не будет получать никаких сигналов.
Функция сброса
Обработчик, используемый для сброса сигнала; если sig пуст, вся обработка сигнала сбрасывается.
func Reset(sig ...os.Signal)
Пример использования
контролировать все сигналы
func main() {
c := make(chan os.Signal)
signal.Notify(c)
s := <-c
fmt.Println("退出信息", s)
}
Примечание. При фактическом использовании обязательно укажите используемый сигнал, не контролируйте все сигналы. В реальных проектах встречаются проблемы, вызванные неразборчивым использованием сигналов:
srv := &http.Server{
Addr: app.Addr,
Handler: app.Gin,
}
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("http listen fatal error %s\n", err)
}
}()
c := make(chan os.Signal, 1)
signal.Notity(c) // 监听所有信号,当接收到信号时候,应用进程会退出
<-c
Вышеупомянутая сервисная программа http считает само собой разумеющимся, что сигналы отправляются людьми (например, команда kill при ручном выходе из программы).На самом деле, когда сервер записывает данные отключенному клиенту, система генерирует сигнал SIGPIPE. Или, когда клиент отправляет внеполосные данные приложению Go Http, ядро системы отправит сигнал SIGURG приложению Go. Обе эти ситуации могут привести к закрытию приложения Go.
Демон выходит изящно
слушаяSIGQUIT
, SIGINT
В ожидании сигнала мы можем реализовать изящную функцию выхода из http-сервиса:
WaitGracefulExit(srv *http.Server) {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
logger.Debug("server will exit")
srv.Close()
logger.Debug("server exited")
return
default:
}
}
}
распечатать информацию о трассировке стека
Согласно описанию выше в поведении сигналов по умолчанию для программ Go приложение GoSIGQUIT
,SIGABRT
При ожидании сигнала информация о трассировке стека всех горутин будет напечатана, но приложение будет закрыто. Если мы хотим распечатать информацию о стеке без выхода, мы можем отслеживать информациюSIGUSER1
Сигнал и информация о трассировке стека печати.
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGUSR1)
go func() {
for range c {
DumpStacks()
}
}()
func DumpStacks() {
buf := make([]byte, 16384)
buf = buf[:runtime.Stack(buf, true)]
fmt.Printf("=== BEGIN goroutine stack dump ===\n%s\n=== END goroutine stack dump ===", buf)
}
Примечание: система Windows не поддерживаетсяSIGUSER1
Сигнал, если мы хотим поддерживать оконную систему, мы можем заменить ее наSIGHUP
Сигнал.