What do the Linux real-time scheduling policies SCHED_FIFO and SCHED_RR change about how a thread is scheduled, and what can a runaway SCHED_FIFO thread do to the machine?
answer
- strict priority, not fair share
- the scale runs the opposite way to nice
- the two differ only among equals
- one of them never gives up voluntarily
- the kernel keeps a five percent escape hatch
basics
~20 sSCHED_FIFO and SCHED_RR give a thread a static priority of 1-99 that preempts every normal task; FIFO runs until it blocks or yields, RR adds a time slice between equal priorities. A runaway FIFO loop starves everything below it, which is why Linux throttles real-time tasks by default.
solid answer
~50 sBoth policies replace fair sharing with strict static priority. A thread set to `SCHED_FIFO` or `SCHED_RR` carries a static priority from 1 to 99, and any runnable real-time thread preempts every `SCHED_OTHER` task on that CPU regardless of nice values — normal tasks effectively sit at priority 0. The difference between the two is only what happens among threads of *equal* priority: `SCHED_FIFO` runs until it blocks, yields or is preempted by something higher, while `SCHED_RR` round-robins equal-priority threads through a time slice. You set them with `chrt`, and doing so requires `CAP_SYS_NICE` or a raised `RLIMIT_RTPRIO`. The danger is that a `SCHED_FIFO` thread that never blocks — a spin loop, or a bug — will hold its CPU indefinitely and starve everything of lower priority. Linux mitigates this with real-time throttling: by default `kernel.sched_rt_runtime_us` is 950000 against a `kernel.sched_rt_period_us` of 1000000, capping real-time tasks at 95% of each period so a login shell can still get in.
code
bash · 8 lines# start a thread under SCHED_FIFO at static priority 50
chrt -f 50 ./control-loop
# inspect the policy and priority of a running process
chrt -p 4242
# the throttle that stops a runaway real-time task hanging the box
sysctl kernel.sched_rt_runtime_us kernel.sched_rt_period_usgo deeper
Know that Linux has real-time policies above ordinary scheduling, that their priorities run 1 to 99 with higher winning, and that setting one requires privilege.
Explain that any runnable real-time thread preempts all normal tasks, and that FIFO versus RR matters only among threads of equal priority, where RR adds a time slice.
Show the operational judgment: a non-blocking FIFO thread starves its CPU, real-time throttling caps the class at 95% of each period by default, and page faults or blocking calls undermine the whole point.
Own the policy decision — when a workload genuinely needs real-time guarantees versus isolation or capacity, who may hold CAP_SYS_NICE, and why SCHED_DEADLINE's admission control is a stronger contract than a priority number.
## Two schedulers in one kernel Linux runs several scheduling classes in a fixed order of precedence. Simplified: deadline tasks first, then real-time tasks (`SCHED_FIFO`, `SCHED_RR`), then normal fair-share tasks (`SCHED_OTHER`, plus `SCHED_BATCH` and `SCHED_IDLE`). The scheduler always takes work from the highest non-empty class. That is the whole story of what real-time policies buy you: **if a real-time thread is runnable on a CPU, no normal task on that CPU runs at all.** Note what "real-time" means here. It is not "fast" and it is not a hard guarantee from a real-time operating system. It is *predictable preemption ordering*: a promise about who wins, not about how many microseconds anything takes. ## Static priority 1-99 Real-time threads carry a **static priority** from 1 (lowest) to 99 (highest). Higher wins, which is the opposite direction from nice values — a classic point of confusion, since nice runs -20 (best) to 19 (worst) while real-time priority runs 1 (worst) to 99 (best). Normal tasks are conceptually static priority 0, below every real-time thread. Nice values are meaningless for a real-time thread. It is scheduled purely by its static priority and its policy. ## FIFO versus RR The two policies differ in exactly one respect: behaviour among threads of the same static priority. - **`SCHED_FIFO`** has no time slice. A running FIFO thread continues until it blocks (on I/O, a lock, a sleep), voluntarily yields, or a higher-priority thread becomes runnable. An equal-priority thread waits, however long that takes. - **`SCHED_RR`** is identical except that a thread that exhausts its time slice goes to the back of the queue *for its priority level*, letting equal-priority peers take turns. The slice length is exposed through `kernel.sched_rr_timeslice_ms`. Against threads of *different* priority, both behave the same: the highest runnable priority always runs. ## Setting it ```bash chrt -f 50 ./control-loop # start under SCHED_FIFO, static priority 50 chrt -r 10 ./poller # start under SCHED_RR, static priority 10 chrt -p 4242 # show the policy and priority of a running PID chrt -o -p 0 4242 # put it back to SCHED_OTHER ``` Granting real-time priority is a privileged act: it needs `CAP_SYS_NICE`, or a per-user `RLIMIT_RTPRIO` ceiling configured for the account. That gate exists precisely because of the next section. ## The runaway problem Consider a `SCHED_FIFO` thread at priority 50 that enters a busy loop and never blocks. On its CPU it will run forever. Every normal task on that CPU — your shell, the monitoring agent, the SSH daemon's worker — is at priority 0 and never gets scheduled there. On a single-CPU machine this historically meant a hang with no way in: you could not even start a shell to kill it. Linux ships a safety valve, **real-time throttling**, controlled by two sysctls: - `kernel.sched_rt_period_us`, default 1000000 (one second) - `kernel.sched_rt_runtime_us`, default 950000 Real-time tasks may consume at most 950 ms of every 1000 ms period; in the remaining 5% the class is forced to yield so lower-priority work can run. That is enough for an operator to log in and fix things, and it turns a hard hang into a severe slowdown. Setting the runtime to -1 disables throttling entirely — occasionally done on tuned real-time systems, and a foot-gun everywhere else. The kernel logs a message when it throttles, which is a useful breadcrumb during an incident. ## The other real-time hazards - **Priority inversion.** A high-priority real-time thread blocks on a lock held by a low-priority thread, which cannot run because a mid-priority thread is hogging the CPU. The fix is priority-inheritance mutexes, which temporarily lift the holder's priority. - **Page faults.** A real-time thread that takes a major fault waits on storage like anything else, which is why real-time applications lock their memory in advance rather than trusting the scheduler alone. - **Blocking calls.** Any logging, allocation or syscall that can sleep reintroduces exactly the unpredictability the policy was meant to remove. ## SCHED_DEADLINE Above the real-time class sits `SCHED_DEADLINE`, an earliest-deadline-first policy where a thread declares a runtime, a deadline and a period rather than a priority. The kernel performs admission control and refuses the request if the declared budget cannot be guaranteed — a genuinely stronger contract than "priority 99 and hope". It is set through `sched_setattr(2)` and is worth naming if the interviewer pushes further. ## What an interviewer is checking That you know real-time means strict preemption ordering rather than speed, that you can state the one real difference between FIFO and RR without waffling, and above all that you respect the operational hazard: real-time priority is a privileged, machine-endangering setting whose only default protection is the 95% throttle.
- What is priority inversion, and why is it worse under SCHED_FIFO than under normal fair scheduling?A high-priority thread blocks on a lock held by a low-priority thread, which then cannot run because a mid-priority thread occupies the CPU — so the high-priority thread waits on the mid one indirectly. Under fair scheduling the holder still gets CPU eventually and the stall is bounded; under strict real-time priority it may never run. Priority-inheritance mutexes fix it by lifting the holder's priority.
- How does SCHED_DEADLINE differ from setting a very high SCHED_FIFO priority?SCHED_DEADLINE takes a declared runtime, deadline and period instead of a priority, schedules by earliest deadline, and applies admission control — the kernel refuses a thread whose budget cannot be met alongside existing deadline tasks. That is an enforced contract with a bounded budget, whereas a FIFO priority is only a relative ordering with no limit on how long the thread runs.
- Why would setting kernel.sched_rt_runtime_us to -1 be dangerous on a general-purpose server?It disables real-time throttling entirely, so a real-time thread that never blocks can hold its CPU indefinitely. On a machine where that thread is buggy you lose the 5% window that lets a shell, the monitoring agent, or a remote login get scheduled — turning a recoverable slowdown into a box that must be power-cycled.
- A team sets their service to SCHED_FIFO priority 99 to make it faster. What do you tell them?Real-time policy changes who preempts whom, not how quickly code executes. If the service is not CPU-starved it gains nothing, and at priority 99 it now preempts kernel-supporting threads and every management process on the CPU. The realistic outcomes are unchanged latency plus a new class of incident, so the correct question is what is actually stalling them.
saying these in an interview costs you the question
- Says real-time policies make the code run faster
- Thinks SCHED_FIFO and SCHED_RR differ across priority levels
- Believes nice values still apply to real-time threads
- Assumes a real-time thread can never hang the machine
- Treats real-time priority as an unprivileged tuning knob