← Назад к вопросам

Какие сигналы бывают в Linux?

2.0 Middle🔥 101 комментариев
#DevOps и инфраструктура#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Сигналы в Linux

Сигналы — это асинхронные уведомления, отправляемые операционной системой процессам для обработки определенных событий. Это механизм inter-process communication (IPC) на уровне ОС.

Основные понятия

Сигнал — это просто число (от 1 до 31 в базовом наборе). Когда процесс получает сигнал, он прерывается и выполняет заранее определенный обработчик.

import signal
import time
import sys

# Определение обработчика сигнала
def signal_handler(signum, frame):
    print(f"Received signal {signum}")
    sys.exit(0)

# Регистрация обработчика
signal.signal(signal.SIGTERM, signal_handler)

# Теперь при получении SIGTERM выполнится signal_handler
while True:
    time.sleep(1)

Основные сигналы (POSIX)

1. SIGTERM (15) - Завершить процесс (graceful shutdown)

import signal
import time

def graceful_shutdown(signum, frame):
    print("Graceful shutdown initiated...")
    # Очистить ресурсы
    cleanup_resources()
    print("Cleanup complete. Exiting.")
    exit(0)

signal.signal(signal.SIGTERM, graceful_shutdown)

def cleanup_resources():
    print("Closing database connections...")
    print("Saving state...")
    time.sleep(1)

# При kill <pid> (по умолчанию SIGTERM) выполнится graceful_shutdown
while True:
    time.sleep(1)

Использование: kill <pid> отправляет SIGTERM. Процесс может обработать и завершиться красиво.

2. SIGKILL (9) - Принудительное завершение (нельзя перехватить!)

import signal

# ❌ SIGKILL нельзя перехватить
try:
    signal.signal(signal.SIGKILL, signal_handler)  # ValueError!
except ValueError as e:
    print(f"Cannot handle SIGKILL: {e}")

Использование: kill -9 <pid> - ядро немедленно завершает процесс, без возможности очистки.

3. SIGINT (2) - Interrupt (Ctrl+C)

import signal
import time

def handle_interrupt(signum, frame):
    print("\nKeyboardInterrupt received!")
    raise KeyboardInterrupt()

signal.signal(signal.SIGINT, handle_interrupt)

print("Press Ctrl+C to interrupt")
try:
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    print("Process interrupted gracefully")

Использование: Нажать Ctrl+C в терминале.

4. SIGUSR1 и SIGUSR2 (10, 12) - Пользовательские сигналы

import signal
import os

def handle_usr1(signum, frame):
    print("Received SIGUSR1 - do something")

def handle_usr2(signum, frame):
    print("Received SIGUSR2 - do something else")

signal.signal(signal.SIGUSR1, handle_usr1)
signal.signal(signal.SIGUSR2, handle_usr2)

print(f"Process ID: {os.getpid()}")
print("Waiting for signals...")

while True:
    pass

Использование: kill -USR1 <pid> или kill -USR2 <pid> для кастомных действий.

5. SIGCHLD (17) - Дочерний процесс завершился

import signal
import os
import time

def handle_child(signum, frame):
    # Получить информацию о завершенном процессе
    try:
        while True:
            child_pid, status = os.waitpid(-1, os.WNOHANG)
            if child_pid == 0:
                break
            print(f"Child process {child_pid} exited with status {status}")
    except OSError:
        pass

signal.signal(signal.SIGCHLD, handle_child)

for i in range(3):
    pid = os.fork()
    if pid == 0:  # Дочерний процесс
        print(f"Child {i} working...")
        time.sleep(1)
        exit(0)
    else:  # Родительский процесс
        print(f"Started child {pid}")

while True:
    time.sleep(1)

6. SIGHUP (1) - Разорвано соединение (hangup)

import signal
import sys

def handle_hup(signum, frame):
    print("Terminal disconnected - reloading config")
    reload_config()

signal.signal(signal.SIGHUP, handle_hup)

def reload_config():
    print("Configuration reloaded")

# Используется для перезагрузки конфига без перезапуска
# kill -HUP <pid>

Использование: При отключении терминала или для перезагрузки конфига.

7. SIGSTOP (19) и SIGCONT (18) - Остановка и возобновление

import signal
import time

