Переопределить захват GDB управляющего терминала и его SIGINT.

Вопрос или проблема

У меня есть скрипт на Python, который запускает подпроцессы, вызывающие gdb --batch.

Этот скрипт должен обрабатывать Control-C (SIGINT). Когда один из подпроцессов GDB выполняется, если я отправляю Control-C на терминале, вместо того чтобы сигнал шел в скрипт, он идет в GDB. После завершения работы GDB, Control-C снова корректно поступает в скрипт.

Я считаю, что это происходит потому, что GDB устанавливает себя как управляющий терминал. Затем, когда GDB завершает работу, управляющий терминал (из-за другого ввода-вывода и т.д.) возвращается к скрипту.

  1. Есть ли способ сказать GDB не занимать управляющий терминал?

Я посмотрел исходники GDB и не вижу способа указать выполнять open() с O_NOCTTY, что, как я подозреваю, помогло бы в этой ситуации. Я не хочу перекомпилировать GDB.

Может быть, возможно какое-то решение с использованием псевдо-TTY?

  1. Если нет, есть ли способ для моего скрипта “забрать” управляющий терминал? Учтите, что GDB должно продолжать работу в другом процессе. Я подозреваю, что мог бы создать новый подпроцесс, сделать его лидером процесса, затем выполнить TTY open, что сделало бы его лидером, затем закрыть подпроцесс, сработает ли это? Мне не нравится это как обходной путь.

Обратите внимание, что я не хочу отделять подпроцессы от группы процессов скрипта.

Спасибо

Ответ или решение

Чтобы ответить на ваш вопрос о том, как предотвратить перенятие GDB контроля над терминалом и тем самым обеспечить корректную обработку сигнала SIGINT вашим Python-скриптом, сначала необходимо понять природу проблемы и возможные решения.

Теория

Когда ваш Python-скрипт запускает GDB в субпроцессе, GDB, как правило, будет пытаться стать управляющим терминала, чтобы выполнять свои задачи отладки. Это происходит, поскольку GDB обычно настроен на работу в интерактивном режиме, где важно иметь полный контроль над вводом-выводом. Вследствие этого сигналы, такие как SIGINT, направляемые с клавиатуры, будут обрабатываться GDB, пока он не завершит свою работу.

Управление терминалом определяется несколькими факторами, в том числе процессной группой и управляющим терминалом. Процесс, который является лидером своей процессной группы, может присвоить себе управляющий терминал, исходя из этой особенности поведения UNIX-подобных систем. Это делает невозможным передать такие сигналы, как SIGINT, вашему основному скрипту, пока GDB не закончит выполнение.

Пример

Предположим, что у вас есть следующий сценарий:

  1. Ваш скрипт запускает несколько субпроцессов, которые выполняют команды, включая gdb --batch.
  2. GDB работает в своей ПГ, получая контроль над вводом-выводом и перехватывая сигналы.
  3. Вы желаете, чтобы при нажатии Ctrl+C сигнал SIGINT передавался вашему скрипту, а не GDB.

На данный момент вы не желаете переписывать или пересобирать GDB, чтобы изменить его попытки захватить управляющий терминал.

Применение

С учетом этой информации, есть несколько стратегий, которые вы могли бы рассмотреть для решения проблемы:

  1. Использование псевдо-терминалов (PTY): Вы можете создать псевдо-терминал, который будет служить интерфейсом между вашим скриптом и GDB. Это позволит вам перехватывать сигналы и управлять вводом-выводом более тонко. При помощи библиотеки pty в Python вы можете создать виртуальный терминал, через который будет запускаться GDB, принеся контроль ввода-вывода обратно в ваш скрипт.

    import pty
    import os
    import subprocess
    
    def run_gdb():
       master_fd, slave_fd = pty.openpty()
       proc = subprocess.Popen(['gdb', '--batch', '...'], stdin=slave_fd, stdout=slave_fd, stderr=slave_fd, close_fds=True)
       os.close(slave_fd)
       # Простое чтение из master_fd чтобы управлять вводом/выводом

    Этот метод создаст виртуальный терминал, в котором GDB будет работать, в то время как основной контроль останется в вашем скрипте.

  2. Создание новой процессной группы: Вы можете изолировать управляющий терминал для вашего скрипта, создав новую процессную группу для вашего Python-скрипта, что может помочь в предотвращении захвата управления со стороны GDB.

    import os
    import subprocess
    
    def run_process():
       # Создаем новую сессию
       os.setsid()
       proc = subprocess.Popen([...])

    Использование os.setsid() создаст новую сессию, тем самым создавая оболочку для управления вводом-выводом.

  3. Восстановление контрольного терминала: Теоретически, это возможно путем создания процесса, который перехватит управление терминалом, однако это действительно более сложный и "хакерский" подход, который нежелателен из-за риска нестабильного поведения системы.

Совокупное использование описанных методов придаст вашему скрипту требуемую гибкость и позволит корректно обработать SIGINT. Путем экспериментов определите наилучший подход, который соответствует вашим требованиям, ресурсоемкости и профессиональным стандартам разработки. Таким образом, ваш скрипт сможет сохранить контроль над сигналами, несмотря на использование сторонних инструментов, таких как GDB.

Оцените материал
Добавить комментарий

Капча загружается...