Comment renice tous les threads (et enfants) d'un processus sous Linux?

22

Linux ne suit pas (encore) la norme POSIX.1 qui dit qu'un renicesur un processus affecte "tous les threads de portée système dans le processus", car selon le doc pthreads (7) "les threads ne partagent pas une belle valeur commune".

Cependant, parfois, il peut être commode de renice«tout» lié à un processus donné (un exemple serait les processus enfants Apache et tous leurs threads). Alors,

  • comment puis-je renicetous les threads appartenant à un processus donné?
  • comment puis-je renicetous les processus enfants appartenant à un processus donné?

Je recherche une solution assez simple.

Je sais que les groupes de processus peuvent parfois être utiles, mais ils ne correspondent pas toujours à ce que je veux faire: ils peuvent inclure un ensemble de processus plus large ou différent.

Utiliser un cgroupgéré par systemdpeut aussi être utile, mais même si cela m'intéresse, je recherche surtout une solution "standard".

EDIT: aussi, man (7) pthreadsdit "tous les threads d'un processus sont placés dans le même groupe de threads; tous les membres d'un groupe de threads partagent le même PID". Alors, est-il même possible de renicequelque chose qui n'a pas son propre PID?

Totor
la source

Réponses:

19

Vous pouvez utiliser /proc/$PID/taskpour trouver tous les threads d'un processus donné, vous pouvez donc utiliser

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

à renicetous les threads appartenant à un processus donné.

