Решение проблемы завершения процесса в скрипте мониторинга сервера

Представленный код реализует простую систему антикраша, которая отслеживает активность сервера путем сравнения значений времени из файла. Основная проблема заключается в том, что процесс не завершается корректно при попытке его остановки.

Анализ текущей реализации

Скрипт выполняет следующие функции:

  • Мониторит изменения временной метки в файле stamp.txt
  • Определяет неактивность сервера по отсутствию изменений в течение заданного порога
  • Пытается завершить старый процесс и запустить новый при обнаружении проблем

Ключевая проблема возникает в строке:

if p != None:
    kill_process_by_pid(p.pid)

Функция завершения процесса

Функция kill_process_by_pid() реализована кроссплатформенно:

  • Для Windows использует команду taskkill /F /PID
  • Для Linux/macOS использует сигнал SIGKILL
  • Обрабатывает возможные ошибки выполнения

Возможные причины проблемы

Процесс может не завершаться по нескольким причинам:

  1. Дочерние процессы: Основной процесс может порождать дочерние процессы, которые не получают сигнал завершения
  2. Привилегии: Недостаточные права для завершения процесса
  3. Блокировки: Процесс может находиться в состоянии ожидания ввода/вывода
  4. Асинхронность: Между проверкой и отправкой сигнала может измениться состояние процесса

Рекомендуемые решения

Для надежного завершения процесса рекомендуется:

  • Использовать p.terminate() перед kill_process_by_pid() для graceful shutdown
  • Для Unix-систем завершать всю группу процессов с помощью отрицательного PID: os.killpg(os.getpgid(pid), signal.SIGKILL)
  • Добавить таймаут между terminate() и принудительным завершением
  • Проверять существование процесса перед попыткой завершения

Улучшенный подход к завершению

Модифицированный код завершения может выглядеть следующим образом:

def safe_kill_process(p):
    if p is None:
        return
    
    try:
        # Попытка graceful termination
        p.terminate()
        p.wait(timeout=5)
    except (subprocess.TimeoutExpired, ProcessLookupError):
        # Принудительное завершение при необходимости
        kill_process_by_pid(p.pid)
    
    # Дополнительная проверка для Unix-систем
    if platform.system() != 'Windows':
        try:
            os.kill(p.pid, 0)  # Проверка существования процесса
        except OSError:
            pass  # Процесс уже завершен

Этот подход обеспечивает более надежное завершение процессов и решает проблему с "зависшими" экземплярами сервера.