Operating Systems 📂 Scheduling Algorithm · 1 of 5 34 min read

FCFS Scheduling — Step-Wise Animated Numericals

Master First-Come-First-Served CPU scheduling with four fully-worked numericals, each accompanied by a step-wise animated Gantt chart. Watch the chart get built block-by-block while a live narrator explains every dispatch decision. Covers the algorithm, all four metrics (CT, TAT, WT, RT), the notorious convoy effect, CPU idle handling, a Python implementation, plus advantages, disadvantages, and where FCFS still makes sense today.

Section 01

The Story That Explains FCFS

The Bank Teller Queue
Walk into any bank. There is one teller and a rope-line snaking in front of the counter. The rule is simple, ancient, and fair: whoever joins the line first gets served first. No skipping, no priority, no shortcuts. If the person at the front is depositing 500 coins one by one, everyone behind them waits — even the customer with a 30-second cheque deposit.

That is First-Come, First-Served (FCFS). The CPU is the teller. Processes are customers. Arrival time is the moment they joined the rope-line. And nobody — nobody — jumps the queue.

FCFS is the oldest and simplest CPU scheduling algorithm. Galvin introduces it first because it is easy to understand, easy to implement, and provides a clear baseline for comparing every other algorithm. Its logic fits in one line: dispatch processes in the order they arrived at the ready queue.

🎯
The Core Insight

FCFS is non-preemptive: once a process gets the CPU, it keeps it until it voluntarily releases it (by finishing or issuing I/O). No timer interrupt, no priority steal — just pure "you arrived first, you go first".


Section 02

How FCFS Works — The Algorithm

🔄 FCFS Algorithm — Six Simple Steps
Step 1
Maintain the ready queue as a FIFO queue (first-in-first-out) — a simple linked list.
Step 2
When a new process arrives, append its PCB to the tail of the ready queue.
Step 3
When the CPU becomes free, remove the process at the head of the ready queue.
Step 4
Dispatch that process — it runs until it finishes or blocks on I/O. No preemption.
Step 5
If the process finishes, go back to Step 3 and pick the next one.
Step 6
If the process blocks on I/O, put it in the waiting queue. When I/O completes, it re-enters the tail of the ready queue.

Characteristics of FCFS

🔒
Non-Preemptive
No interrupts
Once dispatched, a process holds the CPU until it voluntarily releases it. Even higher-priority arrivals must wait.
⚖️
Fair
FIFO order
Every process runs in arrival order. No process starves — you will eventually get the CPU.
🛠️
Trivial to Implement
One linked list
A single FIFO queue is all the data structure you need. Enqueue and dequeue are O(1).

Section 03

The Metrics — What We Compute

Before touching numericals, memorise these four formulas. Every FCFS problem in Galvin asks for these.

Completion Time
CT = start + burst
The instant a process finishes running on the CPU.
Turnaround Time
TAT = CT − AT
Total time from arrival to completion.
Waiting Time
WT = TAT − BT
Time spent sitting in the ready queue (not running, not doing I/O).
Response Time
RT = start − AT
Time from arrival until first execution on CPU. For non-preemptive FCFS, RT == WT.
🔑
FCFS Shortcut

In FCFS, once a process starts, it runs to completion. So Response Time equals Waiting Time — a process starts running the moment its wait ends.


Section 04

Numerical 1 — Basic FCFS (All Arrive At t=0)

Problem: Three processes arrive at time 0. Compute completion time, turnaround time, waiting time, response time, and averages.

ProcessArrival Time (AT)Burst Time (BT)
P1010
P205
P308

Step-Wise Animated Gantt Chart Construction

Watch The Gantt Chart Get Built Block by Block
FCFS Order: P1 → P2 → P3 P1 (10 ms) P2 (5) P3 (8 ms) 0 10 15 23 Step 1: All three arrive at t=0. Queue order = P1, P2, P3. Step 2: Dispatch P1. Runs 0 → 10. CT(P1)=10, WT(P1)=0, TAT(P1)=10 Step 3: Dispatch P2. Runs 10 → 15. CT(P2)=15, WT(P2)=10, TAT(P2)=15 Step 4: Dispatch P3. Runs 15 → 23. CT(P3)=23, WT(P3)=15, TAT(P3)=23 Averages: TAT = (10+15+23)/3 = 16 ms  |  WT = (0+10+15)/3 = 8.33 ms

The Gantt chart builds live, block by block, with a step-by-step narrator explaining each dispatch decision.

Solution Table

ProcessATBT StartCT TAT = CT−ATWT = TAT−BTRT = Start−AT
P10100101000
P2051015151010
P3081523231515
Averages 16.00 8.33 8.33
FINAL ANSWERS
Average Turnaround Time = 16.00 ms Average Waiting Time = 8.33 ms Average Response Time = 8.33 ms CPU Utilization = 100% (no idle time) Throughput = 3 / 23 = 0.130 processes / ms

Section 05

Numerical 2 — Different Arrival Times

Problem: Four processes arrive at different times. Compute all metrics.

ProcessArrival Time (AT)Burst Time (BT)
P106
P224
P342
P463

Step-Wise Reasoning

01
t=0 — P1 arrives, CPU free
Ready queue: [P1]. Dispatch P1. It will run 0 → 6 (burst = 6).
02
t=2 — P2 arrives while P1 running
Non-preemptive → P1 keeps CPU. P2 joins queue: [P2].
03
t=4 — P3 arrives
Queue tail: [P2, P3]. P1 still running.
04
t=6 — P1 finishes, P4 arrives
CT(P1) = 6. Queue: [P2, P3, P4]. Dispatch P2. Runs 6 → 10.
05
t=10 — P2 finishes
CT(P2) = 10. Queue: [P3, P4]. Dispatch P3. Runs 10 → 12.
06
t=12 — P3 finishes, dispatch P4
CT(P3) = 12. Dispatch P4. Runs 12 → 15. CT(P4) = 15.

Animated Step-Wise Gantt Chart

Building the Gantt Chart Step By Step
Order: P1(0-6) → P2(6-10) → P3(10-12) → P4(12-15) P1 arr P2 arr P3 arr P4 arr P1 P2 P3 P4 0 6 10 12 15 Step 1: P1 arrives first. CPU is free. Dispatch P1 immediately. Step 2: P1 runs 0→6. Meanwhile P2,P3 join queue. CT(P1)=6, WT(P1)=0. Step 3: At t=6, P1 done. P4 arrives now too. Queue: [P2,P3,P4]. Dispatch P2. Step 4: P2 runs 6→10. CT(P2)=10, WT(P2)=8-4=4. Then dispatch P3 (10→12). Step 5: CT(P3)=12, WT(P3)=6. Finally P4 runs 12→15. CT(P4)=15, WT(P4)=6. Averages compute after all 4 are complete: Avg TAT = (6 + 8 + 8 + 9)/4 = 7.75 ms Avg WT = (0 + 4 + 6 + 6)/4 = 4.00 ms

Watch each block appear in real time as its process is dispatched. The narrator explains each step.

Solution Table

ProcessATBT StartCT TATWTRT
P10606600
P224610844
P3421012866
P4631215966
Averages 7.75 4.00 4.00
FINAL ANSWERS
Average Turnaround Time = 7.75 ms Average Waiting Time = 4.00 ms Average Response Time = 4.00 ms CPU Utilization = 15/15 = 100% Throughput = 4 / 15 = 0.267 jobs / ms

Section 06

Numerical 3 — The Convoy Effect

Now watch FCFS's fatal flaw in action. When one long CPU-bound process arrives just before many short I/O-bound ones, everyone piles up behind it — like cars stuck behind a slow truck.

ProcessATBTType
P10100CPU-bound (long)
P211I/O-bound (short)
P321I/O-bound (short)
P431I/O-bound (short)

Animated Convoy Effect

Watch The Convoy Form Behind P1
CONVOY EFFECT — Everyone Waits For P1 P1 — 100 ms (Convoy Leader) P1 arr t=0 P2 arr t=1 0 100 103 Waiting times: WT(P1) = 0    WT(P2) = 99    WT(P3) = 99    WT(P4) = 99 Avg WT = (0 + 99 + 99 + 99) / 4 = 74.25 ms ← three 1-ms jobs each waited 99 ms!

P1 hogs the CPU. Three tiny jobs, each finishable in 1 ms, pile up behind — that is the convoy effect.

🚙
The Convoy Effect Explained

One long process at the head of the queue makes many short processes wait far longer than their own execution time. Total system throughput crashes and average waiting time explodes. This is FCFS's biggest weakness — and the whole motivation for algorithms like SJF and Round Robin.


Section 07

Advantages & Disadvantages

👍 Advantages
Simplest scheduling algorithm — one FIFO queue
Fair in the strict FIFO sense — no starvation
Very low scheduling overhead (O(1) per dispatch)
Easy to reason about — predictable order
Great baseline for comparing other algorithms
👎 Disadvantages
Convoy effect — short jobs wait behind long ones
Poor average waiting time on mixed workloads
Terrible for interactive users — no priority
Non-preemptive — cannot respond to urgency
CPU and I/O device utilization drop when convoys form

Section 08

Python Implementation

from dataclasses import dataclass
from typing import List

@dataclass
class Process:
    pid:        str
    arrival:    int
    burst:      int
    start:      int = -1
    completion: int = -1

def fcfs(procs: List[Process]) -> List[Process]:
    # Sort by arrival time to enforce FIFO order
    procs = sorted(procs, key=lambda p: p.arrival)
    clock = 0
    for p in procs:
        # If CPU idle, jump clock forward to next arrival
        clock         = max(clock, p.arrival)
        p.start       = clock
        p.completion  = clock + p.burst
        clock         = p.completion
    return procs

