Найти тему
Nuances of programming

Обработка сигналов в операционных системах семейства Unix на Golang

Оглавление

Источник: Nuances of Programming

Сигналы в операционных системах семейства Unix  —  это программные прерывания, которые отправляются программе для указания на возникновение какого-то важного события. Это могут быть разные события: от запросов пользователей до ошибок некорректного доступа к памяти. Некоторые сигналы, например сигнал прерывания, свидетельствуют о том, что пользователь отправил программе указание выполнить что-то, но сделал это не в обычном потоке управления.

Работа с сигналами операционной системы важна в различных ситуациях. Например, бывает нужно, чтобы сервер нормально завершил работу в случае получения им сигнала SIGTERM или чтобы инструмент командной строки остановил обработку входных данных в случае получения им сигнала SIGINT. Вот как с помощью каналов обрабатываются сигналы на Go:

Изображение автора: сигналы ОС
Изображение автора: сигналы ОС

В этой статье мы рассмотрим, как с помощью применяемого в Golang пакета os/signal обрабатываются сигналы в операционных системах семейства Unix.

Пакет «os/signal»

Пакет os/signal позволяет настроить поведение программы на Golang при получении определенных типов сигналов в операционных системах семейства Unix. Большинство программ на Linux/Unix благополучно завершится после получения kill (сигнала уничтожения). Но бывает нужно, чтобы с помощью программы сначала был перехвачен сигнал, выполнено резервное копирование, сброшены данные на диск и т. д. В этом случае перед завершением следует использовать пакет os/signal.

Типы сигналов

При рассмотрении сигналов сделаем акцент на асинхронных сигналах. К их возникновению ошибки программы не приводят. Эти сигналы отправляются ядром или какой-то другой программой.

  • Сигнал SIGHUP отправляется при потере программой своего управляющего терминала.
  • Сигнал SIGINT отправляется при введении пользователем в управляющем терминале символа прерывания, по умолчанию это ^C (Control-C).
  • Сигнал SIGQUIT отправляется при введении пользователем в управляющем терминале символа выхода, по умолчанию это ^\ (Control-Backslash).
  • SIGTERM  —  это общий сигнал, используемый для завершения программы.

Вот простой пример на Golang того, как перехватывать самые распространенные сигналы уничтожения/завершения в операционных системах семейства Unix (внимание: для лучшего понимания читайте комментарии к коду):

package main

import (
"fmt"
"os"
"os/signal"
"syscall"
)

func main() {
signalChanel := make(chan os.Signal, 1)
signal.Notify(signalChanel,
syscall.SIGHUP,
syscall.SIGINT,
syscall.SIGTERM,
syscall.SIGQUIT)

exit_chan := make(chan int)
go func() {
for {
s := <-signalChanel
switch s {
// kill -SIGHUP XXXX [XXXX - идентификатор процесса для программы]
case syscall.SIGHUP:
fmt.Println("Signal hang up triggered.")

// kill -SIGINT XXXX или Ctrl+c [XXXX - идентификатор процесса для программы]
case syscall.SIGINT:
fmt.Println("Signal interrupt triggered.")

// kill -SIGTERM XXXX [XXXX - идентификатор процесса для программы]
case syscall.SIGTERM:
fmt.Println("Signal terminte triggered.")
exit_chan <- 0

// kill -SIGQUIT XXXX [XXXX - идентификатор процесса для программы]
case syscall.SIGQUIT:
fmt.Println("Signal quit triggered.")
exit_chan <- 0

default:
fmt.Println("Unknown signal.")
exit_chan <- 1
}
}
}()
exitCode := <-exit_chan
os.Exit(exitCode)
}

/* Вывод

➜ ~ kill -SIGINT 451740
Terminal 2 - Output - "Signal interrupt triggered."

➜ ~ kill -SIGHUP 451740
Terminal 2 - Output - "Signal hang up triggered."

➜ ~ kill -SIGTERM 451740
Terminal 2 - Output - "Signal terminte triggered."

➜ ~ kill -SIGQUIT 451846
Terminal 2 - Output - "Signal quit triggered."

*/

Копируем эту программу на локальный компьютер и запускаем. Дадим ей название «signal-controller. go». Вот что получилось при ее выполнении на моем компьютере с Ubuntu:

  • Terminal 1 — go build signal-controller.go
  • Terminal 1 — ./signal-controller

На моем компьютере идентификатор процесса запускаемого двоичного файла был 451575.

  • Terminal 2 — kill -SIGINT 451575

Terminal 1 - Output - "Signal interrupt triggered."

  • Terminal 2 — kill -SIGHUP 451575

Terminal 1 - Output - "Signal hang up triggered."

  • Terminal 2 — kill -SIGTERM 451575

Terminal 1 - Output - "Signal terminte triggered."

  • Terminal 2 — kill -SIGQUIT 451575

Terminal 1 - Output - "Signal quit triggered."

Стоит также упомянуть о том, что в Go 1.16 появилась функция os.NotifyContext, позволяющая создавать контекст, который отменяется при поступлении одного из выбранных сигналов. Так что в большинстве случаев нам больше не придется обрабатывать их вручную.

Заключение

Сигналы в операционных системах семейства Unix легко обрабатываются на Golang. Здесь в этом помогает пакет os/signal. Для чтения сигналов ​нужно использовать канал типа os.Signal. Кроме того, есть возможность реализовать код для обработки всех типов таких сигналов, получаемых программой.

Читайте также:

Читайте нас в Telegram, VK и Яндекс.Дзен

Перевод статьи Radhakishan Surwase: Handling Unix Signals In Golang