# SIGSTOP нельзя перехватить - процесс просто замирает
# kill -STOP <pid>  # Процесс паузируется
# kill -CONT <pid>  # Процесс продолжает работу

print("Process running...")
while True:
    print(time.time())
    time.sleep(1)

8. SIGALRM (14) - Таймер

import signal
import time

def timeout_handler(signum, frame):
    print("Time limit exceeded!")
    raise TimeoutError("Operation took too long")

signal.signal(signal.SIGALRM, timeout_handler)

# Установить таймер на 5 секунд
signal.alarm(5)

try:
    print("Doing something...")
    time.sleep(10)  # Будет прервано в 5 секунд
except TimeoutError:
    print("Operation timed out")
finally:
    signal.alarm(0)  # Отменить таймер

9. SIGSEGV (11) - Segmentation Fault

import signal

def handle_segfault(signum, frame):
    print("Segmentation fault occurred!")
    # Только для логирования - процесс все равно упадет
    exit(1)

signal.signal(signal.SIGSEGV, handle_segfault)

# Обычно возникает при нарушении памяти
# В Python редко, но может быть в C расширениях

10. SIGPIPE (13) - Broken pipe

import signal
import sys

# Игнорировать разорванные pipe (для корректной работы piping)
signal.signal(signal.SIGPIPE, signal.SIG_DFL)

# Используется в команды типа: python script.py | grep something

Полный список сигналов

import signal

print("Signal numbering:")
for name, num in sorted([(n, getattr(signal, n)) 
                         for n in dir(signal) 
                         if n.startswith(SIG) and not n.startswith(SIG_)],
                        key=lambda x: x[1]):
    print(f"{name}: {num}")

# Вывод:
# SIGHUP: 1
# SIGINT: 2
# SIGQUIT: 3
# SIGILL: 4
# SIGTRAP: 5
# SIGABRT: 6
# SIGBUS: 7
# SIGFPE: 8
# SIGKILL: 9
# SIGUSR1: 10
# SIGSEGV: 11
# SIGUSR2: 12
# SIGPIPE: 13
# SIGALRM: 14
# SIGTERM: 15
# ...

Отправка сигналов из командной строки

# Список всех сигналов
kill -l

# Отправить SIGTERM (по умолчанию)
kill <pid>

# Отправить конкретный сигнал
kill -SIGTERM <pid>
kill -15 <pid>
kill -SIGKILL <pid>
kill -9 <pid>

# Отправить SIGUSR1
kill -USR1 <pid>

# Для текущего процесса
kill -STOP $$  # Остановить
kill -CONT $$  # Продолжить

Практический пример - корректное завершение сервера

import signal
import time
import sys
from http.server import HTTPServer, BaseHTTPRequestHandler

class MyHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header("Content-type", "text/plain")
        self.end_headers()
        self.wfile.write(b"Hello World")

server = HTTPServer(("localhost", 8000), MyHandler)

def graceful_shutdown(signum, frame):
    print("\nShutting down gracefully...")
    server.shutdown()
    print("Server stopped")
    sys.exit(0)

# Обработать SIGTERM и SIGINT
signal.signal(signal.SIGTERM, graceful_shutdown)
signal.signal(signal.SIGINT, graceful_shutdown)

print("Server running on http://localhost:8000")
print("Press Ctrl+C to stop gracefully")

server.serve_forever()

Таблица важных сигналов

СигналНомерОписаниеПерехватимый
SIGTERM15Graceful завершение✅ Да
SIGKILL9Принудительное завершение❌ Нет
SIGINT2Ctrl+C✅ Да
SIGUSR110Пользовательский✅ Да
SIGUSR212Пользовательский✅ Да
SIGCHLD17Дочерний упал✅ Да
SIGHUP1Hangup✅ Да
SIGSTOP19Остановить❌ Нет
SIGCONT18Продолжить✅ Да
SIGPIPE13Broken pipe✅ Да

Лучшие практики

  • SIGTERM для graceful shutdown - обработай и заверши красиво
  • Никогда не используй SIGKILL если есть альтернатива - потеря данных
  • Обработчик должен быть быстрым - не делай в нем долгих операций
  • Используй signal.pause() для ожидания сигналов - экономит CPU
  • Не игнорируй SIGCHLD в многопроцессных приложениях
  • В многопоточных приложениях - обработка сигналов сложнее
Какие сигналы бывают в Linux? | PrepBro