Operating Systems 📂 Scheduling Algorithm · 3 of 5 42 min read

Shortest Remaining Time First (SRTF) Scheduling

A complete Galvin-style walkthrough of the Shortest Remaining Time First (SRTF) CPU scheduling algorithm — the preemptive version of SJF. Learn the algorithm step by step, work through three fully-solved numerical examples with Gantt charts and animated SVG diagrams, see the Python implementation, and understand starvation, aging, and burst-time prediction.

Section 01

The Story That Explains SRTF

The Emergency Room Triage
Imagine you are a nurse in a busy emergency room. Patients walk in throughout the night — each needing a different amount of care. A patient with a small cut arrives (5 minutes of work), then a heart attack patient (needs 40 minutes). You are treating the heart attack when suddenly a patient walks in gasping — they need only 2 minutes of oxygen to stabilise.

Do you keep working on the heart attack? Or do you pause, handle the quick 2-minute case, and then resume the long one?

Any sensible ER nurse pauses. Two minutes of quick help + resuming the long case minimises total waiting across all patients. That single decision — always work on whoever needs the least remaining time, even if it means interrupting a job already in progress — is exactly what Shortest Remaining Time First (SRTF) does inside your CPU.

Shortest Remaining Time First (SRTF) is the preemptive version of Shortest Job First (SJF). At every instant a new process arrives, the scheduler compares its burst time to the remaining time of the currently running process. If the newcomer is shorter, the running process is thrown back into the ready queue and the new one takes the CPU.

💡
The Core Insight

SRTF is provably optimal for minimising average waiting time across all preemptive CPU scheduling algorithms — no other algorithm can beat it when burst times are known in advance. It is the theoretical gold standard Galvin uses as the benchmark for judging every other scheduler in the book.


Section 02

Where SRTF Sits in the Scheduling Family

Before diving into SRTF you need the family tree. CPU scheduling algorithms split first by whether they can preempt — i.e. forcibly take the CPU away from a running process — and then by their selection rule.

🌐 The Scheduling Family Tree
Non-Preemptive
FCFS (First-Come, First-Served), SJF (Shortest Job First), Priority (NP) — once a process starts, it runs to completion.
Preemptive
SRTF, Round Robin, Priority (P), MLFQ — a running process can be forcibly stopped and moved back to the ready queue.
SRTF
Preemptive counterpart of SJF. Selection rule: pick the process with the smallest remaining burst at every scheduling point.
🔑
SJF vs SRTF — The One-Line Difference

SJF looks at the shortest total burst only when the CPU is idle. SRTF looks at the shortest remaining burst every time a new process arrives — and will preempt the current process if a shorter one shows up.


Section 03

