Сигналы в операционных системах семейства 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
. Кроме того, есть возможность реализовать код для обработки всех типов таких сигналов, получаемых программой.
Читайте также:
- Реализация интерфейсов в Golang
- Бенчмарки в Golang: тестируем производительность кода
- Оптимизация структур в Golang для эффективного распределения памяти
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Radhakishan Surwase: Handling Unix Signals In Golang