« Previous Lecture | Next Lecture »
How to Check Thread Scheduling Policy and Priority in Linux (chrt, ps, /proc and C Code)
Part 3 of our free Linux kernel development course — practical, hands-on querying of every thread on your system
Knowing how to check the thread scheduling policy and priority in Linux is a daily skill for embedded engineers: is that audio glitch caused by a missing real-time priority? Did a vendor driver spawn a rogue SCHED_FIFO thread hogging a core? In this hands-on lecture of our free Linux kernel programming course, you will learn every practical way to query thread scheduling policy and priority in Linux — with chrt, ps, the /proc filesystem, a complete shell script, and a small C program — all verified on modern kernels (6.6+ with EEVDF, including the current 7.x series).
What You Will Learn
- Reading policy and priority with
chrt -p - Listing every thread’s policy system-wide with
ps - Decoding scheduling data straight from
/proc - Understanding PID vs TID (and the kernel’s TGID naming twist)
- Writing a shell script and a C program that report all of this
- Interpreting real systems: why almost everything is
SCHED_OTHER
Prerequisites
- Lectures 1 and 2 of this series (scheduling classes; the EEVDF fair scheduler)
- A Linux shell; root access only needed for a couple of optional commands
PID vs TID: One Minute of Naming Pain
Userspace and kernel use conflicting names, and every Linux device drivers course student trips on it once:
| Process “app” — userspace PID 4200 (kernel calls this the TGID) | |||
| Main thread TID 4200 |
Worker thread TID 4201 |
Worker thread TID 4202 |
|
| Kernel view | kernel “PID” 4200 TGID 4200 |
kernel “PID” 4201 TGID 4200 |
kernel “PID” 4202 TGID 4200 |
Rules of thumb when reading thread listings:
- If a row’s PID equals its TID, that row is the main thread of the process.
- If that PID appears only once in the whole listing, the process is single-threaded.
- Multiple rows sharing one PID with different TIDs are the process’s worker threads.
Method 1: chrt — the Quickest Way to Check Thread Scheduling Policy and Priority in Linux
The chrt(1) utility (util-linux package) both queries and sets policy/priority. Query mode uses -p:
# Query PID 1 (systemd / init)
$ chrt -p 1
pid 1's current scheduling policy: SCHED_OTHER
pid 1's current scheduling priority: 0
# Query a specific THREAD by its TID (works the same way)
$ chrt -p 4201
# What policies and priority ranges does this kernel support?
$ chrt -m
SCHED_OTHER min/max priority : 0/0
SCHED_FIFO min/max priority : 1/99
SCHED_RR min/max priority : 1/99
SCHED_BATCH min/max priority : 0/0
SCHED_IDLE min/max priority : 0/0
SCHED_DEADLINE min/max priority : 0/0
Note that normal threads always report priority 0 — their real “priority” is the nice value, which chrt does not show (use ps or top for nice).
Method 2: ps — Thread Scheduling Policy and Priority in Linux, System-Wide
GNU ps can print policy and RT priority as columns, and -L expands threads:
# All threads on the system with policy (cls) and RT priority (rtprio)
$ ps -eLo pid,tid,cls,rtprio,ni,comm
# Only real-time threads (FIFO or RR) - great for auditing embedded boards
$ ps -eLo pid,tid,cls,rtprio,comm | awk '$3=="FF" || $3=="RR"'
The cls column decodes as:
| ps cls code | Policy | Priority shown where |
|---|---|---|
| TS | SCHED_OTHER (time-sharing) | nice column (ni) |
| FF | SCHED_FIFO | rtprio column (1–99) |
| RR | SCHED_RR | rtprio column (1–99) |
| B | SCHED_BATCH | nice column |
| IDL | SCHED_IDLE | — |
| DLN | SCHED_DEADLINE | runtime/deadline/period (not in ps) |
Run the audit command on any desktop or embedded board and you will see the pattern predicted in lecture 1: the overwhelming majority of threads are TS (SCHED_OTHER, handled by the EEVDF fair class), with a small set of kernel real-time threads such as the per-CPU migration threads at FIFO priority 99 and things like watchdog or IRQ threads at moderate FIFO priorities. Deadline threads are rare on general systems.
Method 3: Thread Scheduling Policy and Priority in Linux from /proc
No tools needed — the kernel exposes every thread scheduling policy and priority in Linux directly through the /proc filesystem:
# Human-readable scheduler info for a thread (policy and prio near the end)
$ grep -E "^policy|^prio" /proc/1/sched
prio : 120
policy : 0
# Per-thread view: each thread of PID 4200 has its own entry
$ ls /proc/4200/task/
4200 4201 4202
$ grep "^policy" /proc/4200/task/4201/sched
Two decoder rings you need. Policy numbers: 0 = OTHER, 1 = FIFO, 2 = RR, 3 = BATCH, 5 = IDLE, 6 = DEADLINE, 7 = EXT. And the kernel’s internal prio scale runs 0–139: values 0–99 are real-time (lower number = higher priority; internal prio = 99 − rtprio), while 100–139 map the nice range −20…+19 (nice 0 = prio 120).
Build It Yourself #1: A System-Wide Audit Script
This original script reports the thread scheduling policy and priority in Linux for the entire system — a handy tool for embedded bring-up:
#!/bin/bash
# sched_audit.sh - list every thread's scheduling policy and priority
# Tested on kernels 6.6+ (EEVDF era). Requires GNU ps.
printf "%-8s %-8s %-18s %-6s %-7s %-5s\n" \
"PID" "TID" "NAME" "CLS" "RTPRIO" "NICE"
ps -eLo pid=,tid=,comm=,cls=,rtprio=,ni= --sort=pid |
while read -r pid tid name cls rtprio nice; do
# Indent worker threads (tid != pid) for readability
if [ "$pid" != "$tid" ]; then
printf "%-8s %-6s %-18s %-6s %-7s %-5s\n" \
"$pid" "$tid" "$name" "$cls" "$rtprio" "$nice"
else
printf "%-8s %-8s %-18s %-6s %-7s %-5s\n" \
"$pid" "$tid" "$name" "$cls" "$rtprio" "$nice"
fi
done
echo
echo "Real-time threads (audit these on embedded targets):"
ps -eLo tid,cls,rtprio,comm | awk 'NR==1 || $2=="FF" || $2=="RR"'
$ chmod +x sched_audit.sh
$ ./sched_audit.sh | head
Build It Yourself #2: Querying from C
Programmatically, the classic APIs are sched_getscheduler(2) and sched_getparam(2); the modern one-stop API is sched_getattr(2), which also understands deadline parameters. Here is a compact, original example using the portable pair:
/* query_sched.c - print scheduling policy and priority of a given PID/TID
* Build: gcc -Wall -o query_sched query_sched.c
* Usage: ./query_sched [pid] (defaults to itself)
*/
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <errno.h>
#include <string.h>
static const char *policy_name(int pol)
{
switch (pol) {
case SCHED_OTHER: return "SCHED_OTHER";
case SCHED_FIFO: return "SCHED_FIFO";
case SCHED_RR: return "SCHED_RR";
case SCHED_BATCH: return "SCHED_BATCH";
case SCHED_IDLE: return "SCHED_IDLE";
#ifdef SCHED_DEADLINE
case SCHED_DEADLINE: return "SCHED_DEADLINE";
#endif
default: return "UNKNOWN";
}
}
int main(int argc, char *argv[])
{
pid_t pid = (argc > 1) ? (pid_t)atol(argv[1]) : 0; /* 0 = self */
struct sched_param sp;
int pol = sched_getscheduler(pid);
if (pol == -1) {
fprintf(stderr, "sched_getscheduler: %s\n", strerror(errno));
return EXIT_FAILURE;
}
if (sched_getparam(pid, &sp) == -1) {
fprintf(stderr, "sched_getparam: %s\n", strerror(errno));
return EXIT_FAILURE;
}
printf("target : %s\n", pid ? argv[1] : "self");
printf("policy : %s\n", policy_name(pol));
printf("rt priority : %d\n", sp.sched_priority);
printf("prio range : %d to %d for this policy\n",
sched_get_priority_min(pol), sched_get_priority_max(pol));
return EXIT_SUCCESS;
}
$ gcc -Wall -o query_sched query_sched.c
$ ./query_sched 1
target : 1
policy : SCHED_OTHER
rt priority : 0
prio range : 0 to 0 for this policy
Setting (not just reading) policy and priority from code — sched_setscheduler(), sched_setattr(), and pthread_setschedparam() — is the topic of the next lecture in this free Linux kernel development course, along with the CAP_SYS_NICE permission rules.
Common Mistakes and Troubleshooting
- Querying the process when you meant a thread.
chrt -p <PID>reports the main thread only. Pass the specific TID for workers. - Reading kernel prio as userspace priority.
/proc/<pid>/schedprio 120 means nice 0, not “priority 120”. - Expecting a nonzero priority for SCHED_OTHER. It is always 0; check the nice column instead.
- Old
sysctl kernel.sched_*tunables missing. On EEVDF-era kernels many moved to/sys/kernel/debug/sched/; scripts from pre-6.6 guides may need updating. - chrt “failed to set” errors when experimenting: setting RT policies needs root or
CAP_SYS_NICE; querying does not.
Security Considerations
- Any user can query any thread’s policy, but changing policy/priority of other tasks (or raising to RT) requires
CAP_SYS_NICE— by design, since an RT CPU hog can freeze a system. - On production embedded devices, restrict which services get
CAP_SYS_NICE(e.g., via systemd unit hardening) and audit RT threads with the script above. RLIMIT_RTPRIOcan grant unprivileged users a bounded RT ceiling — prefer it over blanket capabilities for audio-style workloads.
Key Takeaways
- To check thread scheduling policy and priority in Linux,
chrt -p <TID>is the fastest single-thread query;ps -eLo pid,tid,cls,rtprio,ni,commis the fastest system-wide audit. - PID = TID identifies a main thread; shared PID with many TIDs identifies worker threads.
- Everything is also readable raw from
/proc/<pid>/task/<tid>/sched. - On real systems nearly all threads run
SCHED_OTHERunder the EEVDF fair class, with a handful of kernel RT threads.
Conclusion
You can now inspect the scheduling state of any thread on any modern Linux system — from a one-liner to a reusable audit script to portable C code. This completes the “read” half of scheduling control. The next lecture in this free Linux kernel programming course covers the “write” half: programmatically setting policies and priorities safely, including SCHED_DEADLINE via sched_setattr() — essential knowledge for anyone following a free linux device drivers course or building latency-sensitive embedded products.
FAQ
How do I check the scheduling policy of a process in Linux?
Run chrt -p <PID>. It prints the policy (e.g., SCHED_OTHER) and the real-time priority of that task.
How do I see the scheduling policy of every thread on the system?
Use ps -eLo pid,tid,cls,rtprio,ni,comm. The -L flag lists threads and cls shows the policy class code.
What is the difference between PID and TID?
In userspace, PID identifies the process and TID identifies a thread. Internally the kernel calls the thread ID “PID” and the process ID “TGID” — the /proc interface translates for you.
Why is my thread’s priority always 0?
Because it runs SCHED_OTHER. Normal threads have RT priority 0 by definition; their relative importance is the nice value (−20 to +19).
Which system call reads a thread’s scheduling policy in C?
sched_getscheduler(2) returns the policy and sched_getparam(2) the RT priority; sched_getattr(2) returns everything including deadline parameters in one call.
Do I need root to query scheduling information?
No — querying is unprivileged. Root or CAP_SYS_NICE is needed only to change policies/priorities or to read some debugfs files.
Does EEVDF change how I query policy and priority?
No. The policies, chrt, ps, /proc, and the syscalls are unchanged; EEVDF only changed how the fair class picks among SCHED_OTHER threads internally.
Continue the free Linux kernel programming course on EmbeddedPathashala

2 Comments