La même manière /proc/$PID/task/$PID/childrenpeut être utilisée pour rechercher tous les processus enfants (ou /proc/$PID/task/*/childrensi vous voulez tous les processus enfants de tous les threads d'un processus donné).

$ cat /proc/$PID/task/$PID/children | xargs renice $PRIO
$ cat /proc/$PID/task/*/children | xargs renice $PRIO
Anton Leontiev
la source
man (7) pthreadsdit à propos de l'implémentation actuelle (NPTL): "tous les threads d'un processus sont placés dans le même groupe de threads; tous les membres d'un groupe de threads partagent le même PID" et "Les threads ne partagent pas une valeur commune". Alors, comment pouvez-vous retailler un thread qui n'a pas son propre PID, quand reniceutilise un PID pour le faire?
Totor
J'ai essayé de renice sur un ID de thread, et il rapporte 24995 (process ID) old priority 0, new priority -10. 24995 n'apparaît pas dans ps, donc ce n'est pas un processus. Peut-être que les threads de retouche fonctionnent réellement?
Stefan Reich
9

Belle valeur ou parts de CPU?

Veuillez noter que de nos jours, les bonnes valeurs peuvent ne pas être aussi pertinentes "à l'échelle du système", en raison du regroupement automatique des tâches, surtout lorsque vous utilisez systemd . Veuillez consulter cette réponse pour plus de détails.

Différence entre les threads et les processus

Question importante sous Linux, car la documentation perpétue les doutes (sur les threads n'ayant pas leur propre PID par exemple).

Remarque: cette réponse explique précisément les threads Linux.

En bref: le noyau ne gère que les "entités exécutables", c'est-à-dire quelque chose qui peut être exécuté et planifié . Du point de vue du noyau, ces entités sont appelées processus. Un thread, est juste une sorte de processus qui partage (au moins) l'espace mémoire et les gestionnaires de signaux avec un autre.

Chacun de ces processus possède un identifiant unique à l'échelle du système: le PID (Process ID). Pour les soi-disant threads, il est parfois appelé TID (Thread ID), mais du point de vue sysadmin (et du noyau!), TID et PID sont la même chose (ils partagent le même espace de noms).

En conséquence, vous pouvez renice chaque "thread" individuellement car ils ont leur propre PID 1 .

Recherche de tous les PID à renice récursive de

Nous devons obtenir les PID de tous les processus ("normaux" ou "thread") qui sont descendants (enfants ou dans le groupe de threads) du processus à mettre en réseau. Cela devrait être récursif (compte tenu des enfants des enfants).

La réponse d' Anton Leontiev donne l'astuce pour le faire: tous les noms de dossiers /proc/$PID/task/sont des PID de threads contenant un childrenfichier répertoriant les processus enfants potentiels.

Cependant, il manque de récursivité, alors voici un script shell rapide et sale pour les trouver:

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

Si le processus PID 1234 est celui que vous souhaitez utiliser de manière récursive, vous pouvez maintenant faire:

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

1 Notez que, pour la conformité POSIX, l'appel getpid(2)dans un thread ne vous donnera pas l'ID unique (PID) à l'échelle du système de cette entité exécutable, mais plutôt le PID du processus principal dans le "groupe de threads". Vous auriez besoin d'appeler à la gettid(2)place. Voir cette réponse pour plus d'informations.

Totor
la source
6

Nous ne devons pas confondre le processus PID et l'ID de thread parfois écrit TID ou dans la commande ps LPW. La scommande a des options pour afficher les threads, et sous topou htopvous basculez entre les threads et traitez par la Hlettre. Comme indiqué précédemment par @Totor, avec NPTL, qui est l'implémentation actuelle avec le noyau> 2.6, tous les threads ont le même pid, mais ils ont un tid distinct. Vous montrez tous les fils d'un processus en:

$ ps -Ljf <pid>

Ces tid sont les noms des répertoires sous /proc/<pid>/task, et même si renice (1) dit que son argument par défaut est un pid lorsqu'il est appliqué à un pid, il ne renice que le thread principal (c'est un bug dans l'implémentation linux comme écrit dans setpriority (2 ) ), il peut également être appliqué à un tid et il renice le fil. C'est pourquoi la réponse de @Anton est valide.

Mais le plus souvent, il existe un moyen plus facile d'obtenir le résultat souhaité, tous ces threads partagent le même pgid qui est le pid du chef de groupe; vous pouvez renice par pgid en émettant:

$ renice -g <pgid>

Si vous ne voulez pas modifier un autre processus qui dépend du même chef de groupe, vous devez utiliser la recette @ Anton:

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

ou:

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

Vous voudrez peut-être également savoir quels sont les autres processus du même groupe que le processus que vous souhaitez renice, c'est-à-dire les processus qui partagent le même pgid. Vous pouvez utiliser ps (1) , psne permet pas de sélectionner les processus par chef de groupe, mais vous pouvez grep a pspour le faire. Les processus avec pgid 1908seront donnés par la commande:

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

ou si vous préférez awk à sed:

$ ps --no-header axo pid,pgid|awk '{if ($2=="1908") print $1;}'
marcz
la source
Cela ne semble pas fonctionner correctement sur 4.19.4 (Debian Stretch à partir de maintenant): alors $ 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;}' que la méthode de Totor fonctionne / fonctionne toujours: $ /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 ... j'ai confirmé avec / proc, htop, pstree, etc. que j'ai le bon top- PID de niveau. Peut-être que quelque chose a changé au cours de la dernière année.
Bill McGonigle
Je ne sais pas comment vous avez fait votre test @ bill-mcgonigle, j'ai juste essayé avec trois noyaux 4.9.0 sur Debian Stretch; 4.18.0 et 4.19.0 sur les tests Debian; Et cela fonctionne comme je l'ai dit ci-dessus.
marcz
Comme je l'ai dit, Debian Stretch sur 4.19.4 avec les commandes et la sortie montrées; la différence semble être 4.19.0 vs 4.19.4 mais je suis surpris qu'il y ait beaucoup de changement entre ces versions mineures.
Bill McGonigle
Je suppose que votre processus 8524 est le PID de tous les processus filetés TID ou LPW, mais pas le groupe de processus, donc bien sûr, vous trouvez tous les threads /proc/8524/taskmais renice -géchouez. Lorsque vous regardez une arborescence de processus, une branche se trouve dans le même groupe de processus, pas seulement un processus threadé. Essayez à nouveau de vérifier le résultat de ps -Ljf.
marcz
0

Je voudrais recommander d'utiliser l'argument -g (groupes de processus) au lieu de -p (identificateurs de processus) lors de l'utilisation de renice. Il fait la même chose sans bash-foo.

c'est à dire

(sudo) renice -n <NEW_PRIORITY> -g <MAIN_PROCESS_ID>
user12042
la source
la réponse de marcz le mentionne déjà.
Totor
-1

Voici un de mes scripts:

pgrep -v <PROCESS_NAME> | sudo xargs renice <NEW_PRIORITY>
Antonio Petricca
la source
1
Cela lance renice sur tous les processus, sauf celui que vous nommez. Je considère personnellement cette commande comme dangereuse et inadéquate.
Totor
Je me demande s'il avait voulu dire -w pas -v
Diablo-D3