Como renovar todos os threads (e filhos) de um processo no Linux?

22

O Linux ainda não segue o padrão POSIX.1, que diz que um reniceprocesso afeta "todos os encadeamentos do escopo do sistema no processo", porque de acordo com o documento pthreads (7) "os segmentos não compartilham um valor legal comum".

No entanto, às vezes, pode ser conveniente renice"tudo" relacionado a um determinado processo (um exemplo seria os processos filhos do Apache e todos os seus threads). Tão,

  • como posso renicetodos os threads pertencentes a um determinado processo?
  • como posso renicetodos os processos filhos pertencentes a um determinado processo?

Estou procurando uma solução bastante fácil.

Sei que os grupos de processos às vezes podem ser úteis, no entanto, eles nem sempre correspondem ao que eu quero fazer: eles podem incluir um conjunto de processos mais amplo ou diferente.

Usar um cgroupgerenciado por systemdtambém pode ser útil, mas mesmo que eu esteja interessado em ouvir sobre isso, estou procurando principalmente uma solução "padrão".

EDIT: também man (7) pthreadsdiz que "todos os threads em um processo são colocados no mesmo grupo de threads; todos os membros de um grupo de threads compartilham o mesmo PID". Então, é possível renicealgo que não tenha seu próprio PID?

Totor
fonte

Respostas:

19

Você pode usar /proc/$PID/taskpara encontrar todos os threads de um determinado processo; portanto, você pode usar

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

a renicetodos os threads pertencentes a um determinado processo.

Da mesma maneira, /proc/$PID/task/$PID/childrenpode ser usado para encontrar todos os processos filhos (ou /proc/$PID/task/*/childrense você quiser todos os processos filhos de todos os segmentos de um determinado processo).

$ cat /proc/$PID/task/$PID/children | xargs renice $PRIO
$ cat /proc/$PID/task/*/children | xargs renice $PRIO
Anton Leontiev
fonte
man (7) pthreadsdiz sobre a implementação atual (NPTL): "todos os threads em um processo são colocados no mesmo grupo de threads; todos os membros de um grupo de threads compartilham o mesmo PID" e "Threads não compartilham um valor legal comum". Então, como você pode renomear um thread que não possui seu próprio PID, quando reniceusa um PID para fazer isso?
Totor
Tentei renice em um ID de thread, e ele relata 24995 (process ID) old priority 0, new priority -10. 24995 não aparece ps, portanto não é um processo. Talvez os threads de renovação sejam realmente úteis?
Stefan Reich
9

Valor agradável ou compartilhamentos de CPU?

Observe que hoje em dia, valores agradáveis ​​podem não ser tão relevantes "em todo o sistema", devido ao agrupamento automático de tarefas, espacialmente ao usar systemd . Por favor, veja esta resposta para mais detalhes.

Diferença entre threads e processos

Pergunta importante no Linux, porque a documentação perpetua dúvidas (sobre threads que não possuem seu próprio PID, por exemplo).

Nota: esta resposta explica os threads do Linux com precisão.

Em resumo: o kernel lida apenas com "entidades executáveis", ou seja, algo que pode ser executado e agendado . Em termos de kernel, essas entidades são chamadas de processos. Um encadeamento é apenas um tipo de processo que compartilha (pelo menos) espaço de memória e manipuladores de sinal com outro.

Todo processo desse tipo possui um identificador exclusivo em todo o sistema: o PID (ID do processo). Para os chamados encadeamentos, às vezes é chamado TID (ID do encadeamento), mas do ponto de vista do sysadmin (e do kernel!), TID e PID são a mesma coisa (eles compartilham o mesmo espaço de nome).

Como resultado, você pode renice "encadear" individualmente porque eles têm seu próprio PID 1 .

Localizando todos os PIDs para renice recursiva

Precisamos obter os PIDs de todos os processos ("normais" ou "encadeamentos") que são descendentes (filhos ou no grupo de encadeamentos) do processo a ser percebido. Isso deve ser recursivo (considerando os filhos das crianças).

As respostas de Anton Leontiev dão a dica para fazer isso: todos os nomes de pastas /proc/$PID/task/são PID de threads contendo um childrenarquivo que lista os possíveis processos filhos.

No entanto, falta recursividade, então aqui está um script shell rápido e sujo para encontrá-los:

#!/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

Se o processo PID 1234 é o que você deseja, recursivamente agradável, agora você pode fazer:

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

1 Observe que, para conformidade com o POSIX, a chamada getpid(2)em um encadeamento não fornecerá o PID (ID exclusivo para todo o sistema) dessa entidade executável, mas o PID do processo principal no "grupo de encadeamentos". Você precisaria ligar em gettid(2)vez disso. Veja esta resposta para mais informações.

Totor
fonte
6

Não devemos confundir o PID do processo e o ID do thread em algum momento escrito TID ou no comando ps LPW. O scomando possui opções para exibir threads e, abaixo topou abaixo, htopvocê alterna entre threads e processo pela Hletra. Como dito anteriormente pelo @Totor, com o NPTL, que é a implementação atual com o kernel> 2.6, todos os threads têm o mesmo pid, mas eles têm um tid distinto. Você mostra todos os threads de um processo da seguinte maneira:

$ ps -Ljf <pid>

Estes são os nomes dos diretórios sob /proc/<pid>/task , e mesmo que renice (1) diga que seu argumento padrão é um pid, quando aplicado a um pid, renomeie apenas o thread principal (este é um bug na implementação do linux, conforme escrito em setpriority (2) ) ), também pode ser aplicado a uma maré e renova o encadeamento. É por isso que a resposta de @Anton é válida.

