26

Linux does not (yet) follow the POSIX.1 standard which says that a renice on a process affects "all system scope threads in the process", because according to the pthreads(7) doc "threads do not share a common nice value".

However, sometimes, it can be convenient to renice "everything" related to a given process (one example would be Apache child processes and all their threads). So,

  • how can I renice all threads belonging to a given process ?
  • how can I renice all child processes belonging to a given process ?

I am looking for a fairly easy solution.

I know that process groups can sometimes be helpful, however, they do not always match what I want to do: they can include a broader or different set of processes.

Using a cgroup managed by systemd might also be helpful, but even if I am interested to hear about it, I mostly looking for a "standard" solution.

EDIT: also, man (7) pthreads says "all of the threads in a process are placed in the same thread group; all members of a thread group share the same PID". So, is it even possible to renice something which doesn't have it's own PID?

24
+50

You can use /proc/$PID/task to find all threads of a given process, therefore you can use

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

to renice all threads belonging to a given process.

Same way /proc/$PID/task/$PID/children can be used to find all child processes (or /proc/$PID/task/*/children if you want all child processes of all threads of a given process).

$ cat /proc/$PID/task/$PID/children | xargs renice $PRIO
$ cat /proc/$PID/task/*/children | xargs renice $PRIO
| improve this answer | |
  • man (7) pthreads says about the current (NPTL) implementation: "all of the threads in a process are placed in the same thread group; all members of a thread group share the same PID" and "Threads do not share a common nice value". Then, how can you renice a thread which doesn't have it's own PID, when renice uses a PID to do so? – Totor Oct 19 '16 at 12:34
  • I tried renice on a thread ID, and it reports 24995 (process ID) old priority 0, new priority -10. 24995 does not appear in ps, so it's not a process. Maybe renice-ing threads actually works? – Stefan Reich Sep 28 '19 at 13:29
12

Finding all PIDs to renice recursively

We need to get the PIDs of all processes ("normal" or "thread") which are descendant (children or in the thread group) of the to-be-niced process. This ought to be recursive (considering children's children).

Anton Leontiev answer's gives the hint to do so: all folder names in /proc/$PID/task/ are threads' PID containing a children file listing potential children processes.

However, it lacks recursivity, so here is a quick & dirty shell script to find them:

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

If process PID 1234 is the one you want to recursively nice, now you can do:

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

Side notes

Nice value or CPU shares ?

Please note that nowadays, nice values may not be so relevant "system-wide", because of automatic task grouping, espacially when using systemd. Please see this answer for more details.

Difference between threads and processes

Note: this answer explains Linux threads precisely.

In short: the kernel only handles "runnable entities", that is, something which can be run and scheduled. Kernel wise, these entities are called processes. A thread, is just a kind of process that shares (at least) memory space and signal handlers with another one. Every such process has a system-wide unique identifier: the PID (Process ID).

As a result, you can renice each "thread" individually because they have their own PID1.


1 See this answer for more information about PID (ProcessID) and TID difference (ThreadID).

| improve this answer | |
7

We should not confuse the process PID and the thread id sometime written TID or in the ps command LPW. The scommand has options to display threads, and under top or htop you switch between threads and process by the H letter. As previously told by @Totor, with NPTL, which is the current implementation with kernel > 2.6, all threads have the same pid, but they have a distinct tid. You show all the threads of a process by:

$ ps -Ljf <pid>

These tid are the names of the directories under /proc/<pid>/task, and even if renice(1) say that its default argument is a pid when applied to a pid it renice only the main thread (this is a bug in linux implementation as written in setpriority(2)), it can also be applied to a tid and it renices the thread. Thats why the answer of @Anton is valid.

But most often there is an easier way to achieve the desired result, all these threads share the same pgid which is the pid of the group leader; you can renice by pgid by issuing:

$ renice -g <pgid>

If you don't want to renice some other process that depends of the same group leader, you have to use the @Anton's recipe:

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

or:

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

You may also want to know what are the other processes of the same group than the process you want to renice, that is the processes which share has the same pgid. You can use ps(1), ps does not allow to select processes by group leader, but you can grep a ps to do it. The processes with pgid 1908 will be given by the command:

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

or if you prefer awk to sed:

$ ps --no-header axo pid,pgid|awk '{if ($2=="1908") print $1;}'
| improve this answer | |
  • This doesn't seem to work correctly on 4.19.4 (Debian Stretch as of now): $ 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;}' Whereas Totor's method does/still works: $ /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 ... I've confirmed with /proc, htop, pstree, etc. that I do have the correct top-level PID. Maybe something has changed in the past year. – Bill McGonigle Feb 6 '19 at 15:15
  • I don't know how you made your test @bill-mcgonigle, I just tried with three kernels 4.9.0 on Debian Stretch; 4.18.0 and 4.19.0 on Debian testing; And it works as I told above. – marcz Feb 12 '19 at 18:20
  • Like I said, Debian Stretch on 4.19.4 with the commands and output shown ; the difference seems to be 4.19.0 vs 4.19.4 but I am surprised there would be much change between such minor versions. – Bill McGonigle Feb 14 '19 at 21:12
  • I guess your process 8524 is the PID of all threaded process TID or LPW, but not the process group, so of course you find all the threads in /proc/8524/task but renice -g fail. When you look at a process tree one branch is in the same process group, not only one threaded process. Try again checking result of ps -Ljf. – marcz Feb 17 '19 at 20:08
1

I'd like to recommend using the -g (process groups) argument instead of the -p (process id's) while using renice. It does the same thing without the bash-foo.

i.e.

(sudo) renice -n <NEW_PRIORITY> -g <MAIN_PROCESS_ID>
| improve this answer | |
  • marcz's answer already mentions this. – Totor Jan 4 '19 at 13:48
1

According to man renice (emphasis mine):

renice alters the scheduling priority of one or more running processes. The first argument is the priority value to be used. The other arguments are interpreted as process IDs (by default), process group IDs, user IDs, or user names. (...)

And indeed, man pgrep demonstrates, as an example:

Example 4: Make all chrome processes run nicer:

$ renice +4 $(pgrep chrome)

According to these, you can combine renice with pgrep's -w | --lightweight switch to renice all threads at once, like so:

renice <priority> $(pgrep -w <process_name>)

And to renice all child processes:

renice <priority> $(pgrep -P $(pgrep <process_name>))

Notice you can also use the -f switch with pgrep to query not only the process name, but the entire command line (needn't be a full match).

| improve this answer | |
  • 1
    It seems to me that this solution doesn't renice children processes of children processes of the main process (not fully recursive, only 2 levels). I found the --lightweight option of pgrep interesting though. – Totor Dec 23 '19 at 19:26
0

Here is a script of mine:

pgrep -v <PROCESS_NAME> | sudo xargs renice <NEW_PRIORITY>
| improve this answer | |
  • 3
    This launches renice on every processes except the one you name. I personnaly consider this command as dangerous and inadequate. – Totor Aug 29 '18 at 12:20
  • 2
    I wonder if he had meant -w not -v – Diablo-D3 Jun 1 '19 at 10:09
  • @Totor While I agree your comment in general, it is dangerous only for users not knowing what are they doing. If you need to work quickly and you know that <PROCESS_NAME> is the only process on your system, that is ok. – peterh - Reinstate Monica Jun 22 at 14:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.