Вопрос или проблема
Проблема
Мне нужно найти, какой процесс работает в конкретном окне экрана (за разумное время).
Сценарий
Мне нужно использовать имя сеанса и заголовок окна, чтобы найти процесс, работающий там. Это должно выполнить не очень медленно.
Также, возможно, стоит отметить: я использую byobu как оболочку для screen.
Что я пробовал
- Поиск в интернете
- Чтение man-страницы screen (не для слабонервных). (Окей, я не прочитал все, но ознакомился с большинством важных разделов и тщательно искал все, что может быть полезным.)
- Что я узнал:
- Единственный способ получить информацию, которая может мне понадобиться от screen (вызвав screen), — это использование его флагов командной строки.
- -Q позволит вам запрашивать определенные команды, но ни одна из них не предоставляет все, что мне нужно. Та, которую я использую, возвращает номер окна.
- number – то, что я использую для получения номера окна
- windowlist – позволяет получить кастомную строку информации, но PID сеанса не является одной из доступных запрашиваемых вещей
- echo, info, lastmsg, select, time, title — это другие, и они не выглядели полезными
- -ls перечисляет активные сеансы. Он добавляет PID к имени сеанса, так что это то, как я сейчас получаю PID сеанса.
- -Q позволит вам запрашивать определенные команды, но ни одна из них не предоставляет все, что мне нужно. Та, которую я использую, возвращает номер окна.
- Как только у меня есть PID оболочки, работающей в определенном окне, я могу проверить её переменные окружения на наличие WINDOW. Эта переменная устанавливается в номер окна. Это то, что я использую для сопоставления процесса с окном.
- Нет одной команды, которая позволит мне вернуть PID сеанса и карту заголовков окон к номерам окон. Также я не смог найти никак, чтобы детерминированно определить ID сеанса и карту заголовков окон к номерам окон вне вызова screen.
- Единственный способ получить информацию, которая может мне понадобиться от screen (вызвав screen), — это использование его флагов командной строки.
- Что я узнал:
- Попытки и ошибки / рытье через переменные окружения
- Написание скрипта
Мой скрипт
Я написал скрипт, который, кажется, успешно решает проблему, но выполнение занимает чуть больше 0,75 секунды. Это слишком долго для того, что мне нужно, но, что более важно, слишком долго, когда сервер ожидает его завершения, чтобы отправить ответ на HTML-запрос.
Вот скрипт:
#!/bin/bash
# Принимаем имя сеанса GNU/screen и окно и возвращаем процесс, работающий в его оболочке
SessionName=$1
TabName=$2
# ====== В среднем 0.370 секунд ======
# Это находит номер окна, заданный именем сеанса и заголовком окна
# Команда screen запрашивает номер окна указанного
# заголовка окна в указанном сеансе.
# Пример вывода команды screen: 1 (Main)
# Пример вывода после команды cut: 1
TargetTabNum=$(screen -S $SessionName -p $TabName -Q number | cut -d ' ' -f1)
# ====== В среднем 0.370 секунд ======
# Это находит PID сеанса, заданный именем сеанса.
# Команда screen выводит список ID сеансов
# Пример вывода команды screen:
# На экране:
# 29676.byobu (12/09/2019 10:23:19 AM) (Подключено)
# 1 сокет в /run/screen/S-{здесь_имя_пользователя}.
# Пример вывода после команды sed: 29676
SessionPID=$(screen -ls | sed -n "s/\s*\([0-9]*\)\.$SessionName\t.*/\1/p")
# ====== Остаток в среднем 0.025 секунды ======
# Это получает все процессы, имеющие сеанс как родительский,
# перебирает их, проверяя переменную WINDOW в окружении
# каждого из них, пока не найдет тот, который соответствует заголовку окна,
# а затем находит процесс с этим процессом как родительским и выводит его
# команду и аргументы (или null, если нет совпадающих процессов)
ProcessArray=( $(ps -o pid --ppid $SessionPID --no-headers) )
for i in "${ProcessArray[@]}"
do
ProcTabNum=$(tr '\0' '\n' < /proc/$i/environ | grep ^WINDOW= | cut -d '=' -f2)
if [ ! -z "$ProcTabNum" ] && [ "$TargetTabNum" -eq "$ProcTabNum" ]; then
ProcInTab=$(ps -o cmd --ppid $i --no-headers)
if [[ $? -eq 1 ]]; then
ProcInTab=NULL
fi
echo $ProcInTab
exit 0
fi
done
echo "Не удалось найти указанную вкладку: $TabName" >&2
exit 1
Как вы можете видеть, проблемные команды — это две команды screen. Я могу избавиться от одной из них, найдя процесс screen, запущенный с именем сеанса, но это кажется немного ненадежным, и я не уверен, что это будет детерминированно:
SessionPID=$(ps -eo pid,command --no-headers | grep -i "[0-9]* screen.*\-s $SessionName " | grep -v grep | cut -d ' ' -f1)
Цель
Я хотел бы иметь быстрый и надежный способ определить, какой процесс в данный момент работает в конкретном окне screen. Мне кажется, я что-то упускаю, поэтому буду очень благодарен, если кто-нибудь из вас заметит это!
(Я все еще довольно нов на StackExchange, так что любые отзывы по моему вопросу приветствуются!)
Единственный способ получить информацию, которая может мне понадобиться от screen (вызвав screen), — это использование его флагов командной строки.
Нет, вы смотрите на screen
неправильно. screen
— это виртуальный псевдотерминал (поэтому он прикрепляется к /dev/pts/*
) и с ним можно взаимодействовать сотней разных способов. В момент, когда вы запускаете screen
, вы получаете сокет, видимый в netstat
, с которым вы можете взаимодействовать, и то, как вы управляете этой сокетной связью, будет определять, сколько времени займет данный обмен. Лучший способ сделать эту связь как можно более быстрой — воспользоваться вещами, которые уже возвращаются, вместо отправки запроса и ожидания ответа.
В вашем случае вопрос можно решить, запустив w
следующим образом:
shark@tank:~$ sudo w
20:39:11 up 2 days, 1:24, 2 users, load average: 0.64, 0.41, 0.44
USER TTY FROM LOGIN@ IDLE WHAT
shark tty7 :0 Sun19 2days /usr/lib/xorg/Xorg :0 *
fish pts/1 :pts/6:S.0 20:31 7:42 htop
как вы можете видеть, второй пользователь fish
работает на screen
сокете, подключенном к tty
на /dev/pts/1
, который принадлежит пользователю shark
, как можно увидеть, запустив ls -la /dev/pts/
, и этот сокет слушает на /dev/pts/6, и там работает htop
.
Как мы можем проверить, что это действительно подключение к сокету? Запустив:
sudo file /run/screen/*/*
/run/screen/S-fish/98633.pts-6.tank: socket
и номер, который мы видим, соответствует
ps aux | grep 98633 -B 1 -A 1
fish 98632 0.0 0.0 6732 2964 pts/6 S+ 20:31 0:00 screen
fish 98633 0.0 0.0 7032 2576 ? Ss 20:31 0:00 SCREEN
fish 98634 0.0 0.0 8660 5400 pts/1 Ss+ 20:31 0:00 /bin/bash
Команда получает эту информацию из /proc
и /var/run/utmp
, и задержка минимальна, как можно увидеть, запустив:
$ sudo usr/bin/time -f "\t%E real\n\t%U user\n\t%S sys\n" w > /dev/null
0:00.01 real
0.00 user
0.01 sys
Ответ или решение
Поиск выполнения конкретного процесса в окне GNU screen может быть сложной задачей, особенно если требуется выполнить это быстро и надежно. В контексте использования byobu как обёртки для screen, задача становится ещё более специфичной. В данной статье мы рассмотрим теоретические аспекты, приведём примеры и предложим практическое решение проблемы.
Теория
GNU screen — это утилита, которая позволяет пользователям работать в нескольких терминальных сессиях одновременно, независимо от их состояния. Она обеспечивает удобный интерфейс для управления этими сессиями, что особенно полезно для удаленной работы. Основная сложность при получении информации о процессе заключается в том, что screen не предоставляет прямого метода для ассоциации окон с конкретными процессами. Однако, screen создает псевдотерминалы (/dev/pts/*), с которыми можно взаимодействовать, используя различные системные утилиты.
Простое использование screen с флагами -Q или -ls может не дать всех необходимых данных о процессах, что было обнаружено в ходе попыток автора вопроса. С другой стороны, доступ к этим псевдотерминалам может быть более эффективным способом решения поставленной задачи.
Пример
Представим, что у нас есть screen-сессия, и мы хотим узнать, какой процесс выполняется в определенном окне. Один из вариантов — использование команды w
, которая отображает информацию о пользователях и их текущих процессах:
$ w
20:39:11 up 2 days, 1:24, 2 users, load average: 0.64, 0.41, 0.44
USER TTY FROM LOGIN@ IDLE WHAT
user1 tty1 :0 Sun19 2days /usr/lib/xorg/Xorg :0
user2 pts/1 :pts/6:S.0 20:31 7:42 htop
Здесь видно, что пользователь user2
использует screen-связь через /dev/pts/1
, которая подключена к псевдотерминалу, связанному с командой htop
. Эта команда позволяет быстро определить активный процесс в окне screen, минимизируя системные ресурсы.
Применение
Чтобы применить этот теоретический подход на практике и интегрировать его в более широкую автоматизацию, мы можем разработать сценарий, который будет обрабатывать screen-сессии. Ниже приведен пример такого сценария:
#!/bin/bash
SessionName=$1
WindowTitle=$2
# Получение сведений об активной сессии
ActiveSessions=$(screen -ls | awk -v session="$SessionName" '$0 ~ session {print $1}')
# Проверка на наличие активной сессии
if [ -z "$ActiveSessions" ]; then
echo "Сессия $SessionName не найдена"
exit 1
fi
# Получение информации о процессе, связанного с указанным заголовком окна
for Session in $ActiveSessions; do
PtsName=$(screen -S "$Session" -Q windows | grep "$WindowTitle" | awk '{print $1}')
if [ -z "$PtsName" ]; then
continue
fi
TTY=$(pgrep -fla screen | grep "$Session" | grep "$PtsName" | awk '{print $NF}')
if [ -n "$TTY" ]; then
ProcessInfo=$(ps aux | grep $TTY | awk 'NR==1')
echo "Процесс в окне $WindowTitle: $ProcessInfo"
exit 0
fi
done
echo "Процесс для окна $WindowTitle не найден"
exit 1
Заключение
Приведённый выше сценарий опирается на принципы взаимодействия с screen через псевдотерминалы, что позволяет сократить время на выполнения операций за счет использования стандартных утилит, таких как ps
и w
. Это повышает отзывчивость системы и делает интеграцию более удобной. Важно помнить, что каждая система уникальна, и для наиболее эффективного решения может потребоваться адаптация скрипта под конкретные условия эксплуатации.
В заключение, для решения задачи идентификации процесса, выполняющегося в окне GNU screen, можно использовать методы, которые выгодно используют существующие системные инструменты и псевдотерминалы, тем самым значительно снижая нагрузку на систему и сокращая время выполнения поисковых операций.