Вопрос или проблема
Предположим, что приложение 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
-
Открыть конфигурационный файл tmux:
Откройте файл~/.tmux.conf
в вашем любимом текстовом редакторе. -
Добавить привязку клавиши:
Внесите следующую строку в файл:bind-key k run-shell 'kill -s USR1 -- "-$(ps -o tpgid:1= -p #{pane_pid})"'
В данной строке
prefix k
(по умолчанию комбинация клавишCtrl+b k
) будет отправлять сигнал SIGUSR1 в группу процессов переднего плана, связанной с активной панелью в tmux. -
Важно о работе команды:
- Использование символа
-
перед$(…)
позволяет целенаправленно отправить сигнал именно группе процессов, а не только процессу-лидеру. Это важно, потому что процесс-лидер может отсутствовать. - В
: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 вы можете легко отправлять сигналы процессам, работать с ними более эффективно и гибко. Убедитесь, что вы понимаете последствия отправки сигналов и всегда тестируйте свою привязку клавиш в безопасной среде, прежде чем использовать её в работе.