CPU Scheduling & Affinity

CFS scheduler, nice/renice, ionice, taskset for CPU affinity, numactl, and real-time priorities.

Overview

Overview The Linux CPU scheduler determines which process runs on which CPU at any given moment. Understanding the scheduler helps optimize performance for both latency-sensitive and throughput-oriented workloads.

CFS (Completely Fair Scheduler): The default scheduler since kernel 2.6.23. CFS tracks "virtual runtime" (vruntime) for each runnable process — how much CPU time it has received, weighted by its nice value. At each scheduling decision, the process with the lowest vruntime runs next. This ensures fairness: every process gets proportional CPU time relative to its weight.

Nice values: Range from -20 (highest priority, gets more CPU time) to +19 (lowest priority, "background" process). Default is 0. Each nice level changes weight by about 10%. nice -n 10 ./command starts a command with nice 10. renice -n 5 -p PID changes a running process. Non-root users can only increase nice (lower priority), not decrease below their starting value.

ionice — I/O scheduling: Linux's I/O scheduler also supports priority classes. Class 1 (real-time): gets I/O first, time-slice based. Class 2 (best-effort): default, priority levels 0-7. Class 3 (idle): only gets I/O when no other class wants disk access — perfect for background backups and indexing. ionice -c 3 -p PID changes a running process.

taskset — CPU affinity: Bind a process to specific CPUs using a CPU mask or list. taskset -c 0,1 command runs on CPUs 0 and 1 only. taskset -c 0 -p PID sets affinity of running process. Reduces cache misses by keeping process on same core (hot cache). Essential for latency-sensitive applications.

numactl: On multi-socket servers, memory access to a remote NUMA node is slower. numactl --cpunodebind=0 --membind=0 command keeps process and its memory on NUMA node 0. numactl --hardware shows NUMA topology. numastat shows per-node memory statistics.

Real-time scheduling: chrt -f 99 command uses FIFO scheduling at priority 99 (highest). chrt -r 50 command uses Round-Robin at priority 50. Real-time processes preempt all normal processes. Dangerous if a bug causes a busy loop — can lock up the system. Requires CAP_SYS_NICE or root.

Key concepts

CFS vruntime fairness
CFS tracks vruntime (weighted CPU time) per process. Lowest vruntime runs next. Nice value multiplies the weight: nice -20 gets 1024 weight units, nice +19 gets 15. Proportional fair scheduling.
nice -20 to +19
nice -20 = highest priority (more CPU weight). nice +19 = lowest. nice -n N cmd starts with nice N. renice -n N -p PID changes running process. Only root can go negative.
ionice classes
ionice -c 1 = real-time I/O (gets disk first). ionice -c 2 = best-effort (default, priority 0-7). ionice -c 3 = idle (only when nothing else wants I/O). Use class 3 for backups.
taskset CPU affinity
taskset -c 0,1 cmd — bind to CPUs 0 and 1. taskset -c 0 -p PID — change running process. Keeps hot cache on same core. Use numactl for full NUMA-aware placement.

When to use it

  • Protecting latency-sensitive services from noisy neighbor processes
  • Running background jobs (backups, reindexing) without impacting production
  • Optimizing cache performance for CPU-intensive applications
  • NUMA-aware placement on multi-socket servers

Interview questions

  • How does the CFS scheduler ensure fairness? What role does the nice value play?
  • You have a latency-sensitive service and a batch job on the same server. How do you configure scheduling to protect the service?

Sign up free to read the full topic

More Linux topics

Start free on Cariara · Coding problems · System design studio