Skip to content

About

Implemented a CPU scheduler from scratch in C , comparing 4 classing algorithms

Resources

Stars

5 stars

Watchers

0 watching

Forks

Latest commit

 

History

13 Commits

Folders and files

Repository files navigation

cpu-scheduler

A CPU process scheduler implemented from scratch in C, featuring four classic scheduling algorithms with a clean modular architecture.

What it does

Simulates how an operating system decides which process runs on the CPU and for how long. Each algorithm makes a different tradeoff between fairness, throughput, and responsiveness.

Algorithm Strategy Preemptive Best for
FCFS Run in arrival order No Simple batch jobs
SJF Run shortest job first No Minimizing avg wait time
Round Robin Fixed time slices Yes Interactive / fair scheduling
Priority Run highest priority first No Real-time or tiered workloads

Project structure

Project structure

scheduler/
├── Makefile
├── main.c                  ← entry point, process definitions
├── include/
│   ├── pcb.h               ← Process Control Block struct & state enum
│   ├── queue.h             ← ready queue interface
│   └── scheduler.h         ← scheduler function declarations
└── src/
    ├── pcb.c               ← PCB init and print helpers
    ├── queue.c             ← linked-list ready queue implementation
    ├── stats.c             ← compute and print wait/turnaround metrics
    ├── fcfs.c              ← First Come First Served
    ├── sjf.c               ← Shortest Job First
    ├── rr.c                ← Round Robin
    └── priority.c          ← Priority scheduling

Each algorithm lives in its own file. Adding a new algorithm means creating one .c file — nothing else changes.

File dependencies

How the files #include each other and what main.c links against at compile time — the diagram above (middle section) shows this in full.

Build and run

Requirements: GCC and Make.

# Build
make

# Run
./scheduler

# Clean
make clean

Or compile manually:

gcc -Wall -Iinclude -o scheduler main.c src/pcb.c src/queue.c \
    src/stats.c src/fcfs.c src/sjf.c src/rr.c src/priority.c

Sample output

=== FCFS ===
  PID  Arrival  Burst  Start  Finish  Wait  Turnaround
  P1   arr=0    burst=6  start=0   finish=6   wait=0   tat=6
  P2   arr=1    burst=4  start=6   finish=10  wait=5   tat=9
  ...
  Avg wait: 7.60  |  Avg TAT: 12.20

=== SJF (non-preemptive) ===
  ...
  Avg wait: 6.00  |  Avg TAT: 10.60

SJF produces the lowest average wait time by running the shortest available job first. Round Robin has the highest average wait but gives every process a turn quickly — better for interactive systems where responsiveness matters more than throughput.

How it works at runtime

The diagram above (bottom section) shows the full scheduling flow — from main.c defining processes, through pcb_reset, the clock loop, the pick() function each algorithm implements, and finally print_stats.

Core concepts

Process Control Block (PCB) — the struct that represents a process. Tracks pid, arrival time, burst time, priority, state, and timing stats.

Ready queue — a linked list of processes that have arrived and are waiting for CPU time. The scheduler picks from this queue on every scheduling decision.

Context switch — saving one process's state and restoring another's. Simulated here by updating PCB fields; in a real kernel this saves/restores CPU registers.

Starvation — Priority and SJF can starve low-priority or long processes if higher-priority ones keep arriving. The fix is aging: gradually increasing priority of waiting processes over time.

Metrics

  • Waiting time = time spent in the ready queue = turnaround − burst
  • Turnaround time = total time from arrival to completion = finish − arrival
  • Avg wait and Avg TAT are computed across all processes after each run

Customizing processes

Edit the process table in main.c:

PCB base[] = {
    /* pid  arrival  burst  remaining  priority */
    {1,  0,  6,  6,  3},
    {2,  1,  4,  4,  1},
    {3,  2,  2,  2,  4},
};

Change the TIME_QUANTUM for Round Robin in include/scheduler.h:

#define TIME_QUANTUM 2

What to build next

  • SRTF (Shortest Remaining Time First) — preemptive version of SJF, preempts the running process when a shorter one arrives
  • Aging — prevent starvation in Priority scheduling by incrementally boosting priority of waiting processes
  • Multi-level feedback queue — multiple queues with different quanta, processes move between them based on CPU usage behavior
  • Real context switching — use POSIX ucontext_t (makecontext / swapcontext) for actual coroutine-style switching
  • I/O bursts — alternate CPU and I/O phases to make the waiting state real
  • Gantt chart output — print a visual timeline of which process ran at each ticks

About

Implemented a CPU scheduler from scratch in C , comparing 4 classing algorithms

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages