Scheduling, paging, deadlock & concurrency
Six self-contained experiments, each a genuinely computing simulator written in plain JavaScript — no libraries. Every algorithm runs over data you type: edit the process table, the reference string, the request queue or the allocation matrices and watch the Gantt chart, frame grid, head-movement plot, safe sequence and semaphore state machine recompute exactly. Move through Aim, Theory, Procedure, the live Simulation, a graded Self-assessment and References.
1 · CPU scheduling
A CPU scheduler picks, at every decision point, which ready process runs next on the single CPU. FCFS (first-come-first-served) runs processes in arrival order and is non-preemptive. SJF (shortest-job-first, non-preemptive) always picks the ready process with the smallest total burst; it is provably optimal for minimum average waiting time when all jobs are present. SRTF is preemptive SJF: at every tick it runs the process with the smallest remaining time, so an arriving short job can preempt a longer running one. Round-Robin gives each ready process a fixed time quantum in a circular queue — fair and responsive but with more context switches. Priority scheduling runs the highest-priority ready process (here a smaller number means higher priority).
For each process define completion time C, turnaround time and waiting time:
Waiting WT = Turnaround TAT − Burst BT
Response RT = (first CPU time) − Arrival AT
Average WT = (sum of WT) / n Average TAT = (sum of TAT) / n
FCFS suffers the convoy effect — one long job delays many short ones. SRTF minimises average waiting time but can starve long jobs; Round-Robin trades a little throughput for bounded response time, governed entirely by the quantum.
- Open the Simulation tab. A four-process table is preloaded with arrival, burst and priority.
- Edit any cell, or press Add / Remove to change the process set, or Randomise for a fresh case.
- Choose a policy. For Round-Robin set the time quantum; for Priority recall that a smaller number is higher priority.
- Press Run. Read the Gantt chart — each block is a CPU run with start and end times marked.
- Read the per-process table of completion, turnaround and waiting times, and the averages at the bottom.
- Switch policies on the same data and compare the average waiting time — SJF / SRTF should win.
Gantt chart & results
Process table
| PID | Arrival | Burst | Prio |
|---|
- Silberschatz, Galvin & Gagne — Operating System Concepts, 10th ed., Ch. 5 (CPU Scheduling). Wiley.
- Tanenbaum & Bos — Modern Operating Systems, 4th ed., Sec. 2.4. Pearson.
- Virtual Labs (IIT) — Operating Systems: CPU Scheduling, cse02.vlabs.ac.in.
2 · Page replacement
Under demand paging only the pages actually referenced are loaded. When a referenced page is not resident a page fault occurs and, if every frame is full, the policy must evict a victim. FIFO evicts the page that has been resident longest. LRU (least-recently-used) evicts the page whose last use is furthest in the past — it exploits temporal locality. Optimal (Belady's MIN) evicts the page whose next use is furthest in the future; it is unrealisable online but gives the theoretical lower bound on faults.
Fault ratio = faults / total references = 1 − hit ratio
More frames usually means fewer faults, but FIFO can exhibit Belady's anomaly — adding a frame occasionally increases faults. LRU and Optimal are stack algorithms and never suffer the anomaly. The optimal count is always a lower bound; comparing FIFO and LRU against it measures how good a practical policy is.
- Open the Simulation tab. A reference string and a frame count are preloaded.
- Edit the reference string (space- or comma-separated page numbers) and set the number of frames.
- Pick a policy and press Step to advance one reference at a time, or Run to animate the whole string.
- Each column is one reference; green frames mark a hit, amber a fault, and the evicted page is shown.
- Read the running faults, hits and hit ratio. Re-run under each policy and against Optimal.
Frame timeline FIFO
Configuration
- Silberschatz, Galvin & Gagne — Operating System Concepts, 10th ed., Ch. 10 (Virtual Memory). Wiley.
- Belady, L. A. — A study of replacement algorithms for a virtual-storage computer. IBM Systems Journal, 1966.
- Virtual Labs (IIT) — Operating Systems: Page Replacement, cse02.vlabs.ac.in.
3 · Disk scheduling
The dominant cost of a disk access is the seek time — the time to move the read/write head to the target cylinder — which is roughly proportional to the number of cylinders crossed. A disk scheduler reorders the pending request queue to shrink total head movement. FCFS services requests in arrival order (fair, but the head may swing wildly). SSTF (shortest-seek-time-first) always serves the nearest pending request; it is greedy and can starve far requests. SCAN (the elevator) sweeps in one direction to the disk edge, then reverses. C-SCAN sweeps one way, then jumps back to the start and sweeps again, giving more uniform wait. LOOK / C-LOOK behave like SCAN / C-SCAN but turn around at the last request instead of the physical edge.
(starting from the initial head position; C-SCAN counts the wrap-around jump)
SSTF and the elevator family dramatically cut average seek distance versus FCFS under load. C-SCAN sacrifices a little total movement for a more uniform response time, since every cylinder is visited on a regular cycle.
- Open the Simulation tab. A request queue, head position and disk size are preloaded.
- Edit the request queue (cylinder numbers), the initial head position and the maximum cylinder.
- For the elevator policies choose the initial sweep direction (towards 0 or towards the maximum).
- Pick a policy and press Run. The plot draws the head's path; each visited cylinder is a turning point.
- Read the total head movement and the service order. Compare policies on the same queue.
Head movement FCFS
Configuration
- Silberschatz, Galvin & Gagne — Operating System Concepts, 10th ed., Ch. 11 (Mass-Storage Structure). Wiley.
- Stallings, W. — Operating Systems: Internals and Design Principles, Sec. 11.5. Pearson.
- Virtual Labs (IIT) — Operating Systems: Disk Scheduling, cse02.vlabs.ac.in.
4 · Deadlock avoidance — Banker's algorithm
The Banker's algorithm avoids deadlock by never entering an unsafe state. Each process declares its Max demand of every resource type up front. The system tracks the Allocation already given and the Available (free) units. The remaining demand is the Need:
A state is safe if there exists an ordering of all processes such that each can obtain its full Need from the currently free units plus whatever earlier processes release on finishing. The safety check maintains a Work vector (initially Available) and a Finish flag per process; it repeatedly finds an unfinished process whose Need is ≤ Work, simulates it running and releasing, adding its Allocation back to Work:
Work = Work + Allocation[i]; Finish[i] = true; repeat
safe if every Finish[i] becomes true
To check a request by process i, the algorithm tentatively grants it (only if Request ≤ Need and Request ≤ Available), then re-runs the safety check; the request is granted only if the resulting state is still safe.
- Open the Simulation tab. A classic 5-process, 3-resource safe state is preloaded.
- Set the number of processes and resource types, then edit the Allocation, Max and Available cells. The Need matrix updates live.
- Press Check safety. If safe, the safe sequence is shown; if not, the state is reported unsafe.
- Enter a resource request for a chosen process and press Test request to see whether granting it keeps the system safe.
- Use Load unsafe to see a state with no safe sequence, and Reset for the textbook example.
Matrices
Allocation
Max
Need = Max − Allocation
Available (free units)
Controls
Test a request
- Dijkstra, E. W. — EWD108: Een algorithme ter voorkoming van de dodelijke omarming (the Banker's algorithm), 1965.
- Silberschatz, Galvin & Gagne — Operating System Concepts, 10th ed., Ch. 8 (Deadlocks). Wiley.
- Virtual Labs (IIT) — Operating Systems: Deadlock Avoidance, cse02.vlabs.ac.in.
5 · Memory allocation
In contiguous allocation the main memory is a row of holes (free partitions) of various sizes. When a process of size s arrives, the allocator must choose a hole large enough to hold it. First-fit scans from the start and takes the first hole that fits — fast and simple. Best-fit takes the smallest hole that fits, aiming to waste the least space, but it tends to litter memory with tiny unusable slivers. Worst-fit takes the largest hole, hoping the leftover stays big enough to be useful.
After placement the chosen hole shrinks by s; the unused tail remains free. Memory ends up checkerboarded with free fragments — this is external fragmentation: enough total free space exists, but no single hole is large enough for the next request.
External fragmentation = total free space when the next request cannot fit any single hole
Allocated = sum of placed process sizes
Best-fit and first-fit usually outperform worst-fit on utilisation; first-fit is often the fastest in practice. None eliminate external fragmentation — only compaction or paging does.
- Open the Simulation tab. A set of memory holes and a queue of process sizes are preloaded.
- Edit the holes (free partition sizes) and the process request sizes.
- Pick a strategy and press Step to place one process at a time, or Run to place them all.
- Watch each process land in a coloured block; the chosen hole and leftover are logged. Failures are flagged.
- Read the placed count, total leftover and free space. Compare strategies on the same memory.
Memory map first-fit
Configuration
- Silberschatz, Galvin & Gagne — Operating System Concepts, 10th ed., Ch. 9 (Main Memory). Wiley.
- Knuth, D. E. — The Art of Computer Programming, Vol. 1, Sec. 2.5 (Dynamic Storage Allocation). Addison-Wesley.
- Virtual Labs (IIT) — Operating Systems: Memory Allocation, cse02.vlabs.ac.in.
6 · Producer–consumer
The bounded-buffer problem has a producer adding items to a buffer of N slots and a consumer removing them. Three semaphores keep them safe. The counting semaphore empty (initially N) counts free slots; full (initially 0) counts occupied slots; the binary semaphore mutex (initially 1) gives exclusive access to the buffer. A wait (P) decrements a semaphore and blocks if it would go negative; a signal (V) increments it and may wake a waiter.
Consumer: wait(full); wait(mutex); dequeue item; signal(mutex); signal(empty)
invariant: empty + full = N when no thread is inside the critical section
Acquiring the counting semaphore before the mutex is essential: if a thread held the mutex while blocked on a full or empty buffer it would deadlock the other party out of the critical section. With this ordering the buffer never overflows (the producer blocks on empty = 0) nor underflows (the consumer blocks on full = 0), and mutual exclusion guarantees no torn updates.
- Open the Simulation tab. Set the buffer capacity N; the three semaphores initialise to empty = N, full = 0, mutex = 1.
- Drive the producer with its step button: it runs wait(empty), wait(mutex), enqueue, signal(mutex), signal(full) one operation at a time.
- Drive the consumer similarly. Watch the circular buffer fill and drain and the semaphore values change.
- Try to over-produce into a full buffer or over-consume an empty one — the offending thread blocks on its semaphore instead of corrupting the buffer.
- Use Auto to interleave both threads, and read the invariant empty + full = N holding outside the critical section.
Bounded buffer & semaphores
Controls
- Dijkstra, E. W. — Cooperating Sequential Processes (semaphores, the producer-consumer problem), 1968.
- Silberschatz, Galvin & Gagne — Operating System Concepts, 10th ed., Ch. 6–7 (Synchronization). Wiley.
- Downey, A. B. — The Little Book of Semaphores, 2nd ed. Green Tea Press.