SJF & SRTF Scheduling in OS
Introduction
SJF (Shortest Job First) is a CPU scheduling algorithm that prioritizes processes with the smallest burst time. It’s like letting the quickest grocery store customers check out first to reduce waiting time!
- Goal: Minimize average waiting time.
- Criteria: Burst Time
- Two Variants:
- Non-Preemptive SJF: Runs processes to completion once started.
- Preemptive SJF (SRTF): Can interrupt running processes for shorter jobs.
Key Characteristics
- Optimal for minimizing waiting time (if burst times are known).
- Starvation risk: Long processes may wait indefinitely.
- Requires prior knowledge of burst times (often estimated).
Real-Life Analogy
At a clinic:
- A doctor prioritizes patients with quicker treatments (e.g., flu shots over surgeries).
- If a critical patient arrives mid-treatment (preemptive), the doctor switches immediately.
Non-Preemptive SJF Example
Calculate average waiting time and average turnaround time for:
Process | Arrival Time | Burst Time |
---|---|---|
P1 | 0 | 6 |
P2 | 2 | 2 |
P3 | 4 | 8 |
Step 1: Sort by Arrival Time & Execute Shortest Available Job
- Time 0: Only P1 (burst 6) is available → Runs until 6.
- Time 6: Available processes = P2 (burst 2) and P3 (burst 8).
- Pick P2 (shortest) → Runs until 8.
- Time 8: Run P3 → Completes at 16.
Step 2: Calculate Completion Times
Process | Completion Time |
---|---|
P1 | 6 |
P2 | 8 |
P3 | 16 |
Step 3: Calculate Turnaround Time
\[ \text{Turnaround Time} = \text{Completion Time} - \text{Arrival Time} \]
Process | Turnaround Time |
---|---|
P1 | 6 - 0 = 6 |
P2 | 8 - 2 = 6 |
P3 | 16 - 4 = 12 |
Average Turnaround Time = \( \frac{6 + 6 + 12}{3} = 8.0 \)
Step 4: Calculate Waiting Time
\[ \text{Waiting Time} = \text{Turnaround Time} - \text{Burst Time} \]
Process | Waiting Time |
---|---|
P1 | 6 - 6 = 0 |
P2 | 6 - 2 = 4 |
P3 | 12 - 8 = 4 |
Average Waiting Time = \( \frac{0 + 4 + 4}{3} = 2.67 \)
Final Timeline
0──────6────8───────16
P1 P2 P3

Figure 1: Gantt Chart for SJF Scheduling
Preemptive SJF (SRTF) Example
Calculate average waiting time for:
Process | Arrival Time | Burst Time |
---|---|---|
P1 | 0 | 8 |
P2 | 1 | 4 |
P3 | 2 | 9 |
P4 | 3 | 5 |
Step 1: Track Shortest Remaining Time at Each Step
Time 0-1: P1 runs (remaining burst = 7).
Time 1: P2 arrives (burst 4 < 7). Preempt P1 → Run P2.
Time 1-5: P2 runs to completion (burst 4).
Time 5: Available processes = P1 (7), P3 (9), P4 (5).
Pick P4 (burst 5).
Time 5-10: P4 runs to completion.
Time 10: Run P1 (remaining 7).
Time 10-17: P1 completes.
Time 17-26: Run P3.
Step 2: Completion Times
Process | Completion Time |
---|---|
P1 | 17 |
P2 | 5 |
P3 | 26 |
P4 | 10 |
Step 3: Calculate Waiting Time
Process | Waiting Time |
---|---|
P1 | (17 - 0) - 8 = 9 |
P2 | (5 - 1) - 4 = 0 |
P3 | (26 - 2) - 9 = 15 |
P4 | (10 - 3) - 5 = 2 |
Average Waiting Time = \( \frac{9 + 0 + 15 + 2}{4} = 6.5 \)
Final Timeline
0 1 5 10 17 26
|P1|P2| P4 | P1 | P3 |