Key Terminology (Galvin's Glossary)

⏱️
Arrival Time (AT)
The moment a process enters the ready queue.
Processes rarely arrive together. AT is the wall-clock time (in units) when the process first becomes eligible for the CPU.
🔥
Burst Time (BT)
Total CPU time the process needs.
The exact number of CPU cycles required. In SRTF we assume we know BT in advance — in practice this is estimated from history.
Remaining Time (RT)
BT minus CPU time already consumed.
The dynamic value SRTF cares about. Every time-unit spent on the CPU decreases RT by 1. When RT hits 0, the process terminates.
🏁
Completion Time (CT)
When the process finishes.
Recorded when RT reaches 0. All the important metrics (TAT, WT) are derived from CT.
🔁
Turnaround Time (TAT)
CT − AT
Total time from submission to completion — including waiting, running, and preemption pauses. The user-visible latency.
🕑
Waiting Time (WT)
TAT − BT
Time spent sitting in the ready queue, doing nothing. Minimising average WT is the objective SRTF is provably optimal for.
Turnaround Time
TAT = CT − AT
Completion minus arrival — the full journey through the system.
Waiting Time
WT = TAT − BT
Journey minus useful work — pure idle time in the ready queue.
Response Time
RespT = First-CPU − AT
Delay before the process first touches the CPU. Critical for interactivity.
Average WT
Avg WT = ΣWTi / n
The single number SRTF minimises when all burst times are known.

Section 04

The SRTF Algorithm — Step by Step

⚙️ SRTF Scheduling Rule
Step 1
At every clock tick, check the ready queue and pick the process with the smallest Remaining Time (RT). Ties are broken by arrival time, then by process ID.
Step 2
Run that process for 1 time unit. Decrease its RT by 1.
Step 3
Check if any new process has arrived at this tick. Add it to the ready queue.
Step 4
If a newly arrived process has RT smaller than the running process's remaining time → preempt. The current process goes back to the queue.
Step 5
If a process's RT reaches 0, mark it complete. Record its Completion Time.
Step 6
Repeat until all processes complete. Compute TAT, WT, and averages.
⚠️
The Preemption Trigger

SRTF only makes a scheduling decision on two events: (1) a new process arrives, or (2) the current process completes. Between these events the current process runs uninterrupted. You do NOT re-sort the queue on every tick — only at arrival or completion.


Section 05

Numerical Example 1 — The Classic Galvin Problem

Let's schedule four processes using SRTF. This is the canonical example from Galvin's Operating System Concepts, chapter 6.

Process Arrival Time (AT) Burst Time (BT)
P108
P214
P329
P435

Step-by-Step Trace

📈 Timeline Reasoning
t = 0
Only P1 is in the queue (RT=8). P1 starts running. RT(P1)=8.
t = 1
P2 arrives with BT=4. Compare: RT(P1)=7 vs RT(P2)=4. P2 is shorter → PREEMPT P1. P2 runs.
t = 2
P3 arrives with BT=9. Compare: RT(P2)=3 vs RT(P3)=9. P2 still shortest → P2 keeps running.
t = 3
P4 arrives with BT=5. Compare: RT(P2)=2 vs RT(P4)=5. P2 still shortest → P2 keeps running.
t = 5
RT(P2)=0 → P2 completes. CT(P2)=5. Ready queue: P1(RT=7), P3(RT=9), P4(RT=5). Pick P4 (shortest).
t = 10
RT(P4)=0 → P4 completes. CT(P4)=10. Queue: P1(RT=7), P3(RT=9). Pick P1.
t = 17
RT(P1)=0 → P1 completes. CT(P1)=17. Pick P3.
t = 26
RT(P3)=0 → P3 completes. CT(P3)=26. All done.

Gantt Chart (SVG)

📊 SRTF Gantt Chart — Example 1
P1 P2 P4 P1 P3 0 1 5 10 17 26 PREEMPT Time units →

The red arrow marks a preemption event: P1 is kicked off the CPU at t=1 because P2 (RT=4) is shorter than P1's remaining time (RT=7).

Computing TAT and WT

Process AT BT CT TAT = CT − AT WT = TAT − BT
P1081717 − 0 = 1717 − 8 = 9
P21455 − 1 = 44 − 4 = 0
P3292626 − 2 = 2424 − 9 = 15
P4351010 − 3 = 77 − 5 = 2
FINAL METRICS
Average Turnaround Time = (17 + 4 + 24 + 7) / 4 = 52 / 4 = 13.00 units Average Waiting Time = ( 9 + 0 + 15 + 2) / 4 = 26 / 4 = 6.50 units
Compare with FCFS on the Same Input

FCFS on the same processes gives Average WT = 7.75 units. SRTF gives 6.50 units — a 16% improvement. This gap grows dramatically when short and long jobs are mixed in bursty arrival patterns.


Section 06

Animated Step-by-Step State (SVG)

The diagram below animates the ready queue and running process at each tick. Watch how the queue re-sorts as new processes arrive and how P1 gets kicked off at t=1.

🎬 Animated SRTF Execution
CPU READY QUEUE (sorted by Remaining Time) P1 Clock t = P1 (BT=8) P2 (BT=4) P3 (BT=9) P4 (BT=5) Event log: t=0: P1 arrives → CPU idle → P1 runs (loop restarts every 26 seconds — one full simulation)

Above: an animated single-cycle simulation. Each real second represents one time unit in the schedule. The loop restarts at t=0 every 26s.


Section 07

Numerical Example 2 — Ties, Idle CPU, and Sharp Preemption

A trickier scenario: some processes arrive after a gap (CPU sits idle) and later a very short job appears and preempts a long-running one. Watch how idle time is handled.

ProcessATBT
P107
P224
P341
P454
⏯️ Trace
t = 0
Only P1 (RT=7). P1 runs.
t = 2
P2 arrives (BT=4). Compare: RT(P1)=5 vs RT(P2)=4 → preempt P1. P2 runs.
t = 4
P3 arrives (BT=1). RT(P2)=2 vs RT(P3)=1 → preempt P2. P3 runs.
t = 5
P3 completes (RT=0). CT(P3)=5. P4 arrives (BT=4). Queue: P1(5), P2(2), P4(4). Pick P2.
t = 7
P2 completes. CT(P2)=7. Queue: P1(5), P4(4). Pick P4.
t = 11
P4 completes. CT(P4)=11. Only P1 left. Pick P1.
t = 16
P1 completes. CT(P1)=16.
📊 Gantt Chart — Example 2
P1 P2 P3 P2 P4 P1 0 2 4 5 7 11 16 PREEMPT PREEMPT

P1 is preempted twice — first by P2 at t=2, and P2 is later preempted by the tiny P3 at t=4. P1 waits the longest.

ProcessATBTCTTATWT
P10716169
P224751
P341510
P4541162
METRICS
Average Turnaround Time = (16 + 5 + 1 + 6) / 4 = 28 / 4 = 7.00 units Average Waiting Time = ( 9 + 1 + 0 + 2) / 4 = 12 / 4 = 3.00 units

Section 08

Numerical Example 3 — Practical Scenario (Bank Server)

Online Banking Backend
A bank server receives five transaction requests during peak hour. Each has a known processing cost. The scheduler must decide the execution order to minimise average response time (customers waiting on the app).
RequestTaskAT (ms)BT (ms)
P1Balance query06
P2UPI transfer13
P3Statement PDF gen28
P4Login OTP32
P5Card block41

Trace

👤 Execution Trace
t=0
P1 runs (only one). RT(P1)=6.
t=1
P2 arrives (BT=3). RT(P1)=5 > 3 → preempt. P2 runs.
t=2
P3 arrives (BT=8). RT(P2)=2 < 8 → P2 keeps CPU.
t=3
P4 arrives (BT=2). RT(P2)=1 < 2 → P2 keeps CPU.
t=4
P2 completes (RT=0). CT(P2)=4. P5 arrives (BT=1). Queue: P1(5), P3(8), P4(2), P5(1). Pick P5.
t=5
P5 completes. CT(P5)=5. Queue: P1(5), P3(8), P4(2). Pick P4.
t=7
P4 completes. CT(P4)=7. Queue: P1(5), P3(8). Pick P1.
t=12
P1 completes. CT(P1)=12. Only P3 left.
t=20
P3 completes. CT(P3)=20.
📊 Gantt Chart — Bank Server Example
P1 P2 P5 P4 P1 P3 0 1 4 5 7 12 20 PREEMPT

Notice: P5 (arrived at t=4 with BT=1) completes at t=5 — only 1 ms after arriving. Fast responses for tiny jobs is exactly what SRTF is designed for.

ProcessATBTCTTATWT
P10612126
P213430
P328201810
P432742
P541510
METRICS
Average TAT = (12 + 3 + 18 + 4 + 1) / 5 = 38 / 5 = 7.60 ms Average WT = ( 6 + 0 + 10 + 2 + 0) / 5 = 18 / 5 = 3.60 ms

Section 09

Python Implementation

def srtf_schedule(processes):
    """
    processes: list of dicts with keys 'pid', 'at' (arrival), 'bt' (burst).
    Returns: schedule (list of (time, pid)) and metrics dict.
    """
    n         = len(processes)
    remaining = {p['pid']: p['bt'] for p in processes}
    completion = {}
    schedule   = []
    t          = 0
    completed  = 0

    while completed < n:
        # find processes that have arrived AND still have work
        ready = [p for p in processes
                 if p['at'] <= t and remaining[p['pid']] > 0]

        if not ready:
            schedule.append((t, 'IDLE'))
            t += 1
            continue

        # pick shortest remaining time; ties -> earlier AT, then pid
        current = min(ready, key=lambda p: (remaining[p['pid']], p['at'], p['pid']))
        schedule.append((t, current['pid']))
        remaining[current['pid']] -= 1
        t += 1

        if remaining[current['pid']] == 0:
            completion[current['pid']] = t
            completed += 1

    # metrics
    tat = {p['pid']: completion[p['pid']] - p['at'] for p in processes}
    wt  = {p['pid']: tat[p['pid']] - p['bt']   for p in processes}

    return {
        'schedule':  schedule,
        'completion': completion,
        'tat':       tat,
        'wt':        wt,
        'avg_tat':   sum(tat.values()) / n,
        'avg_wt':    sum(wt.values()) / n
    }


# --- Example 1 (Galvin classic) ---
procs = [
    {'pid': 'P1', 'at': 0, 'bt': 8},
    {'pid': 'P2', 'at': 1, 'bt': 4},
    {'pid': 'P3', 'at': 2, 'bt': 9},
    {'pid': 'P4', 'at': 3, 'bt': 5},
]

result = srtf_schedule(procs)

print("Completion Times:", result['completion'])
print("Turnaround Times:", result['tat'])
print("Waiting Times:  ", result['wt'])
print(f"Avg TAT: {result['avg_tat']:.2f}")
print(f"Avg WT:  {result['avg_wt']:.2f}")
OUTPUT
Completion Times: {'P1': 17, 'P2': 5, 'P3': 26, 'P4': 10} Turnaround Times: {'P1': 17, 'P2': 4, 'P3': 24, 'P4': 7} Waiting Times: {'P1': 9, 'P2': 0, 'P3': 15, 'P4': 2} Avg TAT: 13.00 Avg WT: 6.50
Priority Queue Optimisation

The simple version above is O(n2) — fine for classroom problems but slow at scale. Production schedulers use a min-heap keyed on remaining time, giving O(log n) selection per tick. Linux's CFS uses a red-black tree keyed on virtual runtime — a distant descendant of the SRTF idea.


Section 10

SRTF vs FCFS vs SJF — Head-to-Head

Using the same input from Example 1 (P1..P4, AT 0/1/2/3, BT 8/4/9/5), here is what each algorithm produces:

🔴 FCFS (Non-Preemptive)
ProcessWT
P10
P27
P310
P416
Avg WT8.25
🟢 SRTF (Preemptive)
ProcessWT
P19
P20
P315
P42
Avg WT6.50
PropertyFCFSSJF (Non-Preemptive)SRTF (Preemptive)
Preemption?NoNoYes
Average WTWorstBetterBest
Starvation riskNonePossibleHigh for long jobs
OverheadZeroLowHigh — context switches
Needs BT estimate?NoYesYes
Convoy effectYesNoNo
Best forBatch, simpleBatch, known burstShort interactive jobs

Section 11

Advantages and Disadvantages

Optimal Average Waiting Time
Provably the lowest possible average WT among all preemptive schedulers when burst times are known — a theoretical bound.
optimal lower bound
Excellent for Short Jobs
Interactive jobs (clicks, quick queries) get through the system almost immediately — great for user-facing systems.
low response time
No Convoy Effect
Unlike FCFS, a huge job stuck at the front of the queue cannot block small ones — preemption ensures the small ones jump the queue.
preempts long jobs
Starvation of Long Jobs
If short jobs keep arriving, a long job may sit in the queue forever. Classic example: P3 in our example waited 15 units — imagine that pattern indefinitely.
indefinite postponement
Burst Time Must Be Known
In real systems the OS does NOT know how long a process will run. SRTF requires an estimate — usually an exponential average of prior bursts, which can be wrong.
practical implementation gap
High Context-Switch Overhead
Every preemption costs a context switch — save registers, flush TLB, update page tables. On heavily bursty workloads this cost can wipe out the theoretical gain.
real-world tax

Section 12

Burst Time Prediction — The Missing Piece

Real operating systems cannot see the future. To use SRTF in practice, Galvin describes the exponential averaging technique to predict the next CPU burst from historical data.

Prediction Formula
τn+1 = α·tn + (1−α)·τn
Weighted average of last actual burst tn and previous prediction τn.
Common Choice
α = 0.5
Equal weight to recent history and long-term average — balances stability and responsiveness.
📈
Exponential Averaging Example

With α=0.5 and τ0=10, if bursts are t1=6, t2=4, t3=6, then predictions are τ1=8, τ2=6, τ3=6, τ4=6. Older bursts get exponentially less weight — recent behaviour dominates.


Section 13

When to Use SRTF (and When Not To)

🎯
Batch Systems with Known Bursts
Scientific workloads, ETL pipelines, batch processing — where each job's runtime is estimated from historical logs. SRTF minimises total wait.
scientific + batch
📱
Interactive Systems Prioritising Short Tasks
Web servers, database query optimisers, IDE background tasks — where short interactive jobs must not be blocked by long ones.
low latency UI
📊
Simulation and Analysis
As a theoretical lower bound to benchmark other schedulers against — you always compare Round Robin, MLFQ etc. to SRTF's optimum.
benchmark yardstick
🚫
Real-Time Systems
Real-time needs deadlines, not shortest-first. Use EDF (Earliest Deadline First) or Rate Monotonic instead — a short job might not have the earliest deadline.
deadline-driven instead
🚫
Fairness-Critical Systems
Shared servers where every user must get a fair CPU slice. SRTF starves long jobs — use Round Robin or CFS instead.
fairness > throughput
🚫
Unknown Burst Patterns
When you cannot predict burst times reliably (highly variable workloads), Round Robin or MLFQ give more consistent performance without needing predictions.
unpredictable workloads

Section 14

Solving the Starvation Problem — Aging

⚠️
The Starvation Trap

If a stream of short jobs keeps arriving, a long job may wait indefinitely. Consider: P1 (BT=100) arrives at t=0. Then P2 (BT=1) at t=1, P3 (BT=1) at t=2, P4 (BT=1) at t=3… P1 will be preempted forever.

The classical fix is aging: gradually decrease the "effective remaining time" of a process as it waits. After some threshold its priority becomes so high it MUST run.

# Aging-based SRTF (pseudocode)
def effective_remaining(process, current_time, aging_factor=0.1):
    wait_time = current_time - process.last_run_time
    return process.remaining - aging_factor * wait_time

# A process waiting 100 units with factor 0.1 has its effective RT reduced by 10.
# Eventually even a huge burst gets scheduled.

Section 15

Golden Rules — Galvin's SRTF Checklist

🛠️ SRTF — Non-Negotiable Rules
1
Only make a scheduling decision on process arrival or process completion. Do NOT re-sort the queue every tick — that wastes CPU on scheduling overhead.
2
Break ties in remaining time using arrival time first, then process ID. This keeps the schedule deterministic and reproducible.
3
SRTF is optimal for average waiting time — but only when burst times are known exactly. In practice we estimate with exponential averaging; results are near-optimal, never truly optimal.
4
Watch out for starvation. If your workload has a stream of tiny jobs arriving continuously, long jobs will never run. Add aging to guarantee progress.
5
Every preemption costs a context switch (~1–10 microseconds on modern hardware). Include this overhead in your calculations — SRTF's theoretical gain can be erased on highly bursty workloads.
6
SRTF is the preemptive twin of SJF, not a separate algorithm family. If preemption is not allowed in the exam, use SJF; if it is allowed, use SRTF. Same selection rule, different decision points.
7
Waiting Time in SRTF can be computed as WT = TAT − BT — the burst time cancels out preemption gaps automatically. You do NOT need to sum individual wait intervals manually.
8
Always draw the Gantt chart first, then read off CT from it, then compute TAT and WT. Trying to compute WT directly by inspection is where 90% of exam mistakes happen.
🏆
The One Sentence to Remember

SRTF = SJF + Preemption on arrival. At every arrival, compare the newcomer's burst to the current process's remaining time, and preempt if shorter. That single rule gives you the provably-optimal average waiting time on any workload where burst times are known.