Contents

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:
    1. Non-Preemptive SJF: Runs processes to completion once started.
    2. 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:

  1. A doctor prioritizes patients with quicker treatments (e.g., flu shots over surgeries).
  2. 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:

ProcessArrival TimeBurst Time
P106
P222
P348

Step 1: Sort by Arrival Time & Execute Shortest Available Job

  1. Time 0: Only P1 (burst 6) is available → Runs until 6.
  2. Time 6: Available processes = P2 (burst 2) and P3 (burst 8).
    • Pick P2 (shortest) → Runs until 8.
  3. Time 8: Run P3 → Completes at 16.

Step 2: Calculate Completion Times

ProcessCompletion Time
P16
P28
P316

Step 3: Calculate Turnaround Time

\[ \text{Turnaround Time} = \text{Completion Time} - \text{Arrival Time} \]

ProcessTurnaround Time
P16 - 0 = 6
P28 - 2 = 6
P316 - 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} \]

ProcessWaiting Time
P16 - 6 = 0
P26 - 2 = 4
P312 - 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

Figure 1: Gantt Chart for SJF Scheduling

Preemptive SJF (SRTF) Example

Calculate average waiting time for:

ProcessArrival TimeBurst Time
P108
P214
P329
P435

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

ProcessCompletion Time
P117
P25
P326
P410

Step 3: Calculate Waiting Time

ProcessWaiting 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

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.