Build a CPU scheduling simulator implementing FCFS, SJF, SRTF, Priority, and Round Robin algorithms with Gantt chart generation, performance metrics, and comparison analysis.
Project Overview
In this project, you will build a complete CPU scheduling simulator that implements five major algorithms, generates Gantt charts, and calculates all performance metrics. The simulator accepts a list of processes with arrival times and burst times, runs each algorithm, and produces a comparative analysis showing which algorithm performs best for the given workload.
This is one of the most valuable OS lab projects because it forces you to understand the exact mechanics of each algorithm — not just the concept, but the precise decision logic at every time unit.
Design Architecture
Input → [Process Queue] → Scheduler → [Gantt Chart] → Metrics Calculator → Output
↓
Algorithm Selection
(FCFS / SJF / SRTF / Priority / RR)
Data Structures
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#define MAX_PROCESSES 50
typedef struct {
int pid;
int arrival_time;
int burst_time;
int priority; // for priority scheduling
int remaining_time; // for preemptive algorithms
int start_time; // first time process gets CPU
int completion_time;
int waiting_time;
int turnaround_time;
int response_time;
int started; // flag for response time calculation
} Process;
typedef struct {
int pid;
int start;
int end;
} GanttEntry;
Process processes[MAX_PROCESSES];
GanttEntry gantt[1000];
int n; // number of processes
Algorithm Implementations
FCFS (First Come First Served)
void fcfs(Process proc[], int count) {
// Sort by arrival time (stable sort preserving order for same arrival)
for (int i = 0; i < count - 1; i++)
for (int j = 0; j < count - i - 1; j++)
if (proc[j].arrival_time > proc[j+1].arrival_time) {
Process temp = proc[j];
proc[j] = proc[j+1];
proc[j+1] = temp;
}
int current_time = 0;
for (int i = 0; i < count; i++) {
if (current_time < proc[i].arrival_time)
current_time = proc[i].arrival_time; // CPU idle
proc[i].start_time = current_time;
proc[i].response_time = current_time - proc[i].arrival_time;
current_time += proc[i].burst_time;
proc[i].completion_time = current_time;
proc[i].turnaround_time = proc[i].completion_time - proc[i].arrival_time;
proc[i].waiting_time = proc[i].turnaround_time - proc[i].burst_time;
}
}
SJF (Shortest Job First — Non-Preemptive)
void sjf(Process proc[], int count) {
int completed = 0, current_time = 0;
int done[MAX_PROCESSES] = {0};
while (completed < count) {
int shortest = -1, min_burst = INT_MAX;
// Find shortest burst among arrived, uncompleted processes
for (int i = 0; i < count; i++) {
if (!done[i] && proc[i].arrival_time <= current_time) {
if (proc[i].burst_time < min_burst) {
min_burst = proc[i].burst_time;
shortest = i;
}
}
}
if (shortest == -1) {
current_time++; // No process available, advance time
continue;
}
proc[shortest].start_time = current_time;
proc[shortest].response_time = current_time - proc[shortest].arrival_time;
current_time += proc[shortest].burst_time;
proc[shortest].completion_time = current_time;
proc[shortest].turnaround_time = proc[shortest].completion_time - proc[shortest].arrival_time;
proc[shortest].waiting_time = proc[shortest].turnaround_time - proc[shortest].burst_time;
done[shortest] = 1;
completed++;
}
}
SRTF (Shortest Remaining Time First — Preemptive)
void srtf(Process proc[], int count) {
int completed = 0, current_time = 0;
int min_remaining, shortest;
for (int i = 0; i < count; i++) {
proc[i].remaining_time = proc[i].burst_time;
proc[i].started = 0;
}
while (completed < count) {
shortest = -1;
min_remaining = INT_MAX;
for (int i = 0; i < count; i++) {
if (proc[i].arrival_time <= current_time && proc[i].remaining_time > 0) {
if (proc[i].remaining_time < min_remaining) {
min_remaining = proc[i].remaining_time;
shortest = i;
}
}
}
if (shortest == -1) { current_time++; continue; }
if (!proc[shortest].started) {
proc[shortest].start_time = current_time;
proc[shortest].response_time = current_time - proc[shortest].arrival_time;
proc[shortest].started = 1;
}
proc[shortest].remaining_time--;
current_time++;
if (proc[shortest].remaining_time == 0) {
proc[shortest].completion_time = current_time;
proc[shortest].turnaround_time = current_time - proc[shortest].arrival_time;
proc[shortest].waiting_time = proc[shortest].turnaround_time - proc[shortest].burst_time;
completed++;
}
}
}
Round Robin
void round_robin(Process proc[], int count, int quantum) {
int queue[1000], front = 0, rear = 0;
int current_time = 0, completed = 0;
int in_queue[MAX_PROCESSES] = {0};
for (int i = 0; i < count; i++) {
proc[i].remaining_time = proc[i].burst_time;
proc[i].started = 0;
}
// Add processes arriving at time 0
for (int i = 0; i < count; i++) {
if (proc[i].arrival_time == 0) {
queue[rear++] = i;
in_queue[i] = 1;
}
}
while (completed < count) {
if (front == rear) { current_time++; // CPU idle
for (int i = 0; i < count; i++)
if (!in_queue[i] && proc[i].arrival_time <= current_time && proc[i].remaining_time > 0) {
queue[rear++] = i; in_queue[i] = 1;
}
continue;
}
int idx = queue[front++];
if (!proc[idx].started) {
proc[idx].response_time = current_time - proc[idx].arrival_time;
proc[idx].started = 1;
}
int exec_time = (proc[idx].remaining_time < quantum) ? proc[idx].remaining_time : quantum;
proc[idx].remaining_time -= exec_time;
current_time += exec_time;
// Add newly arrived processes to queue
for (int i = 0; i < count; i++)
if (!in_queue[i] && proc[i].arrival_time <= current_time && proc[i].remaining_time > 0) {
queue[rear++] = i; in_queue[i] = 1;
}
if (proc[idx].remaining_time > 0) {
queue[rear++] = idx; // Re-add to queue
} else {
proc[idx].completion_time = current_time;
proc[idx].turnaround_time = current_time - proc[idx].arrival_time;
proc[idx].waiting_time = proc[idx].turnaround_time - proc[idx].burst_time;
completed++;
}
}
}
Output: Metrics and Comparison
void print_results(Process proc[], int count, const char* algo_name) {
float total_wt = 0, total_tat = 0, total_rt = 0;
printf("\n=== %s ===\n", algo_name);
printf("PID\tAT\tBT\tCT\tTAT\tWT\tRT\n");
for (int i = 0; i < count; i++) {
printf("P%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
proc[i].pid, proc[i].arrival_time, proc[i].burst_time,
proc[i].completion_time, proc[i].turnaround_time,
proc[i].waiting_time, proc[i].response_time);
total_wt += proc[i].waiting_time;
total_tat += proc[i].turnaround_time;
total_rt += proc[i].response_time;
}
printf("Average WT: %.2f | Average TAT: %.2f | Average RT: %.2f\n",
total_wt/count, total_tat/count, total_rt/count);
}
| Number of processes | 4 |
| P1 | Arrival=0, Burst=8, Priority=2 |
| P2 | Arrival=1, Burst=4, Priority=1 |
| P3 | Arrival=2, Burst=9, Priority=3 |
| P4 | Arrival=3, Burst=5, Priority=4 |
| Time Quantum (for RR) | 3 |
Project Extensions
- Gantt Chart Visualization: Print a text-based or graphical timeline
- CSV Export: Output results to CSV for graphing in Excel
- Interactive Mode: Let users add/remove processes and re-run
- Multilevel Queue: Implement separate queues with different algorithms per level
- Statistical Analysis: Generate random process sets and compare average metrics over 1000 runs
Learning Outcomes
- Understand the precise decision logic of each scheduling algorithm
- Recognize which scenarios favor which algorithm
- Practice implementing preemptive vs non-preemptive logic
- Learn to validate results by hand-tracing with small examples
- Appreciate why real OS schedulers (like Linux CFS) are more complex than textbook algorithms
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for CPU Scheduling Simulator Project.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Operating Systems topic.
Search Terms
operating-systems, operating systems, operating, systems, practicals, and, projects, cpu