Figure 2: Gantt Chart for SRTF Scheduling
C Implementation of SJF & SRTF (Combined)
#include <stdio.h>
#define MAX 100
struct Process {
int pid, arrival, burst, start, remaining, completion, waiting, turnaround, visited;
};
void sortByArrival(struct Process p[], int n) {
struct Process temp;
for (int i = 0; i < n - 1; ++i)
for (int j = 0; j < n - i - 1; ++j)
if (p[j].arrival > p[j + 1].arrival) {
temp = p[j];
p[j] = p[j + 1];
p[j + 1] = temp;
}
}
void sjf_non_preemptive(struct Process p[], int n) {
int current = 0, completed = 0;
while (completed < n) {
int idx = -1, min_burst = 9999;
for (int i = 0; i < n; i++) {
if (!p[i].visited && p[i].arrival <= current && p[i].burst < min_burst) {
min_burst = p[i].burst;
idx = i;
}
}
if (idx != -1) {
p[idx].start = current;
p[idx].completion = p[idx].start + p[idx].burst;
p[idx].turnaround = p[idx].completion - p[idx].arrival;
p[idx].waiting = p[idx].turnaround - p[idx].burst;
p[idx].visited = 1;
current = p[idx].completion;
completed++;
} else {
current++;
}
}
}
int findShortest(int n, struct Process p[], int current_time) {
int min = 9999, idx = -1;
for (int i = 0; i < n; i++) {
if (p[i].arrival <= current_time && p[i].remaining > 0 && p[i].remaining < min) {
min = p[i].remaining;
idx = i;
}
}
return idx;
}
void sjf_preemptive(struct Process p[], int n, int timeline[], int *total_time) {
int time = 0, completed = 0;
while (completed < n) {
int idx = findShortest(n, p, time);
timeline[time] = (idx == -1) ? -1 : p[idx].pid;
if (idx != -1) {
p[idx].remaining--;
if (p[idx].remaining == 0) {
p[idx].completion = time + 1;
p[idx].turnaround = p[idx].completion - p[idx].arrival;
p[idx].waiting = p[idx].turnaround - p[idx].burst;
completed++;
}
}
time++;
}
*total_time = time;
}
void print_table(struct Process p[], int n) {
float total_wt = 0, total_tt = 0;
printf("\nPID Arrival Burst Waiting Turnaround Completion\n");
for (int i = 0; i < n; i++) {
printf("P%-3d %-8d%-7d%-9d%-12d%-10d\n", p[i].pid, p[i].arrival, p[i].burst,
p[i].waiting, p[i].turnaround, p[i].completion);
total_wt += p[i].waiting;
total_tt += p[i].turnaround;
}
printf("\nAverage Waiting Time = %.2f\n", total_wt / n);
printf("Average Turnaround Time = %.2f\n", total_tt / n);
}
void print_gantt_chart_np(struct Process p[], int n) {
printf("\nGantt Chart (Non-Preemptive):\n ");
for (int i = 0; i < n; i++) printf("--------");
printf("-\n|");
for (int i = 0; i < n; i++) printf(" P%-2d |", p[i].pid);
printf("\n ");
for (int i = 0; i < n; i++) printf("--------");
printf("-\n%d", p[0].start);
for (int i = 0; i < n; i++) printf(" %d", p[i].completion);
printf("\n");
}
void print_gantt_chart_pt(int timeline[], int total_time) {
printf("\nGantt Chart (Preemptive):\n ");
for (int i = 0; i < total_time; i++) {
if (i == 0 || timeline[i] != timeline[i - 1]) printf("------");
}
printf("-\n|");
for (int i = 0; i < total_time; i++) {
if (i == 0 || timeline[i] != timeline[i - 1])
if (timeline[i] == -1) printf(" IDLE |");
else printf(" P%-2d |", timeline[i]);
}
printf("\n ");
for (int i = 0; i < total_time; i++) {
if (i == 0 || timeline[i] != timeline[i - 1]) printf("------");
}
printf("-\n0");
for (int i = 1; i < total_time; i++) {
if (timeline[i] != timeline[i - 1])
printf(" %2d", i);
}
printf(" %2d\n", total_time);
}
int main() {
int n, choice;
struct Process p[MAX];
int timeline[MAX], total_time = 0;
printf("Enter number of processes: ");
scanf("%d", &n);
for (int i = 0; i < n; i++) {
p[i].pid = i + 1;
p[i].visited = 0;
printf("Enter Arrival Time and Burst Time for P%d: ", p[i].pid);
scanf("%d %d", &p[i].arrival, &p[i].burst);
p[i].remaining = p[i].burst;
}
printf("\nChoose Scheduling Algorithm:\n1. Non-Preemptive SJF\n2. Preemptive SJF (SRTF)\nEnter choice: ");
scanf("%d", &choice);
if (choice == 1) {
sortByArrival(p, n);
sjf_non_preemptive(p, n);
print_table(p, n);
print_gantt_chart_np(p, n);
} else if (choice == 2) {
sjf_preemptive(p, n, timeline, &total_time);
print_table(p, n);
print_gantt_chart_pt(timeline, total_time);
} else {
printf("Invalid choice.\n");
}
return 0;
}
Pros & Cons of SJF
Advantages
- Minimizes average waiting time (optimal for non-preemptive).
- Efficient for batch systems with known burst times.
Disadvantages
- Starvation: Long processes may never get CPU time.
- Requires burst time estimation (often impractical).
- Preemptive variant adds overhead.
When to Use SJF
- Batch processing (e.g., rendering farms).
- Embedded systems with predictable tasks.
FAQ
Q: Is SJF realistic?
A: No – burst times are rarely known in advance. Real systems use approximations (e.g., exponential averaging).
Q: Which is better: preemptive or non-preemptive?
A: Preemptive (SRTF) gives lower waiting time but higher overhead.
Q: Can SJF be used in interactive systems?
A: No – starvation makes it unfair for long processes.