Level 5 — MasteryOperating Systems

Operating Systems

90 minutes60 marksprintable — key stays hidden on paper

Time limit: 90 minutes Total marks: 60 Instructions: Answer ALL three questions. Show reasoning, derivations, and any assumptions. Math may be written using ...... notation.


Question 1 — Scheduling, Queueing & Overhead Modelling (20 marks)

A single-core system runs a Round-Robin (RR) scheduler with time quantum qq. A context switch costs a fixed overhead ss (seconds). Consider a workload of NN CPU-bound jobs, each requiring exactly TT seconds of pure CPU time, all arriving at t=0t=0.

(a) Derive a closed-form expression for the CPU efficiency (fraction of wall-clock time spent doing useful work, not context switching) as a function of qq, ss, and TT. State your modelling assumptions. (4)

(b) Now consider turnaround time. For the last job to finish under RR, derive the turnaround time TlastT_{last} as a function of NN, TT, qq, and ss, assuming qTq \mid T (i.e. TT is an exact multiple of qq). (5)

(c) A designer proposes: "Make qq very large to reduce overhead." Prove that as qq \to \infty, RR degenerates into FCFS, and give the resulting average turnaround time for the NN identical jobs (ignoring ss in this limit). Compare with the RR average turnaround for small qq and explain the trade-off in one sentence. (6)

(d) Give the specific numeric values: N=4N=4, T=8T=8 ms, q=2q=2 ms, s=0.5s=0.5 ms. Compute the CPU efficiency (part a) and TlastT_{last} (part b). (5)


Question 2 — Concurrency: Correctness Proof & Synchronization (20 marks)

Two threads share an integer counter initialised to 0. Each executes the non-atomic statement counter++ exactly MM times in a loop.

(a) Explain precisely why the final value of counter can be less than 2M2M. Identify the smallest possible final value, and construct an interleaving (at the load/add/store granularity) that achieves it. Prove your minimum is tight (cannot be smaller). (7)

(b) A student protects the increment using the following test-and-set spinlock. State whether it guarantees (i) mutual exclusion, (ii) progress, (iii) bounded waiting. Justify each. (6)

lock = 0
acquire(): while test_and_set(&lock) == 1: ;   // spin
release(): lock = 0

(c) Rewrite the critical-section protection using a counting semaphore to instead allow at most kk threads simultaneously inside a region (not mutual exclusion). Give the P()/V() placement and the semaphore's initial value. Then prove, using the semaphore invariant S = S_0 - (\text{#completed P}) + (\text{#completed V}) \ge 0, that no more than kk threads can be inside at once. (7)


Question 3 — Memory, Paging & Deadlock Avoidance (20 marks)

(a) A 32-bit virtual address space uses a two-level page table. Page size is 4 KB. The virtual address is split as [p1 | p2 | offset]. Each page table entry (PTE) is 4 bytes. Derive the number of bits for offset, p2, and p1 such that each single-level page table fits exactly in one page. Show your arithmetic. (6)

(b) Explain why a two-level (or multi-level) page table can consume less physical memory than a flat single-level page table for a sparse address space. Give a concrete numeric example using your bit split from (a): a process using only 1 code page (bottom of address space) and 1 stack page (top of address space) — compute the page-table memory needed for both flat and two-level schemes. (6)

(c) Consider a system with 3 resource types (A,B,C)(A, B, C) with total instances (10,5,7)(10, 5, 7). The current state is:

Process Allocation (A,B,C) Max (A,B,C)
P0P_0 (0,1,0) (7,5,3)
P1P_1 (2,0,0) (3,2,2)
P2P_2 (3,0,2) (9,0,2)
P3P_3 (2,1,1) (2,2,2)
P4P_4 (0,0,2) (4,3,3)

Compute Available, the Need matrix, run the Banker's safety algorithm, and give a safe sequence (or prove none exists). Then determine whether a request from P1P_1 for (1,0,2)(1,0,2) can be granted. (8)

Answer keyMark scheme & solutions

Question 1

(a) Assumptions: one context switch of cost ss occurs between each quantum of execution; jobs are pure CPU. Over the whole run, total useful CPU time is NTN\cdot T. Each job of length TT is chopped into T/qT/q quanta, so total number of scheduling slices NT/q\approx N\cdot T/q, each incurring one switch cost ss (per switch). Useful work per quantum =q= q, overhead per quantum =s= s.

Efficiency=qq+s\text{Efficiency} = \frac{q}{q+s}

Marks: correct ratio structure (2), independent of N,TN,T observation / assumptions stated (2).

(b) With qTq\mid T, each job needs T/qT/q quanta. Under RR with NN jobs all present, jobs are served in cyclic order. The last job finishes after all N(T/q)N\cdot(T/q) quanta plus the switches between them. Number of context switches before the last quantum completes =N(T/q)1= N\cdot(T/q) - 1 (a switch before each slice except we can neglect the leading one; standard convention counts one switch per slice boundary). Using one switch per served quantum:

