How Do You Check Thread Scheduling Policy in Linux? – Best Linux Device Drivers Course

« 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:

Userspace vs Kernel Naming for a Process with 3 Threads
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>/sched prio 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_RTPRIO can 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,comm is 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_OTHER under 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

« Previous Lecture Next Lecture »

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *