Обработка сигналов в Golang

Go

Что такое сигнал?

Сигнал (signal) — способ связи между процессами, используемый для уведомления процесса о том, что произошло событие. Он относится к асинхронной системе уведомлений. Процессу не нужно ждать поступления сигнала какой-либо операцией, и фактически процесс не знает, когда прибудет сигнал. Процессы могут посылать друг другу сигналы мягкого прерывания через системный вызов kill. Ядро также может посылать процессу сигналы о внутренних событиях, уведомляя процесс о том, что событие произошло.

Процесс приема и обработки сигналов

В системе Linux мы можем пройтиkill -lПроверьте сигналы, поддерживаемые системой. Если в приложении прописана функция обработки сигнала, то при поступлении сигнала будет вызвана функция, иначе будет вызвано действие по умолчанию (action).

На самом деле прием сигнала осуществляет не пользовательский процесс, а агент ядра. Когда процесс P2 посылает сигнал другому процессу P1, ядро ​​получает сигнал и помещает его в очередь сигналов P1. Когда P1 снова переходит в режим ядра (например, системный вызов, прерывание или исключение), он проверяет очередь сигналов и вызывает соответствующую функцию обработки сигналов в соответствии с соответствующим сигналом.

Уведомление:

  1. Когда процесс входит в режим ядра из пользовательского режима, ему необходимо сохранить копию стека пользовательского режима в режиме ядра. Цель состоит в том, чтобы восстановить предыдущий контекст вызова пользовательского режима, когда процесс выходит из режима ядра.

  2. После выполнения обработчика сигнала процесс будет активно вызывать системный вызов 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 Запускается, когда фоновая программа записывает данные в терминал

Уведомление:

  1. SIGKILLа такжеSIGSTOPСигналы нельзя поймать, заблокировать или проигнорировать
  2. Оконная система не поддерживается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/signalAPI в пакете следующие:

Игнорировать функцию

Используется для игнорирования одного, нескольких или всех (сигнал отсутствует) сигналов. Сигнатура функции следующая:

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Сигнал.

Ссылаться на