Tlast=NT+(NTq)s(total useful + total switch time)T_{last} = N\cdot T + \left(N\cdot \tfrac{T}{q}\right)\cdot s \quad\text{(total useful + total switch time)}

More precisely the last job completes on the final quantum, so:

Tlast=NT+(NTq1)sT_{last} = N\,T + \Big(N\tfrac{T}{q}-1\Big)s

Either accepted with justification. Marks: recognise all quanta precede last completion (2), useful term NTNT (1), overhead term (2).

(c) As qq\to\infty, each job's requirement T<qT<q, so each job runs to completion in a single quantum with no preemption — the scheduler serves job 1 fully, then job 2, etc. This is exactly FCFS. Turnaround of job ii (ignoring ss) =iT= i\cdot T. Average:

TˉFCFS=1Ni=1NiT=T(N+1)2\bar T_{FCFS} = \frac{1}{N}\sum_{i=1}^{N} iT = \frac{T(N+1)}{2}

For small qq, all NN jobs finish at nearly the same time NT\approx NT (round-robin makes them progress together), giving average turnaround NT\approx N\,T — much worse for identical jobs. Trade-off: large qq (→FCFS) minimizes average turnaround for identical jobs but harms response time / fairness; small qq improves responsiveness but raises overhead and turnaround. Marks: prove degeneration to FCFS (2), TˉFCFS=T(N+1)/2\bar T_{FCFS}=T(N+1)/2 (2), comparison + trade-off sentence (2).

(d) Efficiency =q/(q+s)=2/(2+0.5)=2/2.5=0.8=80%= q/(q+s) = 2/(2+0.5) = 2/2.5 = 0.8 = 80\%. TlastT_{last}: N=4,T=8,q=2N=4,T=8,q=2 \Rightarrow quanta per job =4=4, total quanta =16=16. Tlast=NT+(NT/q)s=32+16×0.5=32+8=40T_{last} = N\,T + (N T/q)\,s = 32 + 16\times0.5 = 32+8 = 40 ms. (Using the 1-1 convention: 32+(161)0.5=39.532+(16-1)0.5=39.5 ms — either accepted.) Marks: efficiency (2), TlastT_{last} (3).


Question 2

(a) counter++ = load, increment, store (three steps, non-atomic). If both threads read the same old value before either stores, one increment is lost (lost update). Minimum final value: with MM operations each, interleavings can make both threads repeatedly clobber each other. The smallest achievable value is 2 (not lower):

  • Thread A loads 0.
  • Thread B runs all MM increments to completion → counter = MM... but A still holds 0.

Tight-minimum construction: Interleave so each store overwrites, but note counter must reach at least MM from one thread's run then be knocked back. Classic result: the minimum is 2. Construction: A reads 0. B does M1M-1 increments (counter=M1M-1). A does its remaining loop reading/writing... Standard proof: one thread can hold a stale value spanning the other's entire run, but each of its own MM writes advances counter, so the losing thread still contributes; the theoretical floor is 2 (each thread guarantees at least one uncontested final store contributing +1, and one full run can be entirely masked to net +1). Accept: min =2=2 with a valid load/add/store interleaving; proof that it cannot be 0 or 1 because the last thread to store writes at least (its own last read + M-progress ≥ 1), and two threads' final stores give ≥2. Marks: lost-update mechanism (3), min value 2 (2), valid interleaving + tightness (2).

(b) (i) Mutual exclusion — YES. test_and_set atomically reads old value and sets to 1; only the thread that observes old value 0 enters. Atomicity guarantees at most one winner. (ii) Progress — YES. If lock is free (0) and threads are contending, one will succeed (some thread's TAS reads 0); no thread outside its CS blocks entry. No deadlock on a single lock. (iii) Bounded waiting — NO. A spinning thread can be repeatedly overtaken; nothing enforces order/fairness. A thread could starve indefinitely while others keep re-acquiring. Marks: 2 each with justification.

(c) Use semaphore sem initialised to S0=kS_0 = k.

P(sem)          // wait
   <region>
V(sem)          // signal

A thread completes P (and enters) only after decrementing SS. Invariant: S=k(completed P)+(completed V)0S = k - (\text{completed P}) + (\text{completed V}) \ge 0. Number inside region == (completed P) − (completed V) =kSk= k - S \le k since S0S\ge 0. Hence at most kk threads inside simultaneously. A thread cannot complete P when S=0S=0 (it blocks), enforcing the bound. Marks: correct P/V placement + init kk (3), invariant statement (2), derivation inside =kSk=k-S\le k (2).


Question 3

(a) Page size 4KB=2124\text{KB}=2^{12}offset = 12 bits. Remaining 3212=2032-12 = 20 bits for p1+p2p1+p2. Each page table must fit in one page (4096 B) with 4-byte PTEs → entries per table =4096/4=1024=210=4096/4 = 1024 = 2^{10}10 bits each. So p2=10p2 = 10, and p1=2010=10p1 = 20-10 = 10 bits. Split: p1=10, p2=10, offset=12p1=10,\ p2=10,\ \text{offset}=12. Marks: offset (2), entries per table =1024=1024 (2), 10/10 split (2).

(b) A flat single-level table needs one PTE per virtual page over the entire space: 2202^{20} entries × 4 B =222=4= 2^{22} = 4 MB, regardless of usage. A two-level table allocates second-level tables only for used regions; unused p1p1 slots point to nothing.

Numeric example: process uses 1 code page (low address, p1=0p1=0) and 1 stack page (high address, p1=1023p1=1023).

  • Flat: 44 MB (must be fully allocated).
  • Two-level: 1 outer (top-level) table = 4 KB, plus 2 second-level tables (one for p1=0p1=0, one for p1=1023p1=1023) = 2×4KB=82\times 4\text{KB} = 8 KB. Total =4+8=12= 4+8 = 12 KB. Saving: 4 MB → 12 KB. Marks: explanation sparse-alloc (2), flat = 4 MB (2), two-level = 12 KB (2).

(c) Total (10,5,7)(10,5,7). Sum of Allocation =(0+2+3+2+0,  1+0+0+1+0,  0+0+2+1+2)=(7,2,5)= (0+2+3+2+0,\;1+0+0+1+0,\;0+0+2+1+2) = (7,2,5). Available =(10,5,7)(7,2,5)=(3,3,2)= (10,5,7)-(7,2,5) = (3,3,2).

Need = Max − Allocation:

P Need
P0P_0 (7,4,3)
P1P_1 (1,2,2)
P2P_2 (6,0,0)
P3P_3 (0,1,1)
P4P_4 (4,3,1)

Safety: Work=(3,3,2).

  • P1P_1 Need(1,2,2)≤Work → run, Work=(3,3,2)+(2,0,0)=(5,3,2).
  • P3P_3 Need(0,1,1)≤(5,3,2) → Work=(5,3,2)+(2,1,1)=(7,4,3).
  • P0P_0 Need(7,4,3)≤(7,4,3) → Work=(7,4,3)+(0,1,0)=(7,5,3).
  • P2P_2 Need(6,0,0)≤(7,5,3) → Work=(7,5,3)+(3,0,2)=(10,5,5).
  • P4P_4 Need(4,3,1)≤(10,5,5) → Work=(10,5,7).

Safe sequence: P1,P3,P0,P2,P4\langle P_1,P_3,P_0,P_2,P_4\rangle. State is safe.

Request P1=(1,0,2)P_1=(1,0,2): Check Need1(1,2,2)\le Need_1(1,2,2) ✓ and Available(3,3,2)\le Available(3,3,2) ✓. Tentatively: Available =(3,3,2)(1,0,2)=(2,3,0)=(3,3,2)-(1,0,2)=(2,3,0); Alloc1=(3,0,2)Alloc_1=(3,0,2); Need1=(0,2,0)Need_1=(0,2,0). Re-run safety with Work=(2,3,0):

  • P3P_3 Need(0,1,1)? C-component 1>0 → no. P1P_1 Need(0,2,0)≤(2,3,0) → Work=(2,3,0)+(3,0,2)=(5,3,2).
  • P3P_3 Need(0,1,1)≤(5,3,2) → Work=(7,4,3).
  • P0P_0(7,4,3)≤(7,4,3) → (7,5,3); P2P_2(6,0,0)≤ → (10,5,5); P4P_4(4,3,1)≤ → (10,5,7). ✓ Request CAN be granted (safe sequence P1,P3,P0,P2,P4\langle P_1,P_3,P_0,P_2,P_4\rangle). Marks: Available (2), Need matrix (2), safe sequence (2), request analysis (2).
[
  {"claim":"RR efficiency q/(q+s)=0.8 for q=2,s=0.5","code":"q=2; s=Rational(1,2); eff=q/(q+s); result=(eff==Rational(4,5))"},
  {"claim":"T_last=40ms for N=4,T=8,q=2,s=0.5 (no -1 convention)","code":"N=4;T=8;q=2;s=Rational(1,2); Tlast=N*T+(N*T/q)*s; result=(Tlast==40)"},
  {"claim":"FCFS avg turnaround T(N+1)/2 equals 20 for N=4,T=8","code":"N=4;T=8; avg=T*(N+1)/2; result=(avg==20)"},
  {"claim":"Banker Available=(3,3,2)","code":"tot=[10,5,7]; alloc=[[0,1,0],[2,0,0],[3,0,2],[2,1,1],[0,0,2]]; s=[sum(a[i] for a in alloc) for i in range(3)]; avail=[tot[i]-s[i] for i in range(3)]; result=(avail==[3,3,2])"},
  {"claim":"Two-level PT for 2 pages = 12KB vs flat 4MB","code":"flat=2**20*4; two=4096+2*4096; result=(flat==4194304 and two==12288)"}
]