CPU scheduling: how the CPU is shared

Run queues and time slices, priorities and nice values, preemption and why a process stops, real-time scheduling classes, and how to read load average correctly.

Runnable, running, blocked

A thread is runnable when it wants the CPU, running when it has one, and blocked when it is waiting for I/O, a lock or a timer. The scheduler only chooses among runnable threads, so a machine can be busy and idle at the same time.

StateMeaningSeen as
RunningExecuting on a coreR in ps
RunnableWaiting for a coreR, competing for the run queue
Interruptible sleepWaiting for I/O or a signalS in ps
Uninterruptible sleepWaiting on I/O, cannot be interruptedD in ps — a high count is a red flag
StoppedSuspended by a signalT in ps
ZombieExited, not yet reapedZ in ps
ps -eo pid,stat,pcpu,pmem,comm --sort=-pcpu | head
# STAT  R  running            S  sleeping        D  uninterruptible
#       T  stopped            Z  zombie

uptime
# 10:30:01 up 4 days,  1 user,  load average: 8.12, 4.03, 2.00
# the three figures are 1, 5 and 15 minute averages

Time slices, priorities and preemption

  • Each thread gets a time slice; when it expires or the thread blocks, the scheduler picks the next runnable thread.
  • A preemptive scheduler can take the CPU away mid-instruction-stream, so no thread can monopolise a core.
  • The completely fair scheduler on Linux tracks virtual runtime per thread and favours the one that has run least.
  • Nice values range from -20 (most favourable) to +19. Each step changes the share of CPU a thread receives when contended.
  • CPU affinity pins a thread to a set of cores, which helps cache locality and hurts balance if overused.
nice -n 10 ./batch-job              # start at a lower priority
renice -n 5 -p 12345               # change a running process

taskset -c 0-3 ./server            # restrict to cores 0 through 3
chrt -f 50 ./realtime-worker       # FIFO real-time class, priority 50

# what the running thread is waiting for
cat /proc/12345/status | grep -E "State|voluntary"
cat /proc/12345/sched | head
ClassBehaviourRisk
SCHED_OTHERFair sharing, defaultNone
SCHED_BATCHFair, tuned for throughputLower interactivity
SCHED_IDLERuns only when nothing else wants the CPUStarves under load
SCHED_FIFORuns until it blocks or yieldsCan hang the machine if it spins
SCHED_RRRound robin within a priorityStill higher priority than any fair thread
SCHED_DEADLINEReserves CPU bandwidth by deadlineRequires correct parameters or it is rejected

Reading load average without being misled

Load average counts runnable and uninterruptible threads. On Linux it includes threads blocked in D state, which is why a machine with heavy disk I/O can show a high load while CPU usage is low.

nproc                              # compare load against the core count
uptime
vmstat 1 5                         # r = runnable, b = blocked, then CPU breakdown
# procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
#  r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
#  2  1      0 812345  12000 640000    0    0    12   240  500 1200 15  3 80  2  0
#                                                                        ^wa = I/O wait
  • A load of 8 on an 8-core machine is fully busy; the same load on 2 cores is four times oversubscribed.
  • High wa in vmstat means threads are waiting on I/O, not computing.
  • A single-threaded bottleneck can show a load near 1 with idle cores everywhere.
  • Container CPU quotas change the picture: the host may be idle while your cgroup is throttled.
⚠️
High load and high CPU are not the same problem. Diagnose which of the three you have — runnable threads, uninterruptible threads or I/O wait — before adding capacity, or you will scale the wrong resource and see no improvement.

FAQ

Should I use real-time scheduling in production?
Only for small, well-understood work that must meet a deadline, and only on cores excluded from general work. A real-time thread that spins without blocking can make the machine unreachable.
Why does my process show 400 percent CPU?
It is using four cores. CPU percentages are per core, so a value above 100 means the process is multi-threaded rather than that the measurement is wrong.

Synchronisation: locks, mutexes and semaphores Monitoring and troubleshooting

Last refreshed 2026-09-18.