Mas na maioria das vezes existe uma maneira mais fácil de alcançar o resultado desejado, todos esses threads compartilham o mesmo pgid que é o pid do líder do grupo; você pode renice pelo pgid emitindo:

$ renice -g <pgid>

Se você não deseja renomear algum outro processo que dependa do mesmo líder de grupo, use a receita do @ Anton:

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

ou:

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

Você também pode querer saber quais são os outros processos do mesmo grupo que não o processo que deseja renice, ou seja, os processos que compartilham têm o mesmo pgid. Você pode usar o ps (1) , psnão permite selecionar processos pelo líder do grupo, mas pode psfazer um grep a para fazê-lo. Os processos com pgid1908 serão dados pelo comando:

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

ou se você preferir awk a sed:

$ ps --no-header axo pid,pgid|awk '{if ($2=="1908") print $1;}'
Marcz
fonte
Isso parece não funcionar corretamente na 4.19.4 (Debian Stretch a partir de agora): $ 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;}' Enquanto o método de Totor funciona / ainda funciona: $ /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 ... eu confirmei com / proc, htop, pstree, etc. que eu tenho o top correto PID de nível. Talvez algo tenha mudado no ano passado.
Bill McGonigle
Não sei como você fez seu teste @ bill-mcgonigle, tentei com três kernels 4.9.0 no Debian Stretch; 4.18.0 e 4.19.0 no teste Debian; E funciona como eu disse acima.
Marcz
Como eu disse, o Debian Stretch na 4.19.4 com os comandos e a saída mostrados; a diferença parece ser 4.19.0 vs 4.19.4, mas estou surpreso por haver muita mudança entre essas versões menores.
Bill McGonigle 14/02
Eu acho que o seu processo 8524 é o PID de todo o processo encadeado TID ou LPW, mas não do grupo de processos; portanto, é claro que você encontra todos os encadeamentos, /proc/8524/taskmas renice -gfalha. Quando você olha para uma árvore de processo, um ramo está no mesmo grupo de processos, não apenas um processo encadeado. Tente novamente verificar o resultado de ps -Ljf.
Marcz
0

Gostaria de recomendar o uso do argumento -g (grupos de processos) em vez de -p (IDs do processo) enquanto estiver usando o renice. Faz a mesma coisa sem o bash-foo.

ie

(sudo) renice -n <NEW_PRIORITY> -g <MAIN_PROCESS_ID>
user12042
fonte
A resposta de Marcz já menciona isso.
Totor 04/01
-1

Aqui está um script meu:

pgrep -v <PROCESS_NAME> | sudo xargs renice <NEW_PRIORITY>
Antonio Petricca
fonte
1
Isso inicia o renice em todos os processos, exceto no que você nomeou. Pessoalmente, considero esse comando perigoso e inadequado.
Totor 29/08
Gostaria de saber se ele quis dizer -w not -v
Diablo-D3