Отправить сигнал процессу в панели tmux

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

Предположим, что приложение X запущено в переднем плане в панели tmux. Я хотел бы отправить заданный сигнал, например SIGUSR1, приложению X. Могу ли я настроить сочетание клавиш tmux, чтобы отправить сигнал текущему активному процессу (или группе процессов) в панели?

В моей Kubuntu команда ps может дать мне идентификатор группы процессов в переднем плане на терминале, к которому подключен процесс. Ключевое слово – tpgid. Если я скажу ps, чтобы запросить процесс, идентифицированный tmux как #{pane_pid}, то я получу идентификатор группы процессов в переднем плане в этой панели.

Следующее сочетание клавиш (в ~/.tmux.conf) заставит prefixk отправить SIGUSR1 в группу процессов в переднем плане (по умолчанию prefix – это Ctrl+b):

bind-key k run-shell 'kill -s USR1 -- "-$(ps -o tpgid:1= -p #{pane_pid})"'

Примечания:

  • Дефис (-) перед $(…) отвечает за нацеливание на группу процессов в переднем плане. Вы можете попробовать без дефиса, чтобы нацелиться только на один процесс; это будет “лидер” группы процессов в переднем плане. Однако нет никакой гарантии, что “лидер” (все еще) существует. Нацеливание на группу – это разумный подход, это похоже на Ctrl+c, отправляющий SIGINT в группу, хотя механизм другой.

  • :1 взято из этого ответа: Форматирование вывода команды ps без пробелов. Избавление от начальных пробелов имеет решающее значение, когда мы добавляем дефис.

  • Существуют условия гонки: kill срабатывает после ps, и нет гарантии, что группа процессов все еще в переднем плане (или вообще существует).

  • Возможно, вам не повезет, и вы нажмете prefixk в тот момент, когда процесс, который вы хотите нацелить, завершит работу. Таким образом, вы можете непреднамеренно отправить SIGUSR1 другому процессу. Это может быть оболочка. И тогда…

  • Действие по умолчанию для SIGUSR1 – завершить. В частности, ваша интерактивная оболочка, находящаяся в переднем плане (т.е. ожидающая команду), может завершиться при нажатии prefixk. Bash так и делает. Вы можете предотвратить это, настроив ловушку заранее:

    • trap '' USR1 заставит оболочку игнорировать сигнал. В этом случае дочерние процессы также будут игнорировать сигнал, если они специально не выберут его обработку (например, dd делает это).
    • trap : USR1 заставит оболочку “игнорировать” сигнал (реагировать бездействием), но это не повлияет на поведение дочерних процессов.

Tmux предоставляет pane_pid, но это будет PID самого верхнего процесса в панели, который обычно будет оболочкой. Вам нужно будет немного глубже копнуть, чтобы найти PID процесса в переднем плане, но вы можете использовать pane_pid в качестве отправной точки.

Простая версия – что-то вроде:

bind k run-shell "kill -s SIGUSR1 $(cat /proc/#{pane_pid}/task/#{pane_pid}/children)"

Я думаю, это должно сработать, если в панели нет фоновых процессов.

Если у вас могут быть несколько однотипных процессов, работающих в оболочке, некоторые из которых находятся в фоновом режиме, это становится немного сложнее. Я думаю, вам нужно будет сделать что-то вроде использования ps -h --ppid #{pane_pid} -O stat, чтобы получить дочерние процессы для pane_pid, а затем определить, какой из них работает в переднем плане (т.е. имеет + в колонке STAT), и выделить этот PID.

Одно из возможных решений:

bind k run-shell "kill -s SIGUSR1 $(ps -h --ppid #{pane_pid} -O stat | grep -o '^[[:blank:]]*[[:digit:]]\\+[[:blank:]]\\+[^[:blank:]]*+' | cut -d ' ' -f2)"

Тем не менее, это все еще не сработает, если нет дочернего процесса (т.е. процесс в переднем плане в панели является самой оболочкой).

