Что такое сигнал `SIGHUP` и как его обрабатывают в Go-приложениях?

Ответ

SIGHUP (Signal Hang Up) — это сигнал, который исторически отправлялся процессу, когда его управляющий терминал отключался.

  • Поведение по умолчанию: Завершение процесса.
  • Современное использование: В серверных приложениях и демонах SIGHUP принято перехватывать и использовать как триггер для перезагрузки конфигурации без остановки сервиса (zero-downtime reload). Это позволяет применять новые настройки (например, изменить уровень логирования или таймауты) "на лету".

Отправить сигнал процессу можно командой kill:

kill -HUP <process_id>

Пример обработки SIGHUP в Go:

package main

import (
    "log"
    "os"
    "os/signal"
    "syscall"
    "time"
)

// loadConfig - функция, имитирующая загрузку конфигурации
func loadConfig() {
    log.Println("Configuration reloaded!")
    // Здесь могла бы быть логика чтения файла .yaml, .json и т.д.
}

func main() {
    // Создаем канал для получения сигналов ОС
    sigChan := make(chan os.Signal, 1)
    // Уведомляем канал о получении сигнала SIGHUP
    signal.Notify(sigChan, syscall.SIGHUP)

    // Запускаем горутину для обработки сигналов
    go func() {
        for {
            // Блокируемся до получения сигнала
            <-sigChan
            log.Println("Received SIGHUP. Reloading configuration...")
            loadConfig()
        }
    }()

    log.Println("Server started. PID:", os.Getpid())
    log.Println("Waiting for SIGHUP signal (e.g., 'kill -HUP', os.Getpid())...")

    // Бесконечный цикл, чтобы приложение не завершалось
    for {
        time.Sleep(10 * time.Second)
    }
}

Ответ 18+ 🔞

А, ну вот, смотри, опять эти сигналы, блядь. SIGHUP. Исторически — это когда терминал отвалился, и процессу говорят: «Всё, пиши пропало, кончай работу». По умолчанию он так и делает — накрывается медным тазом.

Но современные ребята, особенно всякие демоны и серверы, — они ж хитрожопые, ёпта. Они этот сигнал перехватывают и используют не для смерти, а для перезагрузки конфигов на лету, без остановки. Типа «ой, получил пинок — а я, сука, просто конфиг перечитаю и дальше работаю». Удобно же, блядь. Отправить его можно простой командой:

kill -HUP <process_id>

А вот как это выглядит в коде на Go, если ты хочешь не умирать, а конфиг перезагружать. Смотри, тут вся магия:

package main

import (
    "log"
    "os"
    "os/signal"
    "syscall"
    "time"
)

// loadConfig - функция, имитирующая загрузку конфигурации
func loadConfig() {
    log.Println("Configuration reloaded!")
    // Здесь могла бы быть логика чтения файла .yaml, .json и т.д.
}

func main() {
    // Создаем канал для получения сигналов ОС
    sigChan := make(chan os.Signal, 1)
    // Уведомляем канал о получении сигнала SIGHUP
    signal.Notify(sigChan, syscall.SIGHUP)

    // Запускаем горутину для обработки сигналов
    go func() {
        for {
            // Блокируемся до получения сигнала
            <-sigChan
            log.Println("Received SIGHUP. Reloading configuration...")
            loadConfig()
        }
    }()

    log.Println("Server started. PID:", os.Getpid())
    log.Println("Waiting for SIGHUP signal (e.g., 'kill -HUP', os.Getpid())...")

    // Бесконечный цикл, чтобы приложение не завершалось
    for {
        time.Sleep(10 * time.Second)
    }
}

Суть в чём, блядь: ты висишь, ждёшь. Как только прилетает SIGHUP — бац, ловишь его в канале, и вместо того чтобы сдохнуть, вызываешь свою функцию loadConfig. И всё, ты уже с новыми настройками, а сервис даже не моргнул. Ёперный театр, просто и гениально. Главное — не проебать, где у тебя этот конфиг лежит и как его читать.