def report(procs: List[Process]) -> None:
    print(f"{'PID':<4} {'AT':>4} {'BT':>4} {'CT':>4} {'TAT':>4} {'WT':>4} {'RT':>4}")
    tat_total = wt_total = rt_total = 0
    for p in procs:
        tat = p.completion - p.arrival
        wt  = tat - p.burst
        rt  = p.start - p.arrival
        tat_total += tat; wt_total += wt; rt_total += rt
        print(f"{p.pid:<4} {p.arrival:>4} {p.burst:>4} {p.completion:>4} {tat:>4} {wt:>4} {rt:>4}")
    n = len(procs)
    print(f"\nAvg TAT = {tat_total/n:.2f}")
    print(f"Avg WT  = {wt_total/n:.2f}")
    print(f"Avg RT  = {rt_total/n:.2f}")

# --- Run Numerical 2 ---
workload = [
    Process("P1", 0, 6),
    Process("P2", 2, 4),
    Process("P3", 4, 2),
    Process("P4", 6, 3),
]
report(fcfs(workload))
OUTPUT
PID AT BT CT TAT WT RT P1 0 6 6 6 0 0 P2 2 4 10 8 4 4 P3 4 2 12 8 6 6 P4 6 3 15 9 6 6 Avg TAT = 7.75 Avg WT = 4.00 Avg RT = 4.00
🏆
Match!

Numbers match our hand-calculation from Numerical 2 exactly. This tiny function is the entire FCFS algorithm — a stark reminder of how simple it really is.


Section 09

Numerical 4 — With CPU Idle Time

Watch out: if all processes have not yet arrived when the CPU is free, it sits idle. This affects CPU utilization.

ProcessATBT
P103
P254
P3102

Step-Wise Solution

📌 Timeline
t=0-3
P1 runs. CT(P1)=3, WT(P1)=0.
t=3-5
CPU IDLE! No process is ready. 2 ms of wasted time.
t=5-9
P2 arrives at t=5, dispatched immediately. CT(P2)=9, WT(P2)=0.
t=9-10
CPU IDLE again! P3 hasn't arrived. 1 ms wasted.
t=10-12
P3 arrives and runs. CT(P3)=12, WT(P3)=0.
Gantt Chart With Idle Gaps
P1 IDLE P2 IDLE P3 0 3 5 9 10 12 Total time = 12 ms  |  Idle time = 3 ms  |  Busy time = 9 ms CPU Utilization = 9/12 = 75%   (25% wasted!)

Grey dashed blocks are CPU idle time. FCFS cannot fill them — it must wait for the next arrival.

ProcessATBTCTTATWT
P103330
P254940
P31021220
Averages 3.00 ms 0.00 ms
FINAL ANSWERS
Avg TAT = 3.00 ms Avg WT = 0.00 ms (best case!) CPU Utilization = 9 / 12 = 75% Throughput = 3 / 12 = 0.25 jobs / ms Total Idle Time = 3 ms (t=3-5 and t=9-10)

Section 10

When Is FCFS a Good Choice?

Batch Systems
Overnight billing, payroll, scientific number-crunching. Nobody is watching interactively; fairness by arrival is fine.
Legacy mainframes
Uniform Workloads
All jobs have roughly similar burst times. No convoy can form because no job is dramatically longer than the rest.
Homogeneous workers
Simple Embedded Devices
Microcontrollers running a few known tasks in a fixed order. FCFS is dead simple and needs almost no code.
Low-memory MCUs
Interactive Systems
Desktops, phones, laptops — one long job at the queue head freezes the UI. Never use pure FCFS here.
Bad UX guaranteed
Time-Sharing Systems
Many users, mixed workloads. FCFS's convoy effect turns quick jobs into slow jobs. Use Round Robin instead.
Convoy effect kills
Real-Time Systems
Safety-critical deadlines. FCFS gives no priority guarantee — a long low-priority job can miss a critical deadline.
Deadline unsafe

Section 11

Golden Rules

📚 FCFS Scheduling — Non-Negotiable Rules
1
Order is arrival time. If two processes arrive at the exact same time, break ties by PID (or by problem statement). Never re-order for any other reason.
2
FCFS is non-preemptive. Once a process starts, it holds the CPU until it finishes or blocks. New arrivals — however urgent — wait their turn.
3
Response Time equals Waiting Time in FCFS, because a process runs to completion the moment it starts. No preemption means no gap between start and continuous execution.
4
Compute in this order: CT → TAT → WT → RT. Draw the Gantt chart first, then the table. Mistakes come from doing table arithmetic before the chart.
5
Watch for idle CPU time. If the next process's arrival time is greater than the current clock, jump the clock forward — the CPU sat idle. This lowers CPU utilization but keeps WT at 0 for that process.
6
Beware the convoy effect. One long process at the head blows up average waiting time for everyone else. If your Gantt chart shows a huge first block followed by tiny slivers, expect terrible average WT.
7
FCFS never starves — every process eventually runs. This alone makes it a valid baseline algorithm. When Galvin asks "which algorithm never starves?", FCFS is one of the safe answers.