Если вам нужно повысить устойчивость, конечно, переместите это в оболочку и проверьте, существуют ли на самом деле дочерние процессы перед отправкой сигнала.

Только для Cygwin.

#! /usr/bin/env python
# coding: utf-8

# tmux bind-key C-z run-shell '$(cygpath -ma ~/py/kill_childs.py) #{pane_pid}||:'
# (Только для CYGWIN)

import os, re, sys, subprocess as sp

ppid = int(sys.argv[1])

class Proc():
    def __init__(self,m):
        self.pid = int(m.group(1))
        self.ppid = int(m.group(2))
        self.pgid = int(m.group(3))
        self.winpid = int(m.group(4))
        self.rest = m.group(5)

rx = re.compile("^\\s*(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(.*)$")
pso = sp.check_output('ps', text=True)
cygprocs = []
for l in pso.splitlines():
    m = rx.match(l)
    if m is not None:
        cygprocs.append(Proc(m))
for c in filter(lambda x: x.ppid == ppid, cygprocs):
    print(vars(c))
    os.kill(c.winpid, 9) # SIGKILL

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

Для отправки сигнала процессу, выполняющемуся в переднем плане в панели tmux, можно настроить привязку клавиш. В этом ответе будет описан процесс настройки привязки для отправки сигнала SIGUSR1 и обсуждены военные тонкости, касающиеся работы с процессами в tmux.

Шаги по настройке привязки клавиш в tmux

  1. Открыть конфигурационный файл tmux:
    Откройте файл ~/.tmux.conf в вашем любимом текстовом редакторе.

  2. Добавить привязку клавиши:
    Внесите следующую строку в файл:

    bind-key k run-shell 'kill -s USR1 -- "-$(ps -o tpgid:1= -p #{pane_pid})"'

    В данной строке prefix k (по умолчанию комбинация клавиш Ctrl+b k) будет отправлять сигнал SIGUSR1 в группу процессов переднего плана, связанной с активной панелью в tmux.

  3. Важно о работе команды:

    • Использование символа - перед $(…) позволяет целенаправленно отправить сигнал именно группе процессов, а не только процессу-лидеру. Это важно, потому что процесс-лидер может отсутствовать.
    • В :1 используется для форматирования вывода команды ps, чтобы убрать лишние пробелы при извлечении идентификатора группы процессов.

Учет особенностей и возможные проблемы

  • Состояние процесса:
    Обратите внимание на то, что между вызовом ps и командой kill может произойти изменение состояния процесса. Например, если процесс в переднем плане завершится после выполнения команды ps, но до команды kill, тогда сигнал будет отправлен другому процессу (например, оболочке).

  • Прерывание оболочки:
    При использовании SIGUSR1 хорошей практикой будет заранее настроить вашу оболочку на игнорирование этого сигнала, чтобы она не завершала свою работу. Это можно сделать с помощью:

    trap '' USR1

    или

    trap : USR1

Альтернативные способы работы с дочерними процессами

Если в панели tmux может выполняться несколько процессов, вам может потребоваться более продвинутый метод для определения, какой из них в данный момент является процессов переднего плана. Для этого можно использовать следующую команду:

bind k run-shell "kill -s SIGUSR1 $(ps -h --ppid #{pane_pid} -O stat | grep -o '^[[:blank:]]*[[:digit:]]\\+[[:blank:]]\\+[^[:blank:]]*+' | cut -d ' ' -f2)"

Эта команда выполнит поиск среди дочерних процессов и определит, какой из них в текущий момент является процессом переднего плана.

Заключение

С помощью настроек tmux вы можете легко отправлять сигналы процессам, работать с ними более эффективно и гибко. Убедитесь, что вы понимаете последствия отправки сигналов и всегда тестируйте свою привязку клавиш в безопасной среде, прежде чем использовать её в работе.

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

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