Как переименовать все потоки (и дочерние элементы) одного процесса в Linux?

22

Linux (пока) не следует стандарту POSIX.1, который гласит, что reniceвключение процесса влияет на «все потоки области системы в процессе», поскольку в соответствии с документом pthreads (7) «потоки не имеют общего полезного значения».

Однако иногда может оказаться удобным renice«все», относящееся к данному процессу (например, дочерние процессы Apache и все их потоки). Так,

  • Как я могу reniceвсе потоки, принадлежащие к данному процессу?
  • как я могу reniceвсе дочерние процессы, принадлежащие данному процессу?

Я ищу довольно простое решение.

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

Также может быть полезно использовать cgroupуправляемый systemd, но даже если мне интересно об этом узнать, я в основном ищу «стандартное» решение.

EDIT: также man (7) pthreadsговорит , что «все потоки в процессе помещены в одну группу потоков; все члены группы потоков имеют один и тот же PID». Итак, возможно ли вообще reniceчто-то, у кого нет собственного PID?

Totor
источник

Ответы:

19

Вы можете использовать, /proc/$PID/taskчтобы найти все потоки данного процесса, поэтому вы можете использовать

$ ls /proc/$PID/task | xargs renice $PRIO

ко reniceвсем потокам, принадлежащим данному процессу.

Таким же образом /proc/$PID/task/$PID/childrenможно найти все дочерние процессы (или, /proc/$PID/task/*/childrenесли вы хотите, чтобы все дочерние процессы всех потоков данного процесса).

$ cat /proc/$PID/task/$PID/children | xargs renice $PRIO
$ cat /proc/$PID/task/*/children | xargs renice $PRIO
Антон Леонтьев
источник
man (7) pthreadsговорит о текущей реализации (NPTL): «все потоки в процессе помещены в одну группу потоков; все члены группы потоков имеют один и тот же PID» и «потоки не имеют общего полезного значения». Затем, как вы можете арендовать поток, который не имеет своего собственного PID, когда для этого reniceиспользуется PID?
Тотор
Я попытался renice по идентификатору потока, и он сообщает 24995 (process ID) old priority 0, new priority -10. 24995 не появляется в ps, так что это не процесс. Может быть, смена потоков действительно работает?
Стефан Райх
9

Хорошая стоимость или доля процессора?

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

Разница между потоками и процессами

Важный вопрос по Linux, поскольку документация увековечивает сомнения (например, о потоках, не имеющих собственного PID).

Примечание: этот ответ объясняет потоки Linux точно.

Короче говоря: ядро ​​обрабатывает только «выполняемые объекты», то есть то, что может быть запущено и запланировано . По сути, эти объекты называются процессами. Поток - это всего лишь процесс, который разделяет (по крайней мере) пространство памяти и обработчики сигналов с другим.

Каждый такой процесс имеет общесистемный уникальный идентификатор: PID (идентификатор процесса). Для так называемых потоков это иногда называется TID (Thread ID), но с точки зрения системного администратора (и ядра!) TID и PID - это одно и то же (они используют одно и то же пространство имен).

В результате вы можете renice каждый «поток» индивидуально, потому что они имеют свои собственные PID 1 .

Поиск всех PID для renice рекурсивного

Нам нужно получить PID всех процессов («нормальных» или «потоковых»), которые являются потомками (дочерние или в группе потоков) ожидаемого процесса. Это должно быть рекурсивным (учитывая детей детей).

Ответ Леонтьева дает подсказку: все имена папок в /proc/$PID/task/PID потоков содержат childrenфайл со списком потенциальных дочерних процессов.

Однако ему не хватает рекурсивности, поэтому вот быстрый и грязный скрипт для их поиска:

#!/bin/sh
[ "$#" -eq 1 -a -d "/proc/$1/task" ] || exit 1

PID_LIST=
findpids() {
        for pid in /proc/$1/task/* ; do
                pid="$(basename "$pid")"
                PID_LIST="$PID_LIST$pid "
                for cpid in $(cat /proc/$1/task/$pid/children) ; do
                        findpids $cpid
                done
        done
}

findpids $1
echo $PID_LIST

Если процесс PID 1234 - это тот, который вы хотите сделать рекурсивно приятным, теперь вы можете сделать:

renice -n 15 -p $(/path/to/findchildren.sh 1234)

1 Обратите внимание, что для соответствия POSIX вызов getpid(2)внутри потока не даст вам общесистемного уникального идентификатора (PID) этого запускаемого объекта, а скорее PID основного процесса в «группе потоков». Вам нужно будет позвонить gettid(2)вместо этого. Смотрите этот ответ для получения дополнительной информации.

Totor
источник
6

Не следует путать PID процесса и идентификатор потока, когда-нибудь записанный TID или в команду ps LPW. У sкоманды есть опции для отображения потоков, а под topили htopвы переключаетесь между потоками и обрабатываете по Hбуквам. Как ранее сообщал @Totor, с NPTL, который является текущей реализацией с ядром> 2.6, все потоки имеют одинаковый pid, но имеют различный tid. Вы показываете все темы процесса:

$ ps -Ljf <pid>

Эти tid являются именами каталогов, находящихся под /proc/<pid>/task, и даже если renice (1) говорит, что его аргумент по умолчанию является pid, когда применяется к pid, он выводит только основной поток (это ошибка в реализации linux, как написано в setpriority (2) ) ), это также может быть применено к tid, и это возобновляет поток. Вот почему ответ @Anton является действительным.

Но чаще всего существует более простой способ достижения желаемого результата, все эти потоки используют один и тот же pgid, который является pid лидера группы; Вы можете арендовать через pgid, выполнив:

$ renice -g <pgid>

Если вы не хотите арендовать какой-то другой процесс, который зависит от того же лидера группы, вы должны использовать рецепт @ Anton:

$ renice <priority> $(ls -1 /proc/<pid>/task)

или:

$renice <priority> $(ps --no-header -Lo tid <pid>)

Возможно, вы также захотите узнать, что представляют собой другие процессы той же группы, чем процесс, который вы хотите арендовать, то есть процессы, которые совместно используют, имеют одинаковый pgid. Вы можете использовать ps (1) , psне позволяет выбирать процессы по групповому лидеру, но вы можете использовать команду grep psдля этого. Процессы с pgid 1908будут заданы командой:

$ ps --no-header axo pid,pgid |sed -n '/^ *[0-9][0-9]*  *1908/s/[0-9][0-9]* *$//p'

или если вы предпочитаете awk для sed:

$ ps --no-header axo pid,pgid|awk '{if ($2=="1908") print $1;}'
marcz
источник
Похоже, что это не работает правильно на 4.19.4 (Debian Stretch на данный момент): тогда $ renice -n 18 -g 8524 renice: failed to get priority for 8524 (process group ID): No such process $ ps --no-header axo pid,pgid|awk '{if ($2=="8524") print $1;}' как метод Totor работает / работает: $ /bin/ls /proc/8524/task | /usr/bin/xargs renice 19 2739 (process ID) old priority 19, new priority 19 2740 (process ID) old priority 19, new priority 19 ... я подтвердил с помощью / proc, htop, pstree и т. Д., Что у меня есть правильный top- уровень PID. Может быть, что-то изменилось в прошлом году.
Билл МакГонигл
Я не знаю, как вы сделали свой тест @ bill-mcgonigle, я только что попробовал с тремя ядрами 4.9.0 на Debian Stretch; 4.18.0 и 4.19.0 по тестированию Debian; И это работает, как я сказал выше.
marcz
Как я уже сказал, Debian Stretch на 4.19.4 с указанием команд и вывода; разница вроде 4.19.0 против 4.19.4, но я удивлен, что между такими второстепенными версиями будет много изменений.
Билл МакГонигл
Я предполагаю, что ваш процесс 8524 является идентификатором PID всех потоковых TID или LPW, но не группы процессов, поэтому, конечно, вы найдете все потоки, /proc/8524/taskно renice -gне сможете . Когда вы смотрите на дерево процессов, одна ветвь находится в одной группе процессов, а не только один поточный процесс. Попробуйте еще раз проверить результат ps -Ljf.
marcz
0

Я бы рекомендовал использовать аргумент -g (группы процессов) вместо -p (идентификаторы процессов) при использовании renice. Это делает то же самое без баш-фу.

т.е.

(sudo) renice -n <NEW_PRIORITY> -g <MAIN_PROCESS_ID>
user12042
источник
Ответ Марча уже упоминает об этом.
Тотор
-1

Вот мой сценарий:

pgrep -v <PROCESS_NAME> | sudo xargs renice <NEW_PRIORITY>
Антонио Петричка
источник
1
Это запускает renice для всех процессов, кроме того, который вы называете. Я лично считаю эту команду опасной и неадекватной.
Тотор
Интересно, имел ли он в виду -w не -v
Diablo-D3