Operating Systems,
samjho ek baar mein.
Zero se shuru. Har concept pehle plain English mein, phir Hinglish mein "asli baat kya hai", phir example, phir woh questions jo interview mein actually poochhe jaate hain — full solutions ke saath.
Start here
Padhne se pehle 2 minute ye padh lo — poora page kaise use karna hai.
Page kaise structured hai
- Plain explanation — normal technical English, jaisa book mein hota hai.
- HINGLISH block — wahi cheez apni bhasha mein, analogy ke saath. Ye woh version hai jo interview mein bolna hai.
- Boxes — ASKED A LOT matlab ye interview mein baar-baar aata hai. COMMON matlab decent chance. RARE matlab depth ke liye.
- Q boxes — question upar, solution collapsed. Pehle khud try karo, phir kholo.
- Process vs thread, aur process memory layout
- fork(), zombie, orphan
- Context switch — kya save hota hai, cost kya hai
- Scheduling algorithms + Gantt chart numericals
- Race condition & critical section ke 3 conditions
- Mutex vs semaphore vs binary semaphore
- Producer–consumer + deadlock in it
- Deadlock ke 4 conditions + Banker's algorithm
- Paging, page table, TLB, EAT calculation
- Page replacement (LRU/FIFO/Optimal) + Belady's anomaly
- Thrashing
- Disk scheduling head movement numericals
Interview mein OS kaise poochha jaata hai? Do style hote hain:
Style 1 — Concept: "Deadlock kya hota hai?" Yahan definition + 4 conditions + ek real example chahiye. 60–90 seconds ka answer.
Style 2 — Numerical: "Ye reference string hai, 3 frames, LRU se page faults nikalo." Yahan table banao, step by step. Ye written round/OA mein zyada aata hai.
Dono ke liye alag prep chahiye. Is page mein dono hai — concept explanation ke baad hamesha numericals honge.
Ek mental model jo poore OS ko jodta hai
OS ke paas 4 cheezein hain jo limited hain, aur bahut saare programs unhe ek saath maangte hain:
| Resource | Kaun manage karta hai | Core problem | Section |
|---|---|---|---|
| CPU (time) | Scheduler | Kisko kab aur kitni der do? | §06 |
| Memory (space) | MMU + memory manager | Sabko lagе poori RAM meri hai | §0B–§0D |
| Disk / I/O | I/O subsystem | Slow device, fast CPU — sync kaise? | §0E |
| Shared data | Sync primitives | Do log ek hi cheez chhuein toh corruption | §07–§0A |
Har OS chapter isi table ka ek row hai. Jab bhi confuse ho, wapas yahin aao — "abhi main kis resource ki baat kar raha hoon, aur uska conflict kya hai?"
What an OS actually is
Definition, goals, aur woh do views jo har viva mein poochhe jaate hain.
An operating system is a program that acts as an intermediary between the user of a computer and the computer hardware. It is a resource allocator (manages CPU, memory, I/O and decides between conflicting requests) and a control program (controls execution of programs to prevent errors and improper use).
There is no universally accepted definition. The one line that always works: "the one program running at all times on the computer is the kernel; everything else is either a system program or an application program."
Socho ek building ka manager. Building = hardware. Tenants = programs. Manager khud koi kaam nahi karta — na paani banata hai, na bijli. Woh sirf ye decide karta hai: kisko kitna paani milega, kaun kis floor pe rahega, aur agar do tenants ek hi parking spot maang rahe hain toh kaun jeetega.
OS bilkul yahi hai. Woh khud calculation nahi karta — CPU karta hai. Woh khud data store nahi karta — disk karti hai. OS sirf allocate karta hai aur protect karta hai.
Do kaam yaad rakho: (1) Convenience — user ko easy lage (2) Efficiency — hardware waste na ho. Aur maza ye hai ki ye dono ek dusre ke against hain. Zyada convenience doge (fancy GUI, abstractions) toh efficiency girti hai. Yahi tension poore OS design mein hai.
Kernel vs OS ASKED A LOT
| Kernel | Operating System | |
|---|---|---|
| Kya hai | Core program, memory mein hamesha loaded | Kernel + system programs + utilities + UI |
| Mode | Kernel mode mein chalta hai | Kuch parts user mode mein bhi |
| Example | Linux kernel | Ubuntu, Fedora (kernel + shell + libs + GUI) |
| Scope | OS ka subset | Superset |
One-liner: "Kernel OS ka dil hai, OS poora sharir hai." Linux technically ek kernel hai; Ubuntu ek OS hai. Interviewer isi galti ko pakadta hai.
User view vs System view
- User view — ease of use, performance of my program. Resource utilization ki koi parwah nahi. (Desktop/mobile user)
- System view — OS = resource allocator. Fairness, throughput, utilization matter karta hai. (Mainframe/server)
Embedded systems (washing machine, car ECU) mein user view almost zero hota hai — koi UI hi nahi. Wahan OS sirf resource manager hai. Isliye ye do views alag-alag OS designs ko justify karte hain.
Kernel architectures COMMON
| Type | Idea | Pros | Cons | Example |
|---|---|---|---|---|
| Monolithic | Sab kuch (FS, drivers, scheduler, IPC) ek hi kernel address space mein | Fast — sab function calls hain | Ek driver crash = poora system down; huge code | Linux, Unix |
| Microkernel | Kernel mein sirf minimum (IPC, scheduling, basic memory). Baaki user space mein servers | Reliable, extensible, secure | Slow — har cheez message passing se | Minix, QNX, L4 |
| Hybrid | Microkernel design par performance-critical parts kernel mein | Balance | Complexity | Windows NT, macOS (XNU) |
| Exokernel | Kernel sirf hardware ko securely multiplex karta hai; abstraction app decide kare | App-level optimization | Research-stage, hard to program | MIT Exokernel |
Microkernel ka trade-off ek line mein: monolithic mein file read karna ek function call hai; microkernel mein wahi read ek message hai jo user→kernel→FS server→kernel→user jaata hai. Char mode switches vs zero. Isliye slow, par reliable — FS server crash ho toh restart kar do, kernel zinda rahega.
Is Linux a monolithic kernel or a modular one? Explain the apparent contradiction.
Show solution
Both — and that's the point. Linux is architecturally monolithic: the file system, device drivers, network stack and scheduler all run in a single kernel address space, and they call each other as ordinary function calls.
But Linux also supports loadable kernel modules (LKMs). A driver can be compiled separately and inserted at runtime with insmod / modprobe, and removed with rmmod. So the kernel is modular in how it is built and loaded, but monolithic in how it executes — a loaded module runs in kernel space with full privileges, so a bug in it can still panic the system.
Yaad rakhne ka tareeka: module load karna matlab code ko kernel ke ghar ke andar bulana. Aane-jaane ka darwaza flexible hai (modular), par ek baar andar aa gaya toh usko poore ghar ka access hai (monolithic).
System types & evolution
Batch se lekar real-time tak. Ye section chhota rakho par multiprogramming vs multitasking pakka aata hai.
The evolution, in one flow
Batch systems
Similar jobs are batched together and run without user interaction. The resident monitor loads one job, runs it, loads the next. Control cards (JCL) tell the monitor what to do.
- Problem: CPU idle rehta hai jab job I/O kar raha ho. Utilization terrible.
- Spooling (Simultaneous Peripheral Operation On-Line) — disk ko buffer ki tarah use karo. Jab job A print kar raha ho, CPU job B chala sake. Ye job pool banata hai on disk.
Spooling ka asli fayda: disk pe ab ek pool of ready jobs ban gaya. Pehle jobs tape pe sequentially padi thi — jo pehle aayi wahi chalegi. Ab disk se OS koi bhi job uthha sakta hai. Yahi se job scheduling ka concept paida hua. Interviewer poochhta hai "spooling se kya mila?" — sirf "buffering" mat bolna, bolo "job pool aur isliye scheduling ka choice".
Multiprogramming vs Multitasking vs Multiprocessing vs Multithreading ASKED A LOT
| Term | Kya hota hai | Switch kab hota hai | CPUs |
|---|---|---|---|
| Multiprogramming | Multiple jobs RAM mein; CPU idle na rahe | Jab current job I/O ke liye block ho | 1 |
| Multitasking / Time sharing | Multiprogramming + har job ko chhota time slice | I/O par ya time quantum khatam hone par | 1 |
| Multiprocessing | Ek system mein multiple CPUs/cores | — | >1 |
| Multithreading | Ek process ke andar multiple threads | Thread scheduler ke hisaab se | 1 ya >1 |
Ek line ka difference (yahi bolna interview mein):
Multiprogramming ka goal hai CPU utilization — CPU khali na baithe. Multitasking ka goal hai response time — user ko lage ki uska program abhi chal raha hai.
Multiprogramming mein agar ek job 10 minute tak CPU-bound calculation kar raha hai, toh baaki sab 10 minute wait karenge — aur ye "theek" hai, kyunki CPU toh busy hai. Time sharing mein ye acceptable nahi — 20ms baad usko force se hatao.
Other system types
Symmetric multiprocessing (SMP)
Har processor identical, sab par same OS copy chalti hai, shared memory. Aaj ke sab multicore systems.
Asymmetric multiprocessing
Ek master processor kaam baantta hai, baaki slaves. Simpler par master bottleneck.
Distributed system
Alag-alag machines, apni-apni memory, network se jude. Resource sharing, computation speedup, reliability.
Clustered system
Multiple machines mil kar ek service dete hain, shared storage. Asymmetric (hot standby) ya symmetric (sab active).
Real-time systems COMMON
Correctness depends not only on the logical result but on the time at which the result is produced. A late answer is a wrong answer.
| Hard real-time | Soft real-time | |
|---|---|---|
| Deadline miss | System failure / catastrophe | Quality degrades, system survives |
| Virtual memory | Usually not allowed (page fault = unpredictable delay) | Allowed |
| Secondary storage | Minimal / ROM only | Normal |
| Example | Airbag controller, pacemaker, missile guidance | Video streaming, VoIP, gaming |
"Real-time = fast" — galat. Real-time means predictable, not fast. Ek system jo hamesha exactly 50ms leta hai woh real-time hai. Ek system jo usually 1ms leta hai par kabhi-kabhi 200ms — woh real-time nahi hai, chahe average fast ho. Determinism > speed.
Hardware, interrupts & system calls
Ye section poore OS ki neev hai. Interrupt vs trap vs system call almost guaranteed question hai.
How a computer actually runs a program
The fetch–decode–execute cycle: the CPU fetches the instruction at the address in the program counter (PC), decodes it, executes it, and increments the PC. That's the whole loop, forever.
A program can execute only if it is loaded in main memory. The CPU can only fetch instructions from memory (via cache/registers) — it can never execute directly from disk. This one rule is why swapping, paging, and virtual memory all exist.
Interrupt vs Trap/Exception vs System call ASKED A LOT
| Interrupt | Trap / Exception | System call | |
|---|---|---|---|
| Source | Hardware, external device | Software, error condition | Software, deliberate request |
| Timing | Asynchronous — kabhi bhi | Synchronous — specific instruction par | Synchronous |
| Intentional? | Not by the program | No — it's a bug/condition | Yes — program chaahta hai |
| Example | Keyboard press, disk done, timer | Divide by zero, invalid memory access, page fault | read(), fork(), open() |
| Handled by | Interrupt Service Routine (ISR) | Exception handler | Kernel system-call handler |
Sabse easy tareeka yaad rakhne ka — ghar ka scene:
Interrupt = doorbell. Bahar se koi aaya. Aapne nahi bulaya, kabhi bhi baj sakti hai. Aap jo kar rahe the woh rok kar darwaza kholte ho, phir wapas wahi kaam.
Trap/Exception = khaana banate waqt haath jal gaya. Aapki hi galti se, aapke hi kaam ke dauraan, exactly us moment par. Predictable in the sense ki wahi instruction dobara chalao toh phir hoga.
System call = aapne khud helpline pe call kiya. Jaan-boojh kar, kyunki jo kaam chahiye woh aap khud nahi kar sakte (privileged hai).
Ek aur crucial baat: teeno ka mechanism same hai — CPU mode switch karta hai, current state save karta hai, vector table se handler ka address uthhata hai, handler chalata hai, wapas aata hai. Sirf trigger alag hai. Ye bolne se interviewer impress hota hai.
Interrupt handling — exact steps
- Device controller raises the interrupt line.
- CPU finishes the current instruction (not the whole program), then checks the interrupt line.
- CPU saves the current state — PC and registers — onto the kernel stack.
- CPU switches to kernel mode.
- Interrupt number indexes into the interrupt vector table to get the ISR address.
- ISR runs, servicing the device.
- State restored, mode switched back, execution resumes at the saved PC.
Without it, the handler would have to poll every device to ask "did you interrupt?" — O(n). The vector table gives O(1) dispatch: interrupt number → handler address directly. It lives at a fixed low memory location.
Three ways to do I/O COMMON
| Method | How | CPU cost | Best for |
|---|---|---|---|
| Programmed I/O (polling) | CPU busy-waits, repeatedly checking status register | 100% wasted while waiting | Very fast devices, tiny transfers |
| Interrupt-driven I/O | CPU issues request, does other work, device interrupts when ready | One interrupt per byte/word | Slow devices, small data (keyboard) |
| DMA | DMA controller moves data device↔memory directly; one interrupt per block | Minimal — one interrupt per block | High-speed bulk transfer (disk, network) |
DMA ko samjho ek courier service ki tarah. Polling matlab aap khud har 2 minute mein gate pe jaa kar dekh rahe ho parcel aaya kya. Interrupt matlab courier ghanti bajayega — par har ek packet ke liye alag ghanti. DMA matlab aapne courier ko keh diya "poora carton seedha store room mein rakh do, sab khatam hone par ek baar bata dena". CPU sirf start aur end pe involve hota hai.
Cycle stealing — DMA controller memory bus ko CPU se chhota-chhota "chura" kar use karta hai. CPU thoda slow hota hai, par interrupt overhead se bahut kam.
Dual-mode operation & protection ASKED A LOT
Hardware provides a mode bit: 0 = kernel/monitor/supervisor mode, 1 = user mode. Certain instructions are privileged and can execute only in kernel mode.
| Protection type | Mechanism |
|---|---|
| I/O protection | All I/O instructions are privileged. A user program must make a system call. |
| Memory protection | Base register (smallest legal physical address) + limit register (size of range). Hardware checks every address: valid iff base ≤ addr < base + limit. Loading base/limit is privileged. |
| CPU protection | Timer interrupts after a set period so no program can hog the CPU forever. Setting the timer is privileged. |
Base + limit register ka poora point ye hai ki check hardware karta hai, har single memory access par, zero extra time mein (parallel comparator circuit). Agar OS software mein check karta toh har instruction 10x slow ho jaati. Protection hamesha hardware + OS ka joint effort hota hai — ye line interview mein bolne layak hai.
System calls — the user→kernel doorway
Categories: process control (fork, exec, exit, wait), file management (open, read, write, close), device management (ioctl, read, write), information maintenance (getpid, time), communication (pipe, shmget, send, recv), protection (chmod, umask).
What actually happens on a system call
- Program calls a wrapper in the C library, e.g.
read(). - Wrapper puts the system call number in a register (e.g.
eax/rax) and arguments in other registers. - Executes a trap instruction (
syscall/int 0x80/svc). - CPU switches to kernel mode, jumps to the system-call handler.
- Handler looks up the number in the system call table, runs the kernel routine.
- Return value goes into a register; mode switches back to user.
Mode switch ≠ context switch. A system call causes a mode switch (user→kernel and back) but the same process keeps running — no PCB save/restore of another process, no scheduler involvement. A context switch means the CPU actually moves to a different process. Mode switch is cheap (~100s of ns); context switch is expensive (µs, plus cache/TLB pollution). Interviewers love this one.
Storage hierarchy & caching
| Level | Typical size | Access time | Managed by |
|---|---|---|---|
| Registers | ~KB | < 1 ns | Compiler |
| L1 / L2 / L3 cache | KB – tens of MB | 1–20 ns | Hardware |
| Main memory (RAM) | GB | ~50–100 ns | OS |
| SSD | hundreds of GB | ~50–100 µs | OS |
| Magnetic disk | TB | ~5–10 ms | OS |
| Tape | TB+ | seconds | OS / operator |
Neeche jaate jaate: size badhta hai, cost/bit girti hai, speed girti hai. Ye teeno hamesha saath chalte hain.
Caching ka principle: jo abhi use kiya hai woh dobara chahiye hoga (temporal locality), aur jo uske paas hai woh bhi chahiye hoga (spatial locality). Isliye faster level pe copy rakh lo.
Cache coherency ka problem — same data ke multiple copies (register, cache, RAM, disk). Multiprocessor mein agar CPU-1 apne cache mein A=5 kar de aur CPU-2 ke cache mein A=3 pada ho, toh kaand. Isliye hardware cache-coherence protocols (MESI) chahiye.
A page fault is triggered by a program's own memory reference. So is it an interrupt, a trap, or a system call? And how is it different from a divide-by-zero?
Show solution
A page fault is a trap (exception) — synchronous, generated by the MMU during the execution of a specific instruction, not by an external device.
The interesting difference from divide-by-zero is what happens after:
- Divide by zero — a fault the program cannot recover from by default. The OS typically sends
SIGFPEand the process dies. - Page fault — a recoverable fault. The OS brings the page in from disk, updates the page table, and then re-executes the very same instruction, which now succeeds. The program never knows it happened.
This restartability is the whole reason virtual memory works, and it puts a real constraint on CPU design: the instruction must be re-executable from the beginning without side effects. Instructions that modify memory before faulting (e.g. a block-move that half-completes) need special hardware handling.
Short answer bolne ke liye: "Page fault ek trap hai, par ek recoverable trap. OS page laata hai aur wahi instruction dobara chalata hai — isliye program ko pata bhi nahi chalta."
Why can't a user program just execute I/O instructions directly? It would be faster.
Show solution
Three reasons, in order of importance:
- Protection. Direct disk access means any program could read or overwrite any other user's files, or the OS itself. All isolation collapses.
- Correctness under sharing. Devices are shared. If two processes issue overlapping disk commands with no arbiter, the device gets contradictory instructions and data is corrupted. The OS serialises and queues requests.
- Abstraction. Direct I/O means the program must know the exact controller registers of that specific hardware. Change the disk model and every program breaks. The system-call interface makes
read()work on any device.
The enforcement mechanism: I/O instructions are marked privileged, so attempting one in user mode traps to the OS.
Processes & the PCB
OS ka sabse core abstraction. Memory layout aur fork() questions almost har interview mein hain.
Program vs Process ASKED A LOT
| Program | Process |
|---|---|
| Passive entity — a file on disk | Active entity — program in execution |
| No program counter, no state | Has PC, registers, stack, heap, state |
| Exists forever until deleted | Lives only while executing |
| One copy | One program → many processes |
Recipe vs cooking. Recipe book mein likhi hui recipe = program. Woh bas padi hui hai, kuch nahi kar rahi. Jab aap kitchen mein khade ho kar us recipe ko follow kar rahe ho — gas on hai, kadhai garam hai, aap step 4 pe ho — woh process hai.
Aur dhyan do: ek hi recipe se do log ek saath alag-alag kitchen mein khaana bana sakte hain. Dono ka step number alag hoga, dono ke ingredients alag honge. Same program, do processes. Isliye Chrome ke 10 tabs = ek program, 10 processes.
Process memory layout ASKED A LOT
Neeche se upar yaad karo: Text → Data → BSS → Heap → (gap) → Stack.
Text read-only kyun? Do reasons — (1) accidentally apna hi code overwrite na ho, (2) same program ki 10 copies chal rahi hain toh text ki ek hi physical copy RAM mein rakho, sab share karein. Memory bachi.
Data vs BSS ka farq: int x = 5; ki value executable file mein likhi hoti hai (Data). int y; ki koi value likhne ki zaroorat nahi — bas "mujhe 4 bytes chahiye, zero se bhare hue" (BSS). Isliye ek bada uninitialised array executable ka size nahi badhata. Ye ek badhiya follow-up answer hai.
Stack neeche ki taraf kyun badhta hai? Taaki stack aur heap ek dusre ki taraf badhein aur beech ka free space dono share kar sakein. Agar dono upar badhte toh beech mein fixed boundary banani padti aur ek side ki memory waste hoti.
Stack overflow = stack itna neeche aa gaya ki heap se takra gaya (infinite recursion). Heap ke saath memory leak hoti hai — malloc kiya, free nahi kiya.
"Threads mein kya share hota hai?" — Text, Data, BSS, Heap, and open files are shared. Each thread gets its own stack and its own registers/PC. Yahi ek line poore thread chapter ka nichod hai.
Process Control Block (PCB) / process image
The PCB is the data structure the OS keeps for every process. Contents fall into three groups:
| Group | Contents |
|---|---|
| Process identification | PID, parent PID (PPID), user ID |
| Processor state information | General-purpose registers, program counter, stack pointer, condition codes / PSW |
| Process control information | Process state, scheduling priority, pointers to memory (base/limit or page table), list of open files, accounting info, IPC info, pointers to other PCBs |
PCB ko socho process ka Aadhaar card + medical file + bank passbook ek jagah. Jab process ko CPU se hatana ho, uski poori "zindagi" PCB mein likh do; jab wapas laana ho, PCB se padh kar registers restore kar do. Context switch ka matlab hi PCB save + PCB load hai.
Process states ASKED A LOT
Five-state model
| Transition | Trigger | Preemptive only? |
|---|---|---|
| New → Ready | Admitted by long-term scheduler (memory available) | No |
| Ready → Running | Dispatched by short-term scheduler | No |
| Running → Ready | Timer interrupt / higher-priority process arrives | Yes — only in preemptive systems |
| Running → Waiting | Process requests I/O or waits for an event | No |
| Waiting → Ready | I/O completes, event occurs | No |
| Running → Terminated | exit() or killed | No |
There is no Waiting → Running transition. Ever. When I/O finishes, the process goes to Ready and must be scheduled again. Interviewers deliberately draw this wrong arrow to see if you catch it. Similarly there is no Ready → Waiting.
New state kyun chahiye? Process create ho chuka hai (PCB ban gaya) par abhi memory mein load nahi hua. OS ne abhi "admit" nahi kiya. Ye long-term scheduler ko control deta hai ki multiprogramming degree kitni rakhni hai. Agar RAM full hai toh naye process New mein wait karenge.
Suspended states (7-state model)
When memory runs short, the OS swaps a process out to disk. That gives two extra states:
- Ready/Suspend — in secondary memory, but ready to run once brought back.
- Blocked/Suspend — in secondary memory and also waiting for an event.
Key transition: Blocked/Suspend → Ready/Suspend happens when the awaited event occurs while the process is still swapped out. The medium-term scheduler handles swapping.
Zombie and orphan processes ASKED A LOT
| Zombie | Orphan | |
|---|---|---|
| Who died | Child died | Parent died |
| Situation | Child called exit(), parent hasn't called wait() yet | Parent terminated while child still running |
| What remains | Only the PCB entry (exit status). All memory freed. | A live, fully functional process |
| Resolution | Parent calls wait() → entry reaped. If parent never does, init reaps it when the parent dies. | Re-parented to init (PID 1), which will wait() on it |
| Harmful? | Yes if many — PID table exhaustion | No — normal, this is how daemons are made |
Zombie = bachcha mar gaya, par uska death certificate abhi tak koi lene nahi aaya. Body toh gayab ho gayi (memory free), par record book mein entry padi hai. Parent jab wait() karega tab entry hategi.
Orphan = maa-baap chal base, bachcha zinda hai. Ab init (PID 1) usko adopt kar leta hai. Ye bura nahi hai — daemons isi tareeke se banaye jaate hain: fork karo, parent ko turant exit kara do, child orphan ho kar init ke neeche background mein chalta rahe.
Zombie ko "kill" nahi kar sakte — woh already dead hai. kill -9 ka koi asar nahi. Solution: parent ko kill karo, phir init reap kar lega. Ye ek favourite trick question hai.
fork(), exec(), wait(), exit()
| Call | What it does | Returns |
|---|---|---|
fork() | Creates a near-identical copy of the calling process | 0 in the child, child's PID in the parent, -1 on failure |
exec() | Replaces the current process image with a new program. Same PID. | Nothing on success (never returns), -1 on failure |
wait() | Parent blocks until a child terminates; reaps the zombie | PID of the terminated child |
exit() | Terminates process, frees resources, keeps exit status for parent | Does not return |
fork + exec ka combo hi Unix ka poora philosophy hai. Shell mein jab aap ls type karte ho: shell fork() karta hai (apni copy banata hai), phir child mein exec("ls") karta hai (copy ko ls se replace kar deta hai), aur parent wait() karta hai.
Do alag calls kyun, ek "spawn" kyun nahi? Kyunki fork ke baad aur exec ke pehle ek window milti hai jismein child apna environment set kar sakta hai — file descriptors redirect karo, permissions drop karo. Yahi window hai jo ls > out.txt ko possible banati hai. Ye answer interviewer ko bahut pasand aata hai.
Copy-on-write: fork poori memory copy nahi karta. Parent aur child same physical pages share karte hain, sab read-only mark karke. Jab koi likhne ki koshish kare tab hi us page ki copy banti hai. Isliye fork+exec fast hai — exec toh sab replace hi kar dega, copy karna waste tha.
How many processes are created (including the original) by the following, and how many times does printf execute?
int main() {
fork();
fork();
fork();
printf("hello");
}Show solution
8 total processes (1 original + 7 new), and printf executes 8 times.
Reasoning. Each fork() doubles the number of running processes, because both parent and child continue from the instruction after the fork.
Here n = 3, so 2³ = 8 processes, 7 children. All 8 reach the printf.
If it were fork(); fork(); fork(); but printf came between the second and third fork, only 4 processes exist at that point, so it prints 4 times. Position matters. Also: printf to a pipe/file is block-buffered, so the buffer itself gets copied by fork and output can appear duplicated — this is why you use fflush() or write() in such puzzles.
What does this print, and how many processes are created?
int main() {
if (fork() && fork())
fork();
printf("X");
}Show solution
4 processes total, so X is printed 4 times.
Two facts do all the work here: fork() returns non-zero (true) in the parent and 0 (false) in the child, and && short-circuits — if the left side is false, the right side never executes.
Note that C1 executes only one fork (the first one, which created it) and C2 executes two — the short-circuit is what keeps the total at 4 instead of 8.
Is question mein trap ye hai ki log && ka short-circuit bhool jaate hain. C1 mein doosra fork chalta hi nahi. Agar && ki jagah || hota toh ulta hota — parent short-circuit karta aur children forks karte. Har fork ke baad dono branches alag se trace karo, table bana kar. Yahi safest method hai — mental math mat karo.
Explain the output ordering problem: after fork(), can you predict whether the parent or the child prints first? How would you force an order?
Show solution
No — the order is non-deterministic. After fork(), both processes are in the Ready queue and it is entirely up to the scheduler which runs first. On the same machine, the same binary can produce different orders on different runs. This is a race condition.
To force parent-after-child: the parent calls wait(), which blocks until the child terminates.
pid_t pid = fork(); if (pid == 0) { printf("child\n"); exit(0); } else { wait(NULL); // blocks until child exits printf("parent\n"); }
To force child-after-parent you need explicit synchronisation — a pipe, a semaphore, or a signal — because there is no "wait for parent" call.
Context switch ASKED A LOT
The mechanism by which the CPU stops executing one process and starts another.
What gets saved
- Program counter, stack pointer, all general-purpose registers
- Processor status word / condition codes
- Memory-management information (page table base register / base & limit)
- Kernel stack pointer, accounting info — all into the outgoing PCB
Why it is expensive
- Direct cost — saving and loading registers, updating queues. Microseconds.
- Indirect cost (bigger) — the CPU cache is now full of the old process's data, so the new process starts with cache misses. The TLB may need flushing (unless it's tagged with an ASID). Branch predictors are cold. This "cache pollution" often dominates.
- Pure overhead — during a context switch the system does no useful work.
Isliye time quantum bahut chhota nahi rakh sakte. Agar quantum 1ms hai aur context switch 0.1ms leta hai, toh 10% CPU sirf switching mein chala gaya. Ye exact trade-off Round Robin section mein numerically aayega.
The three schedulers COMMON
| Long-term (job) | Medium-term | Short-term (CPU) | |
|---|---|---|---|
| Decides | Which jobs enter the system | Which processes to swap out/in | Which ready process gets the CPU |
| Controls | Degree of multiprogramming | Degree of multiprogramming (reduces it) | — |
| Frequency | Seconds/minutes — slow | Occasional | Milliseconds — very fast |
| State change | New → Ready | Ready ↔ Ready/Suspend | Ready → Running |
| Present in | Batch systems (absent in most time-sharing/UNIX) | Time-sharing systems | Every system |
Long-term scheduler ka asli kaam: ek acchha mix banana — kuch CPU-bound aur kuch I/O-bound processes. Agar sab CPU-bound le liye toh I/O devices idle rahengi aur ready queue lambi ho jayegi. Agar sab I/O-bound le liye toh CPU idle rahega. Balance chahiye. Ye specific point interviewers pasand karte hain.
Dispatcher vs Scheduler: Scheduler decide karta hai ("kaun chalega") — ye policy hai. Dispatcher karta hai (context switch, mode switch, jump to the right instruction) — ye mechanism hai. Jo time dispatcher leta hai usko dispatch latency kehte hain. Policy vs mechanism — ye phrase bolna.
Threads
Chhota section, par "process vs thread" ke bina koi interview poora nahi hota.
A thread is a basic unit of CPU utilisation — a lightweight process. It has its own thread ID, program counter, register set and stack, but shares the code section, data section, heap, and OS resources (open files, signals) with other threads of the same process.
Process vs Thread ASKED A LOT
| Process | Thread | |
|---|---|---|
| Address space | Own, isolated | Shared with siblings |
| Creation cost | High (new page tables, PCB, memory) | Low |
| Context switch cost | High — address space changes, TLB flush | Low — no address space change |
| Communication | IPC needed (pipes, shared memory, messages) — kernel involved | Just read/write shared variables |
| Fault isolation | One crashes, others survive | One segfaults → whole process dies |
| Synchronisation | Less needed | Essential — shared data = race conditions |
| Own stack? | Yes | Yes (this is the key private thing) |
Ek line mein: "Process alag-alag ghar hain; threads ek hi ghar ke alag-alag kamre hain." Ghar badalna mehnga hai (address space switch). Kamra badalna sasta hai.
Par ghar share karne ka nuksan bhi hai — kitchen common hai, toh do log ek saath gas use karein toh jhagda (race condition). Aur agar ghar mein aag lagi toh sab kamre jal jaate hain (one thread crashes → whole process dies).
Ye trade-off hi answer hai: threads = fast communication + fast switching, but no isolation. Chrome ne isliye har tab ke liye process chuna, thread nahi — ek tab crash ho toh browser na mare.
Benefits of multithreading
- Responsiveness — one thread blocks on I/O, the UI thread keeps responding.
- Resource sharing — threads share memory by default; no IPC setup.
- Economy — creating and switching threads is far cheaper than processes.
- Scalability — a multithreaded process can run on multiple cores in parallel; a single-threaded process cannot.
User-level vs Kernel-level threads COMMON
| User-level threads (ULT) | Kernel-level threads (KLT) | |
|---|---|---|
| Managed by | Thread library in user space | The OS kernel |
| Kernel aware? | No — sees one process | Yes |
| Switching | Very fast — no mode switch | Slower — needs kernel involvement |
| Blocking I/O | One thread blocks → all block | Only that thread blocks |
| Multicore | Cannot use multiple cores | True parallelism |
Mapping models: Many-to-One (all ULTs → one KLT; simple but blocks everything), One-to-One (each ULT → its own KLT; true concurrency, but thread count limited — Linux, Windows), Many-to-Many (m ULTs multiplexed over n KLTs; best of both, complex).
ULT ka killer flaw yaad rakho: kernel ko pata hi nahi ki andar 10 threads hain — usko toh ek process dikhta hai. Toh jab koi ek thread read() kare aur block ho, kernel poore process ko block kar deta hai. Baaki 9 threads ready hain par chal nahi sakte. Yahi reason hai ki aaj sab one-to-one use karte hain.
CPU scheduling
Sabse zyada numerical wala topic. Gantt chart banana aa gaya toh ye section aapka hai.
The vocabulary — get these exactly right
Burst Time (BT) = CPU time needed
Completion Time (CT)= jab process khatam hua
Turnaround Time (TAT) = CT − AT // total time in system
Waiting Time (WT) = TAT − BT // time spent waiting in ready queue
Response Time (RT) = first CPU time − AT // wait until FIRST run, not completion
WT = TAT − BT ko yaad karne ka logic: total system mein kitna time raha (TAT), usme se jitna time actually CPU pe chala (BT) minus kar do — jo bacha woh wait hai. Simple.
RT vs WT ka farq interactive systems mein sabse important hai. Aap type karte ho aur screen pe letter dikhta hai — usko RT control karta hai, TAT nahi. Isliye time-sharing systems RT optimize karte hain aur batch systems TAT.
Maximise: CPU utilisation, throughput (processes completed per unit time). Minimise: turnaround time, waiting time, response time. Interviewers ask "which one does algorithm X optimise?" — SJF minimises average waiting time, RR minimises response time, FCFS optimises nothing but is fair in arrival order.
Preemptive vs Non-preemptive
| Non-preemptive | Preemptive |
|---|---|
| Once a process gets the CPU it keeps it until it terminates or blocks for I/O | OS can forcibly take the CPU away (timer, higher-priority arrival) |
| Simple, low overhead, no race on kernel data | Better response time, needed for time sharing |
| A long process can starve everyone (convoy effect) | More context switches; needs synchronisation on shared kernel data |
| FCFS, SJF, non-preemptive Priority | SRTF, RR, preemptive Priority, MLFQ |
1. FCFS — First Come First Served
Non-preemptive. Ready queue is a plain FIFO.
Processes arrive at time 0 in the order P1, P2, P3 with burst times 24, 3, 3. Find average WT and TAT under FCFS. Then repeat with arrival order P2, P3, P1.
Show solution
Case A — order P1, P2, P3
| P | AT | BT | CT | TAT = CT−AT | WT = TAT−BT |
|---|---|---|---|---|---|
| P1 | 0 | 24 | 24 | 24 | 0 |
| P2 | 0 | 3 | 27 | 27 | 24 |
| P3 | 0 | 3 | 30 | 30 | 27 |
Average WT = (0 + 24 + 27)/3 = 17. Average TAT = (24 + 27 + 30)/3 = 27.
Case B — order P2, P3, P1
WT: P2 = 0, P3 = 3, P1 = 6. Average WT = 9/3 = 3. Average TAT = (3 + 6 + 30)/3 = 13.
Same processes, same burst times — average waiting time dropped from 17 to 3 just by changing arrival order. When one long CPU-bound process gets the CPU first, all the short processes queue behind it like cars behind a truck. Worse: the I/O devices sit idle the entire time, because all the I/O-bound processes are stuck waiting. This is the argument against FCFS.
Convoy effect ko interview mein aise bolo: "Ek lambi truck aage aa gayi single-lane road pe, aur peeche sab chhoti gaadiyan phas gayi. Sirf average waiting time nahi badhta — I/O devices bhi khaali baithi rehti hain, kyunki jo processes I/O karne wale the woh queue mein hain." Ye doosra half hi differentiate karta hai.
2. SJF (Shortest Job First) & SRTF (Shortest Remaining Time First)
SJF is non-preemptive: pick the ready process with the smallest burst. SRTF is its preemptive version: on every arrival, if the newcomer's burst is shorter than the running process's remaining time, preempt.
SJF gives the minimum possible average waiting time for a given set of processes. That is a theorem, not a heuristic. The catch: it requires knowing burst times in advance, which is impossible in practice. So SJF is a benchmark, not a shipping algorithm.
Compute average WT and TAT for both SJF (non-preemptive) and SRTF.
| Process | Arrival Time | Burst Time |
|---|---|---|
| P1 | 0 | 8 |
| P2 | 1 | 4 |
| P3 | 2 | 9 |
| P4 | 3 | 5 |
Show solution
Part A — SJF (non-preemptive)
At t=0 only P1 is present, so P1 runs to completion (t=0→8). At t=8, ready = {P2(4), P3(9), P4(5)} → shortest is P2. Then P4, then P3.
| P | AT | BT | CT | TAT | WT |
|---|---|---|---|---|---|
| P1 | 0 | 8 | 8 | 8 | 0 |
| P2 | 1 | 4 | 12 | 11 | 7 |
| P3 | 2 | 9 | 26 | 24 | 15 |
| P4 | 3 | 5 | 17 | 14 | 9 |
Avg TAT = (8+11+24+14)/4 = 57/4 = 14.25. Avg WT = (0+7+15+9)/4 = 31/4 = 7.75.
Part B — SRTF (preemptive)
Walk the timeline event by event:
- t=0 — only P1 (rem 8). P1 runs.
- t=1 — P2 arrives with 4 < P1's remaining 7. Preempt. P2 runs.
- t=2 — P3 arrives with 9 > P2's remaining 3. No preemption.
- t=3 — P4 arrives with 5 > P2's remaining 2. No preemption.
- t=5 — P2 finishes. Ready = P1(7), P3(9), P4(5). Shortest = P4. P4 runs.
- t=10 — P4 finishes. Ready = P1(7), P3(9). P1 runs.
- t=17 — P1 finishes. P3 runs → finishes at 26.
| P | AT | BT | CT | TAT | WT |
|---|---|---|---|---|---|
| P1 | 0 | 8 | 17 | 17 | 9 |
| P2 | 1 | 4 | 5 | 4 | 0 |
| P3 | 2 | 9 | 26 | 24 | 15 |
| P4 | 3 | 5 | 10 | 7 | 2 |
Avg TAT = (17+4+24+7)/4 = 52/4 = 13.0. Avg WT = (9+0+15+2)/4 = 26/4 = 6.5.
SRTF beats SJF: 6.5 vs 7.75 average waiting time. Preemption lets short jobs jump ahead of a long one that has already started.
SRTF ka method (rat lo): har arrival par rukо aur compare karo — naya banda chhota hai ya current wale ka remaining? Bas. Beech mein kahin nahi rukna, sirf arrivals par aur completions par. Table mein "remaining time" ka column banao aur har event par update karo — mental math mat karo, galti pakki hai.
Predicting the next burst — exponential averaging
Since we can't know the next burst, we estimate it from history:
tn = actual length of the n-th CPU burst
τn = predicted value for the n-th burst
α ∈ [0, 1], commonly α = 0.5
- α = 0 → τn+1 = τn. Recent history ignored entirely.
- α = 1 → τn+1 = tn. Only the most recent burst matters.
- α = 0.5 → recent and past history equally weighted. Expanding the recursion shows each older term is weighted by a decreasing power of (1−α) — hence "exponential".
3. Priority scheduling
Each process gets a priority number; CPU goes to the highest priority. (Convention varies — usually smaller number = higher priority; always confirm in the question.) Available in preemptive and non-preemptive forms. SJF is just priority scheduling where priority = 1/(next burst).
Problem — starvation (indefinite blocking): a low-priority process may never run if high-priority processes keep arriving.
Solution — aging: gradually increase the priority of processes that have been waiting a long time. A process with priority 127 waiting for 15 minutes, aged by 1 every minute, eventually reaches priority 0 and runs.
Legend hai ki 1973 mein MIT ke IBM 7094 ko shut down kiya gaya toh ek process mila jo 1967 se queue mein pada tha. Chhe saal. Ye story interview mein sunao — starvation ka concept instantly clear ho jaata hai aur banda yaad rakhta hai.
4. Round Robin ASKED A LOT
FCFS with preemption. Each process gets a time quantum q. If it doesn't finish, it is preempted and put at the tail of the ready queue.
each process gets 1/n of CPU time in chunks of at most q
no process waits more than (n − 1) × q time units
Number of context switches ≈ Σ ⌈BTi / q⌉ − 1
Four processes, all arriving at time 0: P1 = 24, P2 = 3, P3 = 3, P4 = 6. Time quantum = 4. Draw the Gantt chart and compute average WT and TAT.
Show solution
Ready queue starts as [P1, P2, P3, P4]. Track remaining times.
| Time | Running | Ran for | Remaining after | Queue after |
|---|---|---|---|---|
| 0–4 | P1 | 4 | P1: 20 | P2,P3,P4,P1 |
| 4–7 | P2 | 3 (finishes) | P2: 0 ✓ | P3,P4,P1 |
| 7–10 | P3 | 3 (finishes) | P3: 0 ✓ | P4,P1 |
| 10–14 | P4 | 4 | P4: 2 | P1,P4 |
| 14–18 | P1 | 4 | P1: 16 | P4,P1 |
| 18–20 | P4 | 2 (finishes) | P4: 0 ✓ | P1 |
| 20–24 | P1 | 4 | P1: 12 | P1 |
| 24–28 | P1 | 4 | P1: 8 | P1 |
| 28–32 | P1 | 4 | P1: 4 | P1 |
| 32–36 | P1 | 4 (finishes) | P1: 0 ✓ | — |
| P | BT | CT | TAT = CT−0 | WT = TAT−BT | RT |
|---|---|---|---|---|---|
| P1 | 24 | 36 | 36 | 12 | 0 |
| P2 | 3 | 7 | 7 | 4 | 4 |
| P3 | 3 | 10 | 10 | 7 | 7 |
| P4 | 6 | 20 | 20 | 14 | 10 |
Avg TAT = (36+7+10+20)/4 = 73/4 = 18.25. Avg WT = (12+4+7+14)/4 = 37/4 = 9.25.
When a process is preempted at time t and another process arrives at exactly time t, the newly arrived process is placed in the queue before the preempted one in the standard convention. Always state your assumption if the question is ambiguous — examiners accept either if you're explicit.
What happens to Round Robin as the time quantum q → ∞? And as q → 0? Given a context switch costs 0.1 ms, what is the CPU overhead at q = 1 ms vs q = 10 ms?
Show solution
- q → ∞ (larger than the longest burst): no process is ever preempted, so RR degenerates to FCFS. Response time becomes terrible.
- q → 0: this is called processor sharing — in theory each of n processes appears to run on its own CPU at 1/n speed. In practice, context switch overhead dominates and the system does almost no useful work (thrashing on switches).
Overhead calculation:
- q = 1 ms: 0.1/(1 + 0.1) = 0.0909 → ≈ 9.1% of CPU wasted
- q = 10 ms: 0.1/(10 + 0.1) = 0.0099 → ≈ 0.99% wasted
The rule of thumb: q should be large compared to the context-switch time, but small enough that ~80% of CPU bursts finish within one quantum. Typical real values: 10–100 ms, with switch cost around 10 µs.
Interview mein bolne ka tareeka: "Quantum ek trade-off knob hai. Bada karo toh FCFS ban jaayega — throughput accha, response time kharab. Chhota karo toh response time accha par overhead kha jaayega. Sweet spot woh hai jahan zyadatar bursts ek hi quantum mein khatam ho jaayein."
5. HRRN — Highest Response Ratio Next
Non-preemptive. Designed to fix SJF's starvation of long jobs.
W = time spent waiting so far S = expected service (burst) time
Pick the process with the highest ratio.
Formula ko todo: agar W = 0 (abhi aaya), ratio = 1. Jaise-jaise wait badhta hai, ratio badhta hai. Aur chhote S wale ka ratio tezi se badhta hai (denominator chhota). Toh short jobs ko preference milti hai par lambi job ka bhi ratio dheere-dheere badhta rahega aur eventually woh jeet jaayegi. Aging built-in hai formula ke andar — yahi HRRN ki khoobsurti hai.
P1(AT=0, BT=3), P2(AT=2, BT=6), P3(AT=4, BT=4), P4(AT=6, BT=5), P5(AT=8, BT=2). Schedule using HRRN.
Show solution
t=0: only P1 → runs 0→3.
t=3: ready = P2 (waited 3−2=1). Only choice → P2 runs 3→9.
t=9: ready = P3, P4, P5.
| P | W = 9 − AT | S | RR = (W+S)/S |
|---|---|---|---|
| P3 | 5 | 4 | (5+4)/4 = 2.25 |
| P4 | 3 | 5 | (3+5)/5 = 1.60 |
| P5 | 1 | 2 | (1+2)/2 = 1.50 |
Highest = P3 → runs 9→13.
t=13: ready = P4, P5.
| P | W = 13 − AT | S | RR |
|---|---|---|---|
| P4 | 7 | 5 | (7+5)/5 = 2.40 |
| P5 | 5 | 2 | (5+2)/2 = 3.50 |
Highest = P5 → runs 13→15. Then P4 runs 15→20.
| P | AT | BT | CT | TAT | WT |
|---|---|---|---|---|---|
| P1 | 0 | 3 | 3 | 3 | 0 |
| P2 | 2 | 6 | 9 | 7 | 1 |
| P3 | 4 | 4 | 13 | 9 | 5 |
| P4 | 6 | 5 | 20 | 14 | 9 |
| P5 | 8 | 2 | 15 | 7 | 5 |
Avg TAT = 40/5 = 8.0. Avg WT = 20/5 = 4.0.
Notice at t=9 the shortest job P5 did not win — P3 had waited longer. Pure SJF would have picked P5. That is exactly the anti-starvation behaviour.
6. Multilevel Queue & Multilevel Feedback Queue COMMON
Multilevel Queue (MLQ)
Ready queue is split into several separate queues (e.g. system processes, interactive, interactive editing, batch, student). Each queue has its own scheduling algorithm. Processes are permanently assigned to one queue based on a property (memory size, priority, type).
Scheduling between queues: either fixed-priority preemptive (serve all of queue 1 before queue 2 — risks starvation) or time-slicing between queues (e.g. 80% CPU to foreground RR, 20% to background FCFS).
Multilevel Feedback Queue (MLFQ)
Same, but processes can move between queues. This is the practical, adaptive one.
Defining parameters: number of queues, scheduling algorithm per queue, the rule to upgrade a process, the rule to demote a process, and the rule to decide which queue a new process enters.
MLFQ ka core idea: hum burst time guess nahi karte — hum observe karte hain. Naya process ko sabse upar wali (highest priority, chhota quantum) queue mein daalo. Agar woh apna quantum khatam kar gaya bina finish hue, matlab CPU-bound hai → neeche bhej do. Agar jaldi I/O ke liye block ho gaya, matlab interactive hai → upar hi rakho.
Aging kyun chahiye: agar ek process neeche chala gaya aur upar naye processes aate rahe, toh woh starve karega. Isliye periodically sab processes ko top queue mein wapas laate hain (priority boost).
Ek line: "MLFQ SJF ka approximation hai bina future jaane" — ye bolo, interviewer khush ho jaayega.
Comparison — the summary table
| Algorithm | Preemptive | Avg WT | Starvation | Best for |
|---|---|---|---|---|
| FCFS | No | High (convoy) | No | Batch, simplicity |
| SJF | No | Optimal (for non-preemptive) | Yes — long jobs | Batch with known bursts |
| SRTF | Yes | Optimal overall | Yes — long jobs | Theoretical benchmark |
| Priority | Either | Depends | Yes — fix with aging | Systems with importance levels |
| Round Robin | Yes | Higher than SJF | No | Time sharing — best response time |
| HRRN | No | Good | No — aging built in | Balanced batch |
| MLFQ | Yes | Good, adaptive | No (with boosting) | General-purpose OS |
Multiprocessor scheduling RARE
- Asymmetric — one master processor handles all scheduling; others just run user code. Simple, no data races on the scheduler, but the master is a bottleneck.
- Symmetric (SMP) — each processor schedules itself, from a common ready queue or its own private queue.
- Processor affinity — keep a process on the same CPU so its cache stays warm. Soft affinity = try; hard affinity = guaranteed.
- Load balancing — push migration (a task periodically moves overloaded work off a CPU) vs pull migration (an idle CPU pulls work). These fight processor affinity — that tension is the interview point.
Concurrency & synchronisation
Ye OS ka sabse conceptual section hai. Race condition se semaphore tak — step by step.
Why there is a problem at all
Two processes share a variable count. One increments, one decrements. In C both look like a single statement, but the compiler emits three machine instructions:
The OS can preempt between any two machine instructions. Start with count = 5 and interleave:
| Step | Executed | R1 | R2 | count |
|---|---|---|---|---|
| 1 | P: R1 = count | 5 | — | 5 |
| 2 | P: R1 = R1 + 1 | 6 | — | 5 |
| 3 | switch → C: R2 = count | 6 | 5 | 5 |
| 4 | C: R2 = R2 − 1 | 6 | 4 | 5 |
| 5 | C: count = R2 | 6 | 4 | 4 |
| 6 | switch → P: count = R1 | 6 | 4 | 6 |
Final answer is 6. Reverse the last two steps and it is 4. The correct answer is 5. Both are wrong, and which one you get depends on timing.
A situation where several processes access and manipulate shared data concurrently, and the outcome depends on the particular order in which the accesses take place.
Yahi poore chapter ki jad hai. count++ aapko ek line dikhti hai, par CPU ke liye woh teen alag steps hain — load, modify, store. Beech mein OS aapko hata sakta hai.
Bank account analogy: aap aur aapka bhai ek hi account se ek saath paise nikaal rahe ho, do alag ATMs se. Dono machine ne balance padha ₹5000. Aapne 3000 nikale, usne 4000. Dono ne apne hisaab se naya balance likh diya. Bank ka record ab galat hai. Padhne aur likhne ke beech ka gap hi problem hai.
Sabse important insight: race condition har baar nahi hoti. 999 baar sahi chalega, 1000vi baar bug. Isliye ye bugs sabse mushkil hote hain — reproduce hi nahi hote. Non-determinism hi asli dushman hai.
Critical section & the three requirements ASKED A LOT
The critical section is the segment of code where a process accesses shared resources. General structure:
do { // ENTRY SECTION ← request permission critical section // EXIT SECTION ← signal that you're done remainder section } while (true);
- Mutual Exclusion — if a process is executing in its critical section, no other process may be in its critical section.
- Progress — if no process is in its critical section and some want to enter, only those not in their remainder section may participate in the decision, and the decision cannot be postponed indefinitely.
- Bounded Waiting — there is a bound on the number of times other processes may enter their critical sections after a process has made a request and before that request is granted.
Teeno ko simple bhasha mein:
Mutual Exclusion = "ek time pe sirf ek banda bathroom mein." Ye sabse obvious hai.
Progress = "agar bathroom khaali hai aur koi bahar khada hai, toh usko andar jaana chahiye." Deadlock nahi hona chahiye. Aur decision lene mein woh log involve nahi hone chahiye jo abhi jaana hi nahi chahte (remainder section wale).
Bounded Waiting = "aapko infinite baar overtake nahi kiya jaa sakta." Line mein khade ho toh koi guarantee honi chahiye ki max N logon ke baad aapka number aayega. Ye starvation ko rokta hai.
Interview trick: koi bhi galat solution dikhaye toh in teeno ke against check karo. Zyadatar galat solutions Progress fail karte hain, Mutual Exclusion nahi.
Software solutions — the wrong ones first
Attempt 1: strict alternation with a turn variable
// shared: int turn = 0; // Process P0 Process P1 while (turn != 0) ; while (turn != 1) ; critical section critical section turn = 1; turn = 0;
Mutual exclusion: yes. Progress: NO. If P0 finishes its critical section, sets turn = 1, and then never wants to enter again, P1 can enter once — and after that P1 is stuck forever waiting for turn = 1, even though P0 is sitting in its remainder section and the critical section is empty.
Ye strict alternation hai — bari-bari se. Problem: agar ek banda aana hi nahi chahta toh doosra bhi phas jaata hai. Bathroom khaali hai, aap bahar khade ho, par rule kehta hai "abhi uski baari hai" — aur woh ghar pe hai. Progress fail.
Attempt 2: two flags
// shared: bool flag[2] = {false, false}; // Process Pi flag[i] = true; while (flag[j]) ; critical section flag[i] = false;
Mutual exclusion: yes. Progress: NO — deadlock. If both set their flag to true at almost the same instant, both then see the other's flag as true and both loop forever. Neither enters.
Peterson's solution — the correct one ASKED A LOT
// shared int turn; bool flag[2] = {false, false}; // Process Pi (j = 1 - i) do { flag[i] = true; // I want in turn = j; // but you go first (politeness!) while (flag[j] && turn == j) ; // busy wait critical section flag[i] = false; remainder section } while (true);
Peterson ka jaadu ek line mein hai: turn = j; — "main andar jaana chahta hoon, par pehle tum jao."
Agar dono ek saath aaye, dono flag = true karenge, aur dono turn ko doosre ka number denge. Par turn ek hi variable hai — jo baad mein likhega uski value rahegi. Maan lo P0 ne baad mein turn = 1 likha. Ab P1 ka condition flag[0] && turn == 0 false hai (turn = 1 hai), toh P1 andar chala jaayega. P0 wait karega. Deadlock nahi hua.
Yeh "pehle aap, pehle aap" wali politeness hi solution hai — Attempt 2 mein dono zid kar rahe the "main pehle", isliye dono phase. Peterson mein dono jhukte hain, aur kyunki turn single variable hai, exactly ek ka jhukna "count" hota hai.
Proves all three: Mutual exclusion (both can't have the winning turn value), Progress (the loop condition requires both flag[j] and turn == j; if P1 isn't interested flag[j] is false and P0 walks in), Bounded waiting (after Pi exits and re-requests, it sets turn = j, so Pj gets at most one turn ahead — bound is 1).
Peterson's algorithm is not guaranteed to work on modern processors, because CPUs and compilers reorder memory reads and writes for performance. The write to flag[i] and the write to turn may become visible to the other core in the wrong order, breaking the proof. Real implementations need memory barriers/fences (or volatile/atomics with the right ordering). Saying this shows you understand it beyond the textbook.
Hardware solutions
Disabling interrupts
Simplest idea: turn interrupts off before the critical section, on after. Works on a uniprocessor (no preemption is possible). Fails on multiprocessors — disabling interrupts on one core doesn't stop another core from entering. Also dangerous: a bug means the system freezes, and it's a privileged operation, so user code can't do it.
Test-and-Set (TSL) COMMON
An atomic hardware instruction — reads a value and sets it to true in one indivisible step.
bool TestAndSet(bool *target) { // executed ATOMICALLY by hardware bool rv = *target; *target = true; return rv; } // usage — shared: bool lock = false; do { while (TestAndSet(&lock)) ; // spin until we get false back critical section lock = false; remainder section } while (true);
Compare-and-Swap (CAS)
int CompareAndSwap(int *value, int expected, int new_value) { int temp = *value; if (*value == expected) *value = new_value; return temp; }
Atomic ka matlab kya? "Beech mein koi nahi ghus sakta." Hardware bus ko lock kar deta hai us ek instruction ke liye. Load-modify-store ki teen steps ek ban jaati hain. Yahi race condition ki jad ko kaat deta hai.
TSL ka problem: while(TestAndSet(...)) — ye busy waiting hai. Process CPU pakde rehta hai aur bas loop maar raha hai, koi kaam nahi kar raha. Isko spinlock kehte hain.
Spinlock kab acchha hai? Jab lock bahut chhote time ke liye hold hoga aur aapke paas multiple cores hain. Kyunki block-and-wakeup ka apna cost hai (do context switches). Agar lock 100 nanosecond ke liye hai toh spin karna sasta hai. Agar 10 millisecond ke liye hai toh block karna sasta hai. Yahi answer hai "spinlock vs mutex kab use karein" ka.
TSL bounded waiting guarantee nahi karta — koi unlucky process baar-baar haar sakta hai. Uske liye ek waiting array aur turn-passing logic chahiye.
Semaphores ASKED A LOT
A semaphore S is an integer variable accessed only through two atomic operations: wait(S) (also called P or down) and signal(S) (also called V or up).
// naive version — busy waiting wait(S) { while (S <= 0) ; S--; } signal(S) { S++; } // real version — no busy waiting wait(S) { S--; if (S < 0) { add this process to S.queue; block(); } } signal(S) { S++; if (S <= 0) { remove a process P from S.queue; wakeup(P); } }
In the no-busy-wait version, a negative value of S has meaning: |S| is the number of processes blocked on that semaphore. If S = −3, three processes are waiting. This is a favourite one-mark question.
Counting vs Binary semaphore
| Counting semaphore | Binary semaphore | |
|---|---|---|
| Range | Any integer (unrestricted) | Only 0 or 1 |
| Used for | Controlling access to a resource with N instances | Mutual exclusion (one resource) |
| Init value | Number of available resources | 1 |
| Example | 5 printers → S = 5 | One shared variable → S = 1 |
Mutex vs Binary semaphore ASKED A LOT
| Mutex (lock) | Binary semaphore | |
|---|---|---|
| Purpose | Locking — protect a critical section | Signalling — notify another thread of an event |
| Ownership | Has an owner. Only the thread that locked it may unlock it | No ownership. Any thread can signal |
| Typical use | Protect shared data | Producer signals consumer that an item is ready |
| Priority inversion | Can implement priority inheritance (because owner is known) | Cannot — no owner to boost |
| Recursion | Recursive mutexes exist | No such concept |
Ye question guaranteed hai. Yaad rakhne ka tareeka:
Mutex = bathroom ki chaabi. Jisne li, wahi wapas karega. Koi doosra aapke liye darwaza nahi khol sakta. Ownership hoti hai.
Binary semaphore = ghanti / signal. Koi bhi baja sakta hai. Producer ghanti bajata hai, consumer sunta hai. Jo wait() kare aur jo signal() kare woh alag threads ho sakte hain — aur yahi asli use case hai.
Ek line ka answer: "Mutex locking ke liye hai, semaphore signalling ke liye. Mutex ka owner hota hai, semaphore ka nahi."
Priority inversion wala bonus point: Low-priority thread ne mutex liya, high-priority thread wait kar raha hai, aur beech mein medium-priority thread CPU le kar chal raha hai — high effectively medium se peeche ho gaya. Priority inheritance se lock-holder ko temporarily high priority de dete hain. Ye sirf mutex mein possible hai kyunki owner pata hai. (Mars Pathfinder 1997 mein yahi bug tha — story sunane layak hai.)
Classic synchronisation problems
Teen problems: producer-consumer, readers-writers, dining philosophers. In teeno ka code aur trap yaad rakho.
1. Producer–Consumer (bounded buffer) ASKED A LOT
A producer puts items into a buffer of size n; a consumer removes them. Producer must wait if the buffer is full; consumer must wait if it is empty.
// three semaphores semaphore mutex = 1; // mutual exclusion on the buffer semaphore empty = n; // count of EMPTY slots semaphore full = 0; // count of FULL slots Producer: Consumer: do { do { produce an item wait(full); wait(empty); wait(mutex); wait(mutex); remove item from buffer add item to buffer signal(mutex); signal(mutex); signal(empty); signal(full); consume the item } while (true); } while (true);
Teen semaphores kyun? Har ek ka apna kaam hai:
mutex= buffer ko ek time pe ek hi banda chhue. (mutual exclusion)empty= "kitni khaali jagah bachi hai" ka counter. Producer isko consume karta hai.full= "kitne items pade hain" ka counter. Consumer isko consume karta hai.
Symmetry dekho: Producer wait(empty) → signal(full). Consumer wait(full) → signal(empty). Bilkul mirror image. Yahi se code yaad rehta hai — rat-ne ki zaroorat nahi.
What happens if the producer's two wait statements are swapped, i.e. wait(mutex) comes before wait(empty)?
Producer: wait(mutex); // ← swapped wait(empty); // ← add item signal(mutex); signal(full);
Show solution
Deadlock. Here is the exact sequence:
- Buffer is full, so
empty = 0. - Producer executes
wait(mutex)→ succeeds, mutex is now 0. Producer holds the lock. - Producer executes
wait(empty)→ empty is 0, so the producer blocks — while still holding mutex. - Consumer arrives, executes
wait(full)→ succeeds (buffer has items). - Consumer executes
wait(mutex)→ mutex is 0 → consumer blocks. - Producer waits for the consumer to free a slot. Consumer waits for the producer to release mutex. Circular wait → deadlock.
Never acquire the mutex before the counting semaphore. Generally: never block on a condition while holding a lock. The counting semaphores (empty/full) are the ones that can block for a long time; the mutex must be the innermost, shortest-held lock.
Bolne wala one-liner: "Aapne bathroom ki chaabi le li aur andar ja kar so gaye kyunki paani nahi aa raha. Ab paani wala aa bhi jaaye toh andar aa nahi sakta, kyunki chaabi aapke paas hai. Lock pakad kar wait mat karo."
What if the two signal statements in the producer are swapped — signal(full) before signal(mutex)?
Show solution
No deadlock. It still works correctly — only a small efficiency loss.
Reason: signal() never blocks. It always increments and returns. So the order of two signals cannot create a circular wait.
The minor cost: after signal(full), a waiting consumer becomes ready and may be scheduled immediately, only to block on wait(mutex) because the producer hasn't released it yet. That's one wasted wakeup + context switch. Correct, just slightly slower.
Order of waits matters (can deadlock). Order of signals does not (only performance). If you remember only one thing about this problem, remember this line.
2. Readers–Writers COMMON
Many readers may read simultaneously. A writer needs exclusive access — no other writer and no reader.
semaphore mutex = 1; // protects readcount semaphore wrt = 1; // exclusive access for writers int readcount = 0; Writer: Reader: wait(wrt); wait(mutex); writing is performed readcount++; signal(wrt); if (readcount == 1) wait(wrt); // FIRST reader locks out writers signal(mutex); reading is performed wait(mutex); readcount--; if (readcount == 0) signal(wrt); // LAST reader lets writers in signal(mutex);
Trick ye hai: wrt semaphore ko sirf pehla reader lock karta hai aur aakhri reader release karta hai. Beech wale readers bas readcount badha-ghata kar andar-bahar hote rehte hain.
Socho ek museum ka gate — pehla visitor gate kholta hai aur "CLOSED FOR MAINTENANCE" board hata deta hai; aakhri visitor jaate waqt wapas laga deta hai. Beech wale bas andar-bahar chalte rehte hain.
Ye version "readers preference" hai — aur isme writer starve kar sakta hai. Agar readers continuously aate rahein, readcount kabhi 0 nahi hoga, aur writer hamesha wait karega. Interviewer yahi follow-up poochhta hai: "kya isme koi starve ho sakta hai?" Answer: haan, writer. Fix: writers-preference variant, ya fair queueing.
3. Dining Philosophers ASKED A LOT
Five philosophers sit around a table with five chopsticks between them. To eat, a philosopher needs both the left and right chopstick. They alternate between thinking and eating.
// the NAIVE solution — semaphore chopstick[5] all initialised to 1 do { wait(chopstick[i]); // pick up LEFT wait(chopstick[(i+1) % 5]); // pick up RIGHT eat signal(chopstick[i]); signal(chopstick[(i+1) % 5]); think } while (true);
If all five philosophers pick up their left chopstick at the same instant, every chopstick is held and every philosopher is waiting for the right one, which their neighbour holds. Perfect circular wait. Nobody eats, ever.
Four standard fixes
| Fix | How | Which deadlock condition it breaks |
|---|---|---|
| Allow at most 4 to sit | Add a counting semaphore initialised to 4 | Circular wait (with 5 sticks and 4 diners, someone always gets both) |
| Pick up both or neither | Grab both chopsticks inside one critical section | Hold-and-wait |
| Asymmetric solution | Odd philosophers take left then right; even take right then left | Circular wait — the cycle is broken |
| Resource ordering | Always pick up the lower-numbered chopstick first | Circular wait |
Asymmetric solution sabse elegant hai — aur yahi bolna interview mein. Kyun kaam karta hai: agar P0 aur P1 dono chopstick C1 chahte hain, par ek left-first hai aur doosra right-first, toh dono ek hi chopstick ke liye pehle compete karenge. Ek jeetega, doosra wahin ruk jaayega — bina koi chopstick pakde. Circular chain toot gayi.
Aur ek important baat: deadlock hatana kaafi nahi — starvation bhi rokni hai. Agar do philosophers baar-baar khaate rahein aur teesra hamesha haar jaaye, deadlock nahi hai par woh bhookha mar jaayega. Acchha solution dono handle karta hai. Ye distinction bolo, marks/impression dono milte hain.
Monitors COMMON
A monitor is a high-level synchronisation construct: an abstract data type in which only one process may be active inside the monitor at a time. Mutual exclusion is provided automatically by the compiler/language, not by the programmer.
monitor SharedBuffer {
// shared variables — accessible only from inside
int count = 0;
condition notFull, notEmpty; // condition variables
procedure insert(item x) {
if (count == N) notFull.wait();
... add x ...
count++;
notEmpty.signal();
}
procedure remove() { ... }
}
Condition variables provide x.wait() (suspend the calling process until someone signals x) and x.signal() (resume exactly one suspended process; if none is suspended, the signal is lost — no effect).
| Semaphore | Monitor | |
|---|---|---|
| Level | Low-level primitive | High-level language construct |
| Mutual exclusion | Programmer must write wait/signal correctly | Automatic — compiler enforces it |
| Error-prone? | Very — one swapped/missing call breaks everything | Much safer |
| Signal with no waiter | Remembered (counter increments) | Lost (no effect) |
| Found in | OS kernels, C with pthreads | Java (synchronized), C#, Concurrent Pascal |
Semaphore vs monitor ka farq ek line mein: semaphore mein aapko yaad rakhna padta hai ki lock lena aur chhodna hai; monitor mein compiler ye khud karta hai. Isliye Java mein aap sirf synchronized likhte ho — lock lena/chhodna automatic.
"Signal lost" wala difference bahut important hai. Semaphore ek counter hai — usme yaad rehta hai ki kitne signals aaye. Condition variable ki koi memory nahi — agar signal aaya aur koi wait nahi kar raha tha, woh signal gayab. Isliye condition variables hamesha while (condition) wait(); ke saath use karte hain, if ke saath nahi.
Inter-process communication (IPC)
Chhota section, par "processes baat kaise karte hain" ek common opener hai.
Two models COMMON
| Shared memory | Message passing | |
|---|---|---|
| How | A memory region is mapped into both address spaces; both read/write it | Processes exchange discrete messages via send() / receive() |
| Kernel involvement | Only to set up the region; then none | On every message |
| Speed | Fast — memory speed | Slower — system call per message |
| Synchronisation | Programmer's job — race conditions are yours to fix | Built into the primitives |
| Distributed systems | No — needs shared physical memory | Yes — works across a network |
| Data size | Good for large data | Better for small amounts |
Shared memory = ek common whiteboard jispe dono likh sakte hain — fast, par jhagda ho sakta hai aur aapko khud rules banane padenge. Message passing = ek dusre ko chitthi bhejna — safe, ordered, par har chitthi mein post office (kernel) ka time lagta hai.
Message passing design choices
Synchronisation
- Blocking send — sender waits until the message is received. Non-blocking send — sender continues immediately.
- Blocking receive — receiver waits until a message is available. Non-blocking receive — returns either a message or null.
- Rendezvous = blocking send + blocking receive. Both must arrive at the meeting point.
Addressing
- Direct —
send(P, msg)names the process explicitly. A link is established automatically between exactly two processes. Downside: hard-codes process identity; changing a process name breaks all references. - Indirect (mailbox / port) —
send(A, msg)sends to a mailbox. A link exists only if two processes share a mailbox; a mailbox may be shared by more than two, and one process may have many mailboxes. Far more flexible.
If P1, P2 and P3 all share mailbox A, P1 sends one message, and both P2 and P3 execute receive() — who gets it? The design must pick a rule: (a) allow a link between at most two processes, (b) allow only one process at a time to execute receive, or (c) let the system select the receiver arbitrarily and notify the sender who got it.
Buffering
The queue attached to a link can be zero capacity (no buffering — sender must block until receive; rendezvous), bounded capacity (n messages; sender blocks only when full), or unbounded capacity (sender never blocks — theoretical ideal).
Real-world IPC mechanisms in Unix
| Mechanism | Direction | Related processes? | Persists after process exit? |
|---|---|---|---|
| Pipe (unnamed) | One-way | Must be related (parent–child) | No |
| Named pipe (FIFO) | One-way (or two with two FIFOs) | Any processes | Yes — it's a file |
| Shared memory | Two-way | Any | Yes, until removed |
| Message queue | Two-way | Any | Yes |
| Socket | Two-way | Any, even across machines | No |
| Signal | One-way, no data | Any (with permission) | N/A |
Pipe ka classic example aap roz use karte ho: ls | grep txt. Shell ek pipe banata hai, ls ka stdout usme daal deta hai aur grep ka stdin usse jod deta hai. Ye kaam fork ke baad, exec se pehle hota hai — yaad hai woh window jiski baat §04 mein ki thi? Yahi uska asli use hai.
Deadlocks
4 conditions + Banker's algorithm. Ye do cheezein aati hain toh deadlock ka poora chapter aapka hai.
A set of processes is deadlocked when every process in the set is waiting for an event that can only be caused by another process in the same set. Since all are waiting, none can cause any event, so all wait forever.
Bridge crossing example (slides mein hai): ek single-lane pul par dono taraf se gaadiyan aa gayi, aamne-saamne. Na koi aage jaa sakta, na peeche hat sakta. Deadlock. Recovery ka ek tareeka: ek side ki gaadiyon ko reverse karao — matlab preemption + rollback. Aur agar sirf ek hi side ki gaadiyan hamesha reverse hoti rahein toh starvation ho jaayegi.
The four necessary conditions ASKED A LOT
- Mutual exclusion — at least one resource is non-shareable; only one process at a time can use it.
- Hold and wait — a process holding at least one resource is waiting to acquire additional resources held by others.
- No preemption — a resource can only be released voluntarily by the process holding it.
- Circular wait — there exists a set {P0, P1, …, Pn} such that P0 waits for a resource held by P1, P1 for P2, …, Pn for P0.
Yaad karne ka tareeka — "MHNC" ya simple story:
Ek cheez aisi ho jo share na ho sake (mutual exclusion). Log ek cheez pakde-pakde doosri maang rahe hon (hold and wait). Kisi se zabardasti chheena na jaa sake (no preemption). Aur wait karne ka ek chakkar ban jaaye (circular wait).
Sabse important point jo log bhool jaate hain: ye chaaron necessary hain, par akele sufficient nahi hain — sirf circular wait hone se deadlock guarantee nahi hota agar resources ke multiple instances hain. (Isi ka proof RAG section mein hai.)
Aur: deadlock ko rokne ke liye sirf ek condition todni kaafi hai. Chaaron todne ki zaroorat nahi. Yahi prevention ka poora idea hai.
Resource Allocation Graph (RAG) ASKED A LOT
- Vertices: processes (circles, Pi) and resource types (rectangles, Rj, with a dot for each instance).
- Request edge: Pi → Rj (process is waiting for the resource).
- Assignment edge: Rj → Pi (an instance is allocated to the process).
- No cycle → definitely no deadlock.
- Cycle + only one instance of each resource type in the cycle → deadlock, guaranteed.
- Cycle + multiple instances → deadlock is possible but not certain.
Ye samjho: cycle ka matlab hai "ek chakkar ban gaya". Agar har resource ki sirf ek copy hai, toh chakkar mein har banda exactly usi copy ka wait kar raha hai jo agle ke paas hai — koi ummeed nahi. Par agar do copies hain, toh ho sakta hai doosri copy kisi tisre process ke paas ho jo cycle mein nahi hai — woh khatam hoga, release karega, chakkar toot jaayega. Isliye multiple instances mein cycle sirf "shak" hai, "saboot" nahi.
Four ways to handle deadlock
| Approach | Idea | Cost | Used by |
|---|---|---|---|
| Prevention | Structurally break one of the four conditions | Low device utilisation, low throughput | Special-purpose systems |
| Avoidance | Require advance info on max needs; only grant requests that keep the system in a safe state | Needs max claims upfront; runtime overhead | Rare in practice |
| Detection + recovery | Let deadlock happen, detect it, then recover | Detection algorithm cost + recovery loss | Some databases |
| Ignore it ("ostrich algorithm") | Pretend deadlocks never happen; reboot if the system hangs | Occasional hang | Linux, Windows, most real OSes |
Haan, sach mein — Linux aur Windows deadlock ko ignore karte hain. Kyunki deadlock bahut kam hota hai, aur prevention/avoidance ka cost har request par lagta hai. Reasoning: "har din ek chhota tax dena vs saal mein ek baar reboot karna" — reboot sasta hai. Ye answer bolne se interviewer ko lagta hai aapne sirf textbook nahi padha.
Deadlock prevention — attack each condition
| Condition | How to break it | Problem with doing so |
|---|---|---|
| Mutual exclusion | Make resources shareable (e.g. read-only files); use spooling for printers | Usually impossible. Some resources are inherently non-shareable |
| Hold and wait | Either (a) request all resources before starting, or (b) release everything before requesting anything new | Low utilisation (resources held but unused for long) + starvation (a process needing many popular resources may never get them all at once) |
| No preemption | If a process requests something unavailable, preempt all its currently held resources; it restarts when it can get everything | Only works for resources whose state is easy to save/restore (CPU registers, memory). Not printers or tape drives |
| Circular wait | Impose a total ordering on resource types; a process may only request resources in increasing order of enumeration | Restricts programming freedom; ordering must be chosen well |
Resource ordering is the one that actually gets used in real code. Every lock in the Linux kernel has a documented acquisition order, and tools like lockdep verify it. If you've ever had a code review comment saying "acquire lock A before lock B", that's circular-wait prevention.
Deadlock avoidance — safe states
A state is safe if there exists a safe sequence ⟨P1, P2, …, Pn⟩ such that for each Pi, the resources Pi may still request can be satisfied by the currently available resources plus the resources held by all Pj with j < i. That is: even in the worst case, everyone can finish, in some order.
Unsafe → deadlock is possible, not certain
Deadlocked ⊂ Unsafe
Avoidance = never let the system enter an unsafe state.
Unsafe ≠ deadlock. Unsafe ka matlab sirf itna hai ki "agar sab log apni maximum demand ek saath maang lein toh phas sakte hain." Ho sakta hai woh kabhi na maangein aur sab theek chale. Par OS risk nahi leta — avoidance conservative hai. Ye distinction interview mein bahut poochha jaata hai.
Banker's Algorithm ASKED A LOT
Data structures (n processes, m resource types)
Max[n][m] — maximum demand of each process
Allocation[n][m] — currently allocated to each process
Need[n][m] = Max − Allocation ← always compute this first
Safety algorithm
Work = Available;Finish[i] = falsefor all i.- Find an i with
Finish[i] == falseandNeed[i] ≤ Work. If none exists, go to step 4. Work = Work + Allocation[i];Finish[i] = true; go to step 2.- If
Finish[i] == truefor all i, the system is safe. Otherwise, unsafe.
Bank ka analogy (isi se naam pada): bank ke paas limited cash hai. Har customer ne apni maximum credit limit bata rakhi hai. Bank loan tabhi deta hai jab uske paas itna cash bache ki kam se kam ek customer ki poori limit puri kar sake — woh customer khush ho kar poora paisa wapas karega, aur us paise se agla nipat jaayega. Domino effect. Agar aisa koi order nahi mil raha, toh loan reject.
System has 5 processes and 3 resource types: A (10 instances), B (5), C (7). Current state:
| Process | Allocation A B C | Max A B C | ||||
|---|---|---|---|---|---|---|
| P0 | 0 | 1 | 0 | 7 | 5 | 3 |
| P1 | 2 | 0 | 0 | 3 | 2 | 2 |
| P2 | 3 | 0 | 2 | 9 | 0 | 2 |
| P3 | 2 | 1 | 1 | 2 | 2 | 2 |
| P4 | 0 | 0 | 2 | 4 | 3 | 3 |
(a) Is the system in a safe state? Give a safe sequence.
(b) Can P1's request (1, 0, 2) be granted immediately?
Show solution
Step 1 — compute Available.
Total allocated: A = 0+2+3+2+0 = 7, B = 1+0+0+1+0 = 2, C = 0+0+2+1+2 = 5.
Step 2 — compute Need = Max − Allocation.
| Process | Need (A B C) | ||
|---|---|---|---|
| P0 | 7 | 4 | 3 |
| P1 | 1 | 2 | 2 |
| P2 | 6 | 0 | 0 |
| P3 | 0 | 1 | 1 |
| P4 | 4 | 3 | 1 |
Step 3 — run the safety algorithm. Work = (3, 3, 2).
| Iter | Work before | Check | Pick | Work after = Work + Alloc |
|---|---|---|---|---|
| 1 | (3,3,2) | P0 Need(7,4,3) > Work ✗ · P1 Need(1,2,2) ≤ Work ✓ | P1 | (3,3,2)+(2,0,0) = (5,3,2) |
| 2 | (5,3,2) | P0 (7,4,3) ✗ · P2 (6,0,0) ✗ · P3 (0,1,1) ≤ Work ✓ | P3 | (5,3,2)+(2,1,1) = (7,4,3) |
| 3 | (7,4,3) | P0 (7,4,3) ≤ Work ✓ | P0 | (7,4,3)+(0,1,0) = (7,5,3) |
| 4 | (7,5,3) | P2 (6,0,0) ≤ Work ✓ | P2 | (7,5,3)+(3,0,2) = (10,5,5) |
| 5 | (10,5,5) | P4 (4,3,1) ≤ Work ✓ | P4 | (10,5,5)+(0,0,2) = (10,5,7) |
All five finished. (a) The system IS in a safe state. Safe sequence: ⟨P1, P3, P0, P2, P4⟩.
The safe sequence is not unique. ⟨P1, P3, P4, P0, P2⟩ also works. If asked, giving any one valid sequence is enough — but check yours by walking through it.
(b) P1 requests (1, 0, 2). Run the resource-request algorithm:
- Is Request ≤ Need[P1]? (1,0,2) ≤ (1,2,2) ✓ — legal request.
- Is Request ≤ Available? (1,0,2) ≤ (3,3,2) ✓ — resources exist.
- Pretend to grant it and check safety of the new state:
Available = (3,3,2) − (1,0,2) = (2,3,0)
Allocation[P1] = (2,0,0) + (1,0,2) = (3,0,2)
Need[P1] = (1,2,2) − (1,0,2) = (0,2,0)
New Need table: P0(7,4,3), P1(0,2,0), P2(6,0,0), P3(0,1,1), P4(4,3,1). Work = (2,3,0).
| Iter | Work | Pick | Work after |
|---|---|---|---|
| 1 | (2,3,0) | P1 — Need(0,2,0) ≤ (2,3,0) ✓ | (2,3,0)+(3,0,2) = (5,3,2) |
| 2 | (5,3,2) | P3 — Need(0,1,1) ✓ | (5,3,2)+(2,1,1) = (7,4,3) |
| 3 | (7,4,3) | P0 — Need(7,4,3) ✓ | (7,4,3)+(0,1,0) = (7,5,3) |
| 4 | (7,5,3) | P2 — Need(6,0,0) ✓ | (7,5,3)+(3,0,2) = (10,5,5) |
| 5 | (10,5,5) | P4 — Need(4,3,1) ✓ | (10,5,7) |
Safe. Sequence ⟨P1, P3, P0, P2, P4⟩. So the request can be granted immediately.
Exam mein steps ka order kabhi mat badlo: (1) Available nikalo (2) Need = Max − Allocation (3) safety loop chalao, har iteration mein Work update karo. Request wale part mein teen checks: Request ≤ Need? Request ≤ Available? Naya state safe? Teeno pass toh grant, warna wait.
A system has n processes and m instances of a single resource type. Each process needs at most k instances. What is the condition on m that guarantees the system is deadlock-free?
Show solution
Worst-case reasoning. Deadlock is worst when every process holds as much as it can without being able to finish — that is, each holds k − 1 instances and waits for one more.
In that state, the total held is n(k − 1). If even one extra instance exists, some process can grab it, reach k, finish, and release everything — breaking the chain.
Equivalently, deadlock is possible if m ≤ n(k − 1).
Worked variant: "3 processes, each needs at most 4 instances. What is the minimum m for deadlock-freedom?"
m ≥ 3(4 − 1) + 1 = 3(3) + 1 = 10.
Reverse variant: "There are 12 instances and each process needs at most 3. What is the maximum number of processes such that the system is guaranteed deadlock-free?"
12 ≥ n(3 − 1) + 1 → 12 ≥ 2n + 1 → n ≤ 5.5 → n = 5.
Formula ki intuition: har process ko "ek kam" de do — sab atak jaayenge. Ab agar ek bhi extra instance bacha, toh kisi ek ko poora mil jaayega, woh kaam khatam kar ke sab chhod dega, aur domino chal padega. Isliye +1. Ye formula GATE aur product-company OAs dono mein aata hai — rat lo.
Deadlock detection
Single instance per resource type — wait-for graph
Collapse the RAG: remove resource nodes and draw Pi → Pj if Pi is waiting for a resource held by Pj. A deadlock exists iff the wait-for graph contains a cycle. Cost: O(n²) to search for a cycle.
Multiple instances — the detection algorithm
Same shape as the safety algorithm, but it uses Request (what is actually being asked for right now) instead of Need (what could ever be asked for). Processes not requesting anything are marked finished immediately.
The safety algorithm is pessimistic: it assumes every process will eventually demand its full Need. The detection algorithm is factual: it only looks at the current Request. Same loop, different input matrix. That's why avoidance can call a state "unsafe" when no deadlock actually exists.
When to run detection?
- Every request that can't be granted — catches deadlock instantly, but very expensive.
- Periodically (e.g. every hour) or when CPU utilisation drops below a threshold (say 40%) — cheaper, but you can't tell which process "caused" the deadlock, since several cycles may have formed.
Recovery from deadlock
Process termination
- Abort all deadlocked processes — guaranteed to work, very expensive (all partial computation lost).
- Abort one at a time until the cycle breaks — less waste, but detection must re-run after each kill.
Selection criteria: process priority, how long it has computed and how much remains, resources held, resources still needed, how many processes must be terminated, and whether it is interactive or batch.
Resource preemption
- Selecting a victim — minimise cost.
- Rollback — the victim must return to some safe state and restart. Total rollback (abort and restart) is simplest; partial rollback needs checkpointing.
- Starvation — the same process may always be chosen as victim. Fix: include the number of rollbacks in the cost factor.
Deadlock vs Starvation vs Livelock COMMON
| Deadlock | Starvation | Livelock | |
|---|---|---|---|
| State | Blocked forever | Ready, but never scheduled/granted | Running, but making no progress |
| CPU used | None | None (by the starved process) | Yes — CPU is busy |
| Cause | Circular wait | Unfair policy / priority | Processes keep responding to each other and retrying |
| Can resolve itself? | Never | Possibly (with aging) | Possibly (with randomised backoff) |
Livelock ka best example: corridor mein do log aamne-saamne aa gaye. Ek left hata, doosra bhi left hata. Phir dono right hate. Phir dono left. Dono move kar rahe hain — par koi aage nahi badh raha. Deadlock mein dono khade hote hain; livelock mein dono naach rahe hote hain. Fix: random delay (jaise Ethernet ka exponential backoff).
Memory management
Address binding se lekar fragmentation tak — paging ki neev yahin padti hai.
Address binding — when does a name become an address? COMMON
| Bound at | What happens | Can the program move after loading? |
|---|---|---|
| Compile time | Compiler generates absolute code. If the start location changes, you must recompile. | No |
| Load time | Compiler generates relocatable code; the loader fixes addresses when loading. | No (must reload) |
| Execution time | Binding deferred to run time. Needs hardware support (MMU with relocation register). | Yes |
Aaj ke sab systems execution-time binding use karte hain, kyunki bina uske swapping, paging aur virtual memory — kuch bhi possible nahi. Isliye hardware (MMU) ki zaroorat padti hai. Ye ek line poore memory management ko justify karti hai.
Logical vs Physical address ASKED A LOT
| Logical (virtual) address | Physical address |
|---|---|
| Generated by the CPU | Seen by the memory unit |
| What the program thinks its address is | Actual location in RAM |
| Set of all = logical address space | Set of all = physical address space |
| Same in compile/load-time binding | Differ in execution-time binding |
The MMU (Memory Management Unit) is the hardware that translates one to the other at run time. In the simplest scheme, the relocation register's value is added to every logical address.
Ek line: "User program kabhi physical address nahi dekhta — woh sirf logical addresses banata hai, aur MMU beech mein baith kar translate karta hai." Ye protection ka bhi base hai: program physical address bol hi nahi sakta, toh kisi aur ki memory chhu bhi nahi sakta.
Contiguous allocation
Fixed (static) partitioning
Memory is divided into fixed-size partitions at boot. Each partition holds one process.
- Equal-size partitions: simple, but a process larger than a partition can't run (needs overlays), and a small process wastes the rest of its partition.
- Unequal-size partitions: place each process in the smallest partition that fits. Less waste, still wasteful.
- Maximum number of active processes is fixed by the number of partitions.
Dynamic partitioning
Partitions are created at load time, exactly the size the process needs. No internal waste — but as processes come and go, memory becomes riddled with small holes.
Fragmentation ASKED A LOT
| Internal fragmentation | External fragmentation | |
|---|---|---|
| Where the waste is | Inside an allocated block | Between allocated blocks |
| Cause | Allocated block is bigger than requested | Free memory exists but is not contiguous |
| Occurs in | Fixed partitioning, paging | Dynamic partitioning, segmentation |
| Fix | Smaller block sizes (but bigger page table) | Compaction, or paging |
Sabse easy analogy — suitcase:
Internal fragmentation = aapne 20kg ka suitcase liya par saamaan 15kg hai. 5kg jagah suitcase ke andar khaali. Waste toh hai par aapko allot ho chuka hai.
External fragmentation = almari mein total 10kg jagah hai, par woh 5 alag-alag jagah 2-2kg karke bikhri hai. Aapko ek 6kg ka box rakhna hai — rakh nahi sakte, chahe total jagah kaafi ho.
Paging mein external fragmentation kyun nahi hoti? Kyunki koi bhi free frame kisi bhi page ke liye use ho sakta hai — contiguity ki zaroorat hi nahi. Par aakhri page aadha khaali reh jaata hai → internal fragmentation. Paging ne external ko internal se replace kar diya, aur internal chhota aur predictable hai. Yahi paging ka core selling point hai.
Statistical analysis of first-fit shows that given N allocated blocks, another 0.5N blocks are lost to fragmentation — i.e. about one-third of memory may be unusable. This is the "50 percent rule".
Allocation strategies COMMON
| Strategy | Picks | Speed | Notes |
|---|---|---|---|
| First fit | First hole big enough | Fastest | Good storage utilisation. Fragments the front of memory |
| Best fit | Smallest hole that fits | Slow (searches whole list unless sorted) | Leaves the smallest leftover — but those slivers are useless. Often worst in practice despite the name |
| Worst fit | Largest hole | Slow | Leaves a large usable remainder. Worst utilisation overall |
| Next fit | First fit, but starts from where the last search ended | Fast | Spreads fragmentation evenly; may break up the large block at the end |
Free memory holes, in order: 100 KB, 500 KB, 200 KB, 300 KB, 600 KB. Processes request, in order: 212 KB, 417 KB, 112 KB, 426 KB. Show the placement for First fit, Best fit and Worst fit. Which succeeds?
Show solution
First fit — scan from the start, take the first hole that fits.
| Request | Placed in | Holes after |
|---|---|---|
| 212 | 500 → leaves 288 | 100, 288, 200, 300, 600 |
| 417 | 600 → leaves 183 | 100, 288, 200, 300, 183 |
| 112 | 288 → leaves 176 | 100, 176, 200, 300, 183 |
| 426 | no hole ≥ 426 | must wait |
Best fit — take the smallest hole that fits.
| Request | Placed in | Holes after |
|---|---|---|
| 212 | 300 (smallest ≥ 212) → leaves 88 | 100, 500, 200, 88, 600 |
| 417 | 500 → leaves 83 | 100, 83, 200, 88, 600 |
| 112 | 200 → leaves 88 | 100, 83, 88, 88, 600 |
| 426 | 600 → leaves 174 | 100, 83, 88, 88, 174 |
Worst fit — take the largest hole.
| Request | Placed in | Holes after |
|---|---|---|
| 212 | 600 → leaves 388 | 100, 500, 200, 300, 388 |
| 417 | 500 → leaves 83 | 100, 83, 200, 300, 388 |
| 112 | 388 → leaves 276 | 100, 83, 200, 300, 276 |
| 426 | no hole ≥ 426 | must wait |
Only Best fit satisfies all four requests. First fit and Worst fit both fail on the last one. This is the standard textbook example — expect it verbatim.
Compaction, overlays, swapping
- Compaction — shuffle allocated blocks to one end so all free memory is contiguous. Only possible with execution-time binding (otherwise addresses break). Expensive: it's a big memory-to-memory copy.
- Overlays — keep only the instructions and data currently needed in memory; the programmer manually splits the program into overlay sections that reuse the same memory. Used before virtual memory existed. Programmer's burden — that's the whole problem with it.
- Swapping — move an entire process out to a backing store (disk) and back. The major cost is transfer time, roughly proportional to the amount of memory swapped.
Overlays vs virtual memory: dono ka goal same hai — program RAM se bada ho sakta hai. Farq ye hai ki overlays mein programmer decide karta hai kya andar-bahar hoga, virtual memory mein OS decide karta hai, automatically. Isliye overlays mar gaye. Ye ek clean answer hai agar poochha jaaye "virtual memory se pehle kya karte the?"
Buddy system RARE
Memory is managed in blocks of size 2k. A request of size s is satisfied by the smallest power-of-two block ≥ s; larger blocks are repeatedly split in half ("buddies") until the right size is reached. On free, if a block's buddy is also free, they merge back.
Used by the Linux kernel for physical page allocation. Fast merging, but internal fragmentation can reach nearly 50%.
Paging & segmentation
Numericals ka sabse bada source. Address split aur page table size wale questions pakke hain.
Paging — the core idea ASKED A LOT
- Physical memory is divided into fixed-size blocks called frames.
- Logical memory is divided into blocks of the same size called pages.
- The page table maps page number → frame number. One page table per process.
- A process's pages can sit in any frames, non-contiguously.
Logical address space = 2m → logical address is m bits → page number = (m − d) bits
Number of pages = logical address space / page size
Number of frames = physical memory size / frame size
Page table size = number of pages × page table entry size
Offset kabhi nahi badalta — translation ke baad bhi wahi rehta hai. Sirf page number → frame number badalta hai. Isliye page size aur frame size hamesha barabar hote hain. Ye samajh gaye toh saare numericals aasaan.
A system uses 32-bit logical addresses, page size 4 KB, and each page table entry is 4 bytes. Physical memory is 1 GB.
(a) How many bits for offset and page number?
(b) How many pages and frames?
(c) What is the size of the page table per process?
Show solution
(a) Page size = 4 KB = 212 bytes → offset = 12 bits.
Logical address = 32 bits → page number = 32 − 12 = 20 bits.
(b) Number of pages = 220 = 1,048,576 pages (≈ 1 M).
Physical memory = 1 GB = 230 bytes; frame size = 212 → number of frames = 230/212 = 218 = 262,144 frames.
(c) Page table size = 220 entries × 4 bytes = 222 bytes = 4 MB per process.
4 MB per process, and that page table must itself be in memory, contiguously. With 100 processes that's 400 MB of page tables alone. This is exactly why multilevel paging exists. Whenever an interviewer asks this, the follow-up is always "so what do we do about it?"
Frame number ke bits nikalne ka short cut: physical address bits = log₂(physical memory). Yahan 2³⁰ → 30 bits. Usme se 12 offset ke, toh frame number = 18 bits. Note karo: page number 20 bits ka hai par frame number sirf 18 ka — kyunki logical space physical se bada hai. Ye bilkul normal hai, yahi toh virtual memory ka point hai.
TLB (Translation Lookaside Buffer) ASKED A LOT
Without a TLB, every memory reference needs two memory accesses: one to read the page table, one to read the actual data. That halves performance. The TLB is a small, fast associative cache (typically 64–1024 entries) holding recent page→frame translations.
Convention A (your course slides): TLB search time is added to memory access.
h = hit ratio, t = TLB search time, m = memory access time
Convention B: TLB lookup happens in parallel / is considered negligible, giving EAT = h × m + (1 − h) × 2m. If the question gives you a TLB access time, use Convention A.
TLB search takes 20 ns, memory access takes 100 ns. Compute EAT for hit ratios of (a) 80% and (b) 98%.
Show solution
On a TLB hit: 20 (TLB) + 100 (memory for data) = 120 ns.
On a TLB miss: 20 (TLB, wasted) + 100 (memory for page table) + 100 (memory for data) = 220 ns.
(a) h = 0.80
(b) h = 0.98
Interpretation. Raw memory access is 100 ns. At 80% hit ratio we pay 140 ns — a 40% slowdown. At 98% we pay 122 ns — only 22%. Pushing the hit ratio from 80% to 98% recovers almost half the overhead. This is why real TLBs are aggressively optimised and why hit ratios of 99%+ are typical.
Galti jo sab karte hain: miss ke case mein log 100 + 100 = 200 likh dete hain aur TLB ka 20 ns bhool jaate hain. TLB search toh hui thi na — bas result nahi mila. Time toh laga. Isliye 220, 200 nahi. Agar question mein TLB time diya hai, toh dono cases mein add hoga.
On a context switch the TLB holds the old process's translations, which are wrong for the new one. Two options: flush the entire TLB (simple, but the new process starts with 100% misses), or tag each entry with an ASID (Address Space Identifier) so entries from multiple processes can coexist. ASIDs are why modern context switches don't destroy TLB performance — a good detail to mention.
Multilevel paging ASKED A LOT
Instead of one giant page table, page the page table itself.
The saving is not from the total table being smaller — a fully populated two-level table is slightly bigger. The saving is that inner tables for unused regions are never allocated. A real process uses a tiny fraction of its 4 GB address space (code at the bottom, stack at the top, a big hole in between), so almost all inner tables can be omitted. Only the outer table (4 KB) must always be resident.
Same system as Q12.1 (32-bit addresses, 4 KB pages, 4-byte PTEs). Design a two-level paging scheme and compare memory used when a process only touches 2 pages of code and 2 pages of stack.
Show solution
Design. Offset = 12 bits, leaving 20 bits for the page number. Split it 10 / 10.
Each table then has 210 = 1024 entries × 4 bytes = 4 KB — exactly one page. That's the whole reason for choosing a 10/10 split: every page table fits in exactly one frame.
Single-level: 4 MB, always, entirely resident.
Two-level:
- Outer page table: 4 KB (always needed)
- Code at the low end → 1 inner table: 4 KB
- Stack at the high end → 1 inner table: 4 KB
Saving ≈ 99.7%
The cost: address translation now needs two page-table lookups instead of one, so a TLB miss costs 3 memory accesses (outer table + inner table + data) rather than 2.
EAT = h × (t + m) + (1 − h) × (t + (n+1)m)
Trade-off ek line mein: "Multilevel paging memory bachata hai par time kharcha karta hai. Aur us time ko TLB wapas bacha leta hai — isliye dono saath chalte hain." 64-bit systems mein 4 ya 5 levels hote hain, isliye TLB waha aur bhi zyada critical hai.
Inverted & hashed page tables RARE
| Normal page table | Inverted page table | |
|---|---|---|
| One table per | Process | Whole system |
| Number of entries | Number of pages | Number of frames |
| Entry contains | Frame number | <PID, page number> |
| Lookup | Direct index — O(1) | Search the table for a matching <PID, page> — slow |
| Memory used | Grows with number of processes | Fixed, independent of process count |
| Shared memory | Easy | Hard — one physical frame can't map to two virtual pages in one entry |
Hashed page tables handle large address spaces (>32-bit): hash the virtual page number, then walk a collision chain of ⟨virtual page, frame, next⟩ elements.
Inverted page table ko ulta socho: normal table poochhta hai "page 5 kis frame mein hai?" — direct answer. Inverted table poochhna padta hai "kya koi frame hai jisme process 3 ka page 5 pada ho?" — poori table search karni padegi. Memory bacha li, par time gaya. Isliye hash table ka use hota hai search fast karne ke liye.
Segmentation COMMON
Memory is viewed as a collection of variable-sized logical units that match the programmer's view: main program, functions, stack, symbol table, arrays. A logical address is ⟨segment number s, offset d⟩.
The segment table holds a base (starting physical address) and a limit (length) per segment. Translation: if d < limit[s] then physical = base[s] + d, else trap: addressing error.
Paging vs Segmentation ASKED A LOT
| Paging | Segmentation | |
|---|---|---|
| Block size | Fixed | Variable |
| Divided by | Hardware / OS | The programmer / compiler |
| Address form | One number, split by hardware | Two explicit parts ⟨s, d⟩ |
| Fragmentation | Internal | External |
| Table entry | Frame number | Base + limit |
| Reflects program structure? | No — a function can straddle two pages | Yes — one segment = one logical unit |
| Protection/sharing | Per page — arbitrary boundaries | Natural — mark a whole code segment read-only, share a library segment |
Ek line mein: "Paging physical memory management ke liye hai — hardware ki soch. Segmentation logical organisation ke liye hai — programmer ki soch."
Segmentation ka asli fayda protection mein hai. Ek poora "code segment" hai — usko read-only mark kar do, bas. Paging mein aapko har page ko alag se mark karna padega, aur ek page mein aadha code aur aadha data ho sakta hai.
Isliye modern systems dono use karte hain — segmented paging. Pehle segment nikaalo, phir us segment ko paged rakho. x86 exactly yahi karta hai. Best of both: external fragmentation gayi (paging se), logical structure bachi (segmentation se).
Given this segment table, compute the physical address (or report an error) for logical addresses (0, 430), (1, 10), (2, 500), (3, 400), (4, 112).
| Segment | Base | Limit |
|---|---|---|
| 0 | 219 | 600 |
| 1 | 2300 | 14 |
| 2 | 90 | 100 |
| 3 | 1327 | 580 |
| 4 | 1952 | 96 |
Show solution
Rule: valid iff offset < limit; then physical = base + offset.
| Logical | Check | Result |
|---|---|---|
| (0, 430) | 430 < 600 ✓ | 219 + 430 = 649 |
| (1, 10) | 10 < 14 ✓ | 2300 + 10 = 2310 |
| (2, 500) | 500 < 100 ✗ | TRAP — addressing error |
| (3, 400) | 400 < 580 ✓ | 1327 + 400 = 1727 |
| (4, 112) | 112 < 96 ✗ | TRAP — addressing error |
The check is strictly less than. If limit = 600, valid offsets are 0 to 599. An offset of exactly 600 is an error. Examiners plant this deliberately.
x86 segmentation — your syllabus extra RARE
The 80x86 uses segmented paging. A logical address (selector : offset) becomes a linear address via the segment unit, then a physical address via the paging unit.
- GDT (Global Descriptor Table) — system-wide segments. Pointed to by GDTR.
- LDT (Local Descriptor Table) — per-process segments. Pointed to by LDTR.
- IDT (Interrupt Descriptor Table) — interrupt/exception handlers. Pointed to by IDTR.
- A selector is 16 bits: 13-bit index + 1-bit table indicator (GDT/LDT) + 2-bit RPL.
Privilege levels (rings 0–3):
| Field | Meaning |
|---|---|
| CPL — Current Privilege Level | Privilege of the currently executing code (low 2 bits of CS) |
| DPL — Descriptor Privilege Level | Privilege required to access the segment |
| RPL — Requested Privilege Level | Privilege being requested by the selector — lets the kernel voluntarily weaken itself |
Access rule for data segments: access is allowed only if max(CPL, RPL) ≤ DPL (numerically lower = more privileged).
RPL kyun exist karta hai? Socho: user program kernel se kehta hai "ye address padh do". Kernel ring 0 mein hai, toh woh kuch bhi padh sakta hai — including apni hi secret memory. Agar user ne chalaki se kernel ka address de diya toh? RPL se kernel khud ko jaan-boojh kar "downgrade" kar leta hai: "main ye access user ki taraf se kar raha hoon, toh user ki permission lagao." Ye confused deputy problem ka hardware-level solution hai.
Virtual memory
Demand paging, page replacement numericals, thrashing. Ye section interview mein sabse zyada "depth" test karta hai.
Virtual memory is the separation of the user's logical memory from physical memory. It allows a process to execute even when it is only partially in memory, so a program can be larger than physical memory.
What it buys you: programs no longer constrained by RAM size; more processes fit in memory simultaneously (higher multiprogramming, better CPU utilisation); less I/O to load or swap programs, so each user program runs faster.
Demand paging
Bring a page into memory only when it is needed (a "lazy swapper"). Each page-table entry carries a valid–invalid bit:
- v (valid) — the page is legal and currently in memory.
- i (invalid) — either the page is not in the process's logical address space at all, or it is legal but currently on disk.
Accessing an i page traps to the OS → page fault.
Steps in handling a page fault ASKED A LOT
- Reference to the page → trap to the OS.
- OS checks an internal table: is the reference invalid (illegal) or just not in memory? If illegal → terminate the process.
- Find a free frame. If none is free, run the page-replacement algorithm to pick a victim and evict it (writing it back to disk if it is dirty).
- Schedule a disk read to bring the desired page into that frame.
- Disk read completes → update the page table: set the frame number and the valid bit.
- Restart the instruction that was interrupted.
Step 6 hi jaadu hai. Instruction dobara chalti hai — aur ab woh successfully chalti hai. Program ko bilkul pata nahi chalta ki beech mein 8 millisecond ka disk read hua. Yahi "illusion of infinite memory" hai. Aur yahi reason hai ki CPU designers ko instruction restartable banani padti hai.
EAT with page faults ASKED A LOT
EAT = (1 − p) × memory_access_time + p × page_fault_service_time
page_fault_service_time ≈ trap overhead + swap page out (if dirty) + swap page in + restart overhead
Memory access time = 200 ns. Average page-fault service time = 8 ms.
(a) Compute EAT if the page-fault rate is 1 in 1000.
(b) What page-fault rate keeps the slowdown below 10%?
Show solution
First, put both in the same unit: 8 ms = 8,000,000 ns.
(a) p = 0.001
= 199.8 + 8000
= 8199.8 ns ≈ 8.2 µs
That is a 40× slowdown from 200 ns. One fault in a thousand accesses — which sounds rare — makes the machine 40 times slower.
(b) "Less than 10% degradation" means EAT < 220 ns.
220 > 200 − 200p + 8,000,000p
20 > 7,999,800p
p < 20 / 7,999,800 = 0.0000025
That is fewer than 1 fault per 400,000 accesses.
The gap between memory (nanoseconds) and disk (milliseconds) is a factor of ~40,000. So the page-fault rate must be astonishingly low for demand paging to be usable at all. Everything in this chapter — LRU, working sets, thrashing control — exists to keep p that small. If you can state this, you've understood the chapter.
Unit conversion pe hamesha dhyan do — ms aur ns mila dena is topic ki sabse common galti hai. 1 ms = 10⁶ ns. Pehle sab kuch ns mein likho, phir formula lagao.
Page replacement algorithms ASKED A LOT
When no frame is free, pick a victim. Prefer a clean (unmodified) page — no write-back needed, so half the I/O. That is what the dirty bit / modify bit is for.
1. FIFO
Evict the page that has been in memory longest. Implemented as a simple queue. Cheap, but it may evict a heavily used page just because it arrived early.
Reference string: 7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1, with 3 frames. Count page faults for FIFO, Optimal and LRU.
Show solution
FIFO — evict the oldest arrival.
| Ref | F1 | F2 | F3 | Fault? | Evicted |
|---|---|---|---|---|---|
| 7 | 7 | – | – | ✗ F | — |
| 0 | 7 | 0 | – | ✗ F | — |
| 1 | 7 | 0 | 1 | ✗ F | — |
| 2 | 2 | 0 | 1 | ✗ F | 7 |
| 0 | 2 | 0 | 1 | ✓ hit | — |
| 3 | 2 | 3 | 1 | ✗ F | 0 |
| 0 | 2 | 3 | 0 | ✗ F | 1 |
| 4 | 4 | 3 | 0 | ✗ F | 2 |
| 2 | 4 | 2 | 0 | ✗ F | 3 |
| 3 | 4 | 2 | 3 | ✗ F | 0 |
| 0 | 0 | 2 | 3 | ✗ F | 4 |
| 3 | 0 | 2 | 3 | ✓ hit | — |
| 2 | 0 | 2 | 3 | ✓ hit | — |
| 1 | 0 | 1 | 3 | ✗ F | 2 |
| 2 | 0 | 1 | 2 | ✗ F | 3 |
| 0 | 0 | 1 | 2 | ✓ hit | — |
| 1 | 0 | 1 | 2 | ✓ hit | — |
| 7 | 7 | 1 | 2 | ✗ F | 0 |
| 0 | 7 | 0 | 2 | ✗ F | 1 |
| 1 | 7 | 0 | 1 | ✗ F | 2 |
FIFO = 15 page faults.
Optimal (OPT / MIN) — evict the page whose next use is farthest in the future. Positions in the string are numbered 1–20.
| Pos | Ref | Frames after | Fault? | Next-use of each resident page → evict |
|---|---|---|---|---|
| 1 | 7 | 7 | F | — |
| 2 | 0 | 7 0 | F | — |
| 3 | 1 | 7 0 1 | F | — |
| 4 | 2 | 2 0 1 | F | 7→18, 0→5, 1→14 · evict 7 |
| 5 | 0 | 2 0 1 | hit | — |
| 6 | 3 | 2 0 3 | F | 2→9, 0→7, 1→14 · evict 1 |
| 7 | 0 | 2 0 3 | hit | — |
| 8 | 4 | 2 4 3 | F | 2→9, 0→11, 3→10 · evict 0 |
| 9 | 2 | 2 4 3 | hit | — |
| 10 | 3 | 2 4 3 | hit | — |
| 11 | 0 | 2 0 3 | F | 2→13, 4→never, 3→12 · evict 4 |
| 12 | 3 | 2 0 3 | hit | — |
| 13 | 2 | 2 0 3 | hit | — |
| 14 | 1 | 2 0 1 | F | 2→15, 0→16, 3→never · evict 3 |
| 15 | 2 | 2 0 1 | hit | — |
| 16 | 0 | 2 0 1 | hit | — |
| 17 | 1 | 2 0 1 | hit | — |
| 18 | 7 | 7 0 1 | F | 2→never, 0→19, 1→20 · evict 2 |
| 19 | 0 | 7 0 1 | hit | — |
| 20 | 1 | 7 0 1 | hit | — |
Optimal = 9 page faults.
At every fault, write down the next occurrence index of each page currently in a frame, then evict the largest. A page that never appears again always wins — evict it immediately. Don't do this in your head; the index column above is the whole method.
LRU — evict the page with the oldest last-used time.
| Pos | Ref | Frames after | Fault? | Last-used times → evict |
|---|---|---|---|---|
| 1–3 | 7 0 1 | 7 0 1 | F F F | filling empty frames |
| 4 | 2 | 2 0 1 | F | 7@1, 0@2, 1@3 · evict 7 |
| 5 | 0 | 2 0 1 | hit | — |
| 6 | 3 | 2 0 3 | F | 2@4, 0@5, 1@3 · evict 1 |
| 7 | 0 | 2 0 3 | hit | — |
| 8 | 4 | 4 0 3 | F | 2@4, 0@7, 3@6 · evict 2 |
| 9 | 2 | 4 0 2 | F | 4@8, 0@7, 3@6 · evict 3 |
| 10 | 3 | 4 3 2 | F | 4@8, 0@7, 2@9 · evict 0 |
| 11 | 0 | 0 3 2 | F | 4@8, 3@10, 2@9 · evict 4 |
| 12 | 3 | 0 3 2 | hit | — |
| 13 | 2 | 0 3 2 | hit | — |
| 14 | 1 | 1 3 2 | F | 0@11, 3@12, 2@13 · evict 0 |
| 15 | 2 | 1 3 2 | hit | — |
| 16 | 0 | 1 0 2 | F | 1@14, 3@12, 2@15 · evict 3 |
| 17 | 1 | 1 0 2 | hit | — |
| 18 | 7 | 1 0 7 | F | 1@17, 0@16, 2@15 · evict 2 |
| 19 | 0 | 1 0 7 | hit | — |
| 20 | 1 | 1 0 7 | hit | — |
LRU = 12 page faults.
| Algorithm | Page faults |
|---|---|
| FIFO | 15 |
| LRU | 12 |
| Optimal | 9 |
Ye order hamesha yahi rehta hai: OPT ≤ LRU ≤ FIFO (usually). OPT implement nahi ho sakta — future kaun jaanta hai? Woh sirf benchmark hai: "meri algorithm OPT se kitni door hai?" LRU OPT ka best practical approximation hai, kyunki past locality future ka accha predictor hai.
2. Optimal (OPT / Belady's algorithm)
Replace the page that will not be used for the longest period of time. Gives the lowest possible fault rate for a fixed number of frames. Unimplementable — requires future knowledge. Used only for comparison.
3. LRU
Approximates OPT by looking backwards: the page unused for the longest time is assumed to be the one that will stay unused longest.
Two implementations:
- Counters — each PTE holds a "time of last use" clock value; on replacement, scan for the smallest. Requires a search plus a memory write on every memory access.
- Stack — keep page numbers in a doubly-linked list; on reference, move that page to the top. The bottom is always the LRU victim. No search needed, but ~6 pointer updates per access.
Both implementations need work on every single memory reference, which means hardware support that no commodity CPU provides. So real operating systems use approximations — the clock algorithm below. Saying this converts a textbook answer into an engineering answer.
4. Second-chance / Clock algorithm COMMON
Each page has a reference bit, set by hardware on any access. Pages are arranged in a circular queue with a pointer ("clock hand").
5. Enhanced second-chance
Use the pair (reference bit, modify bit) and prefer classes in this order:
| Class | (r, m) | Meaning | Priority to evict |
|---|---|---|---|
| 1 | (0, 0) | Not recently used, not modified | Best — evict first, no write-back |
| 2 | (0, 1) | Not recently used, but modified | Second — must write to disk |
| 3 | (1, 0) | Recently used, clean | Third — likely to be used again |
| 4 | (1, 1) | Recently used and modified | Worst — avoid |
6. Counting algorithms
- LFU — evict the page with the smallest reference count. Problem: a page heavily used during initialisation keeps a huge count forever. Fix: shift counts right periodically (exponential decay).
- MFU — evict the page with the largest count, arguing the smallest count means it was just brought in and hasn't had a chance yet. Neither is common in practice.
Belady's anomaly ASKED A LOT
For some page-replacement algorithms, increasing the number of frames can increase the number of page faults. This is counter-intuitive and specific to certain algorithms — FIFO suffers from it; LRU and OPT do not.
Reference string 1 2 3 4 1 2 5 1 2 3 4 5. Show that FIFO gives more faults with 4 frames than with 3.
Show solution
With 3 frames:
| Ref | 1 | 2 | 3 | 4 | 1 | 2 | 5 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| F1 | 1 | 1 | 1 | 4 | 4 | 4 | 5 | 5 | 5 | 5 | 5 | 5 |
| F2 | – | 2 | 2 | 2 | 1 | 1 | 1 | 1 | 1 | 3 | 3 | 3 |
| F3 | – | – | 3 | 3 | 3 | 2 | 2 | 2 | 2 | 2 | 4 | 4 |
| Fault | F | F | F | F | F | F | F | hit | hit | F | F | hit |
3 frames → 9 page faults.
With 4 frames:
| Ref | 1 | 2 | 3 | 4 | 1 | 2 | 5 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| F1 | 1 | 1 | 1 | 1 | 1 | 1 | 5 | 5 | 5 | 5 | 4 | 4 |
| F2 | – | 2 | 2 | 2 | 2 | 2 | 2 | 1 | 1 | 1 | 1 | 5 |
| F3 | – | – | 3 | 3 | 3 | 3 | 3 | 3 | 2 | 2 | 2 | 2 |
| F4 | – | – | – | 4 | 4 | 4 | 4 | 4 | 4 | 3 | 3 | 3 |
| Fault | F | F | F | F | hit | hit | F | F | F | F | F | F |
4 frames → 10 page faults.
9 faults with 3 frames, 10 faults with 4. More memory, worse performance. That's Belady's anomaly.
Why LRU and OPT are immune: they are stack algorithms — the set of pages in memory with n frames is always a subset of the set with n+1 frames. That subset property makes it impossible for a page present with n frames to be absent with n+1. FIFO does not have this property, because eviction order depends only on arrival time, which changes completely when frame count changes.
Interview answer ka structure: (1) define karo (2) bolo FIFO mein hoti hai, LRU/OPT mein nahi (3) reason bolo — stack property. Teesra point hi differentiate karta hai. Aur agar time ho toh ye exact reference string yaad rakho — 1 2 3 4 1 2 5 1 2 3 4 5 — ye standard hai.
Frame allocation
- Minimum number of frames per process is set by the instruction set architecture — an instruction must be able to complete. If an instruction can reference two operands, each of which may straddle a page boundary, you need enough frames or the instruction can never finish (infinite fault loop).
- Equal allocation — m frames, n processes → m/n each.
- Proportional allocation — allocate in proportion to process size:
a_i = (s_i / Σs_j) × m. - Priority allocation — proportional to priority, or a combination of size and priority.
| Global replacement | Local replacement | |
|---|---|---|
| Victim chosen from | All frames in the system | Only that process's own frames |
| Process performance | Unpredictable — depends on other processes' behaviour | Consistent between runs |
| Throughput | Generally better — commonly used | Lower — a process can't grow even when free frames exist elsewhere |
| Thrashing risk | Can spread from one process to all | Contained to one process |
Thrashing ASKED A LOT
A process is thrashing when it spends more time paging than executing. It happens when a process does not have enough frames to hold its active set of pages, so it faults on almost every reference.
The vicious cycle — say this exactly
Thrashing ka asli irony: OS ko lagta hai "CPU khaali hai, aur kaam do" — aur woh aur processes daal deta hai, jo problem ko aur bigaad deta hai. OS apni hi galti ko baar-baar dohra raha hai. Ye "positive feedback loop gone wrong" hai.
Ghar ka example: ek chhoti table pe aap 5 kitaabein le kar padh rahe ho. Table pe sirf 2 aa sakti hain. Har baar teesri chahiye toh ek hatani padti hai aur almari se laani padti hai. Aap padhne se zyada time kitaabein utha-rakh ne mein laga rahe ho. Yahi thrashing hai.
Solution ek line mein: "Degree of multiprogramming kam karo" — kuch processes suspend kar do. Counter-intuitive lagta hai (kam kaam le kar zyada kaam?) par yahi sahi hai.
Locality model
A locality is a set of pages actively used together. Processes migrate from one locality to another (e.g. entering a function pulls in its code, locals, and the globals it touches). Thrashing occurs when the sum of all localities exceeds total memory size.
Working-set model COMMON
WSSi = working set size = |WSi(Δ)|
D = Σ WSSi = total demand for frames
If D > m (total frames) → thrashing. Policy: suspend a process.
Choosing Δ matters: too small → misses the true locality; too large → spans several localities; infinite → the entire program.
Reference string: … 2 6 1 5 7 7 7 7 5 1 6 2 3 4 1 2 3 4 4 4 3 4 3 4 4 4 1 3 2 3 4 4 4 3 4 4 4 … With Δ = 10, find the working set at t1 (after the first 10 references shown) and at t2 (after the next 10).
Show solution
At t1 — the window covers 2 6 1 5 7 7 7 7 5 1. Distinct pages: {1, 2, 5, 6, 7} → WSS = 5.
At t2 — the window covers 3 4 4 4 3 4 3 4 4 4. Distinct pages: {3, 4} → WSS = 2.
Interpretation. The process has moved from one locality (pages 1,2,5,6,7) to a much tighter one (pages 3,4). Between t1 and t2 it needed 5 frames; now it needs only 2. The OS can reclaim 3 frames and give them to another process.
If the OS had allocated a fixed number of frames instead, it would either be wasting frames now or have been thrashing earlier. That adaptivity is the entire value of the working-set model.
Page-fault frequency (PFF) scheme
A more direct approach: set an acceptable upper and lower bound on the page-fault rate per process.
- Actual rate above the upper bound → the process needs more frames → allocate one.
- Actual rate below the lower bound → it has too many → take one away.
- If no free frames exist and someone still needs more → suspend a process and free all its frames.
Working set vs PFF: working set cause ko measure karta hai (kitne pages actually chahiye), PFF symptom ko measure karta hai (kitne faults ho rahe hain). PFF implement karna aasaan hai — bas faults gino. Isliye practical systems PFF-style feedback zyada use karte hain.
Copy-on-write & memory-mapped files
- Copy-on-write (COW) — after
fork(), parent and child share physical pages marked read-only. On the first write, the OS traps, copies just that page, and lets the write proceed. Makesfork()nearly free. - Memory-mapped files — map a file's blocks into the address space so file I/O becomes ordinary memory access. Multiple processes mapping the same file share the pages, giving shared memory almost for free.
- Prepaging — bring in several pages at once to avoid a burst of faults at startup. Wasteful if the guessed pages aren't used.
Page size — the trade-off COMMON
| Aspect | Smaller page | Larger page |
|---|---|---|
| Internal fragmentation | Less (avg = page_size/2 per process) | More |
| Page table size | Bigger (more pages) | Smaller |
| I/O efficiency | Worse — seek dominates per transfer | Better — amortises seek time |
| Locality / resolution | Better — only what's needed is brought in | Worse — brings in unused data |
| TLB coverage | Less memory covered per entry | More — fewer TLB misses |
The historical trend is toward larger pages, because memory and table sizes grew and TLB reach became the bottleneck. Modern systems also support "huge pages" (2 MB, 1 GB) alongside 4 KB pages.
Disks & disk scheduling
Head movement wale numericals guaranteed hain. Access time ka formula bhi.
Disk geometry
Disk access time ASKED A LOT
Seek time — move the arm to the right cylinder. Largest and most variable component.
Rotational latency — wait for the sector to spin under the head.
Average rotational latency = ½ × (60 / RPM) seconds = time for half a rotation
Transfer time = (bytes to transfer) / (transfer rate)
or, per track: (sectors read / sectors per track) × time for one rotation
A disk rotates at 7200 RPM, has an average seek time of 5 ms, 500 sectors per track, and sector size 512 bytes.
(a) Average rotational latency?
(b) Time to read one sector?
(c) Time to read a full track (500 sectors) sequentially?
(d) Time to read 500 sectors that are scattered randomly across the disk?
Show solution
(a) Rotational latency. 7200 RPM = 7200/60 = 120 rotations per second → one rotation = 1/120 s = 8.33 ms.
(b) One sector. Transfer time for one sector = (1/500) × 8.33 ms = 0.0167 ms.
(c) Full track sequentially. One seek, one rotational latency, then read the whole track — which takes exactly one full rotation.
(d) 500 random sectors. Every sector needs its own seek and its own rotational latency.
Same 256 KB of data: 17.5 ms sequential vs 4595 ms random — a 263× difference. This single number justifies disk scheduling, contiguous file allocation, defragmentation, database B-tree design, and log-structured file systems. If an interviewer asks "why does sequential I/O matter?", this calculation is the answer.
Yaad rakhne wali baat: seek time aur rotational latency data size pe depend nahi karte — chahe 1 byte padho ya poori track, seek toh utna hi lagega. Isliye bade chunks mein padhna hamesha sasta hai. Ye "amortise the seek" ka concept hai.
Disk scheduling algorithms ASKED A LOT
Goal: minimise total head movement (which minimises seek time). We'll run all six on the same input.
Request queue: 98, 183, 37, 122, 14, 124, 65, 67. Head currently at 53. Disk has cylinders 0–199. Previous head direction: moving toward 0 where relevant.
For the setup above, compute the total head movement for FCFS, SSTF, SCAN, C-SCAN, LOOK and C-LOOK.
Show solution
1. FCFS — serve in arrival order
| Move | Distance |
|---|---|
| 53 → 98 | 45 |
| 98 → 183 | 85 |
| 183 → 37 | 146 |
| 37 → 122 | 85 |
| 122 → 14 | 108 |
| 14 → 124 | 110 |
| 124 → 65 | 59 |
| 65 → 67 | 2 |
| Total | 640 cylinders |
Fair, simple, but the head swings wildly (183 → 37 → 122 → 14). No optimisation at all.
2. SSTF — Shortest Seek Time First
Always go to the closest pending request.
| Move | Distance | Why |
|---|---|---|
| 53 → 65 | 12 | closest to 53 (65 is 12 away, 37 is 16) |
| 65 → 67 | 2 | — |
| 67 → 37 | 30 | 37 is 30 away, 98 is 31 away |
| 37 → 14 | 23 | — |
| 14 → 98 | 84 | only requests left are above |
| 98 → 122 | 24 | — |
| 122 → 124 | 2 | — |
| 124 → 183 | 59 | — |
| Total | 236 cylinders | — |
Starvation. A request far from the head can wait forever if new nearby requests keep arriving. Also SSTF is not optimal — moving to 37 first, then 14, then sweeping up would give 208.
3. SCAN (elevator)
Head moves in one direction serving everything, goes all the way to the end of the disk, reverses, and serves on the way back.
Up: 0 → 183 = 183
Total = 53 + 183 = 236 cylinders
Note the head goes to cylinder 0 even though no request is there — that's what distinguishes SCAN from LOOK.
4. C-SCAN — Circular SCAN
Serves in one direction only. On reaching the end, it jumps back to the other end without servicing anything on the return trip.
Jump: 199 → 0 = 199
Up again: 0 → 37 = 37
Total = 146 + 199 + 37 = 382 cylinders
Why bother, if the number is worse? Because C-SCAN gives a more uniform wait time. In plain SCAN, a request just behind the head gets served twice as fast as one just in front — cylinders near the middle get served twice per sweep, edges once. C-SCAN treats the disk as circular, so every cylinder is visited at the same frequency.
5. LOOK
Like SCAN, but the head only goes as far as the last request in each direction — it doesn't run to the physical end.
Up: 14 → 183 = 169
Total = 39 + 169 = 208 cylinders
6. C-LOOK
C-SCAN without going to the physical ends.
Jump: 183 → 14 = 169
Up: 14 → 37 = 23
Total = 130 + 169 + 23 = 322 cylinders
| Algorithm | Total movement | Starvation? | Note |
|---|---|---|---|
| FCFS | 640 | No | Worst movement, perfectly fair |
| SSTF | 236 | Yes | Greedy, not optimal |
| SCAN | 236 | No | Goes to cylinder 0 unnecessarily |
| C-SCAN | 382 | No | Most uniform wait times |
| LOOK | 208 | No | Best here — SCAN without wasted travel |
| C-LOOK | 322 | No | C-SCAN without wasted travel |
Naam yaad karne ka logic — SCAN vs LOOK: "SCAN" poori disk scan karta hai — end tak jaata hai chahe wahan request ho ya na ho. "LOOK" pehle dekhta hai ki aage koi request hai kya — agar nahi, toh wahin se ghoom jaata hai. Isliye LOOK hamesha SCAN se kam ya barabar movement karta hai.
"C" ka matlab Circular — wapas aate waqt kuch serve nahi karta, seedha doosre end pe jump. Fayda: fairness, nuksaan: zyada movement.
Exam mein sabse badi galti: SCAN/C-SCAN mein disk ke end (0 aur 199) tak jaana bhool jaana, ya LOOK mein galti se end tak chale jaana. Aur direction ka assumption — agar question mein nahi bola gaya toh likh kar bolo "assuming head moves toward higher cylinders first". Examiner dono accept karta hai agar aapne assumption state ki ho.
On an SSD. There is no arm and no rotation, so seek time is essentially zero and there is no "closest block". SSD schedulers optimise for something completely different — write amplification, wear levelling, and merging writes into erase-block-sized chunks. Mentioning this shows you know the algorithms are tied to a specific physical mechanism.
RAID
Redundant Array of Independent Disks. Levels 0–6 ka comparison table rat lo — ye directly poochha jaata hai.
The two motivations
- Performance — striping data across disks lets several disks work in parallel.
- Reliability — redundancy (mirroring or parity) so a disk failure doesn't lose data.
MTTFarray = MTTFsingle disk / N
100 disks each with MTTF 100,000 hours → array MTTF = 1000 hours ≈ 42 days
Ye number chaunkane wala hai aur yahi RAID ka reason hai. Ek disk 11 saal chalti hai, par 100 disks ka array average 42 din mein fail hoga — kyunki koi ek fail ho jaaye toh kaafi hai. Jitni zyada disks, utni jaldi failure. Isliye redundancy optional nahi, zaroori hai.
The levels
RAID 0 — striping, no redundancy
Best performance, zero fault tolerance. Any one disk failing loses everything. Min disks: 2. Usable capacity: 100%.
RAID 1 — mirroring
Every block written twice. Reads are fast (either copy can serve). Writes cost 2×. Usable capacity: 50%. Survives one disk failure per mirrored pair. Min disks: 2.
RAID 2 — bit-level striping with Hamming ECC
Data is striped at the bit level and protected by a Hamming code stored on dedicated parity disks. Requires spindle synchronisation. Obsolete — modern disks already do internal ECC, so the extra disks are pure overhead. This is the RAID level that uses Hamming code — see §10 for the full working.
RAID 3 — byte-level striping, single parity disk
One dedicated parity disk. Recovery: XOR the surviving disks. Good for large sequential transfers, poor for many small requests (every I/O touches all disks).
RAID 4 — block-level striping, dedicated parity disk
Same as RAID 3 but stripes at block level, so independent small reads can be served by individual disks. Problem: the parity disk is a bottleneck — every write must update it, so writes are serialised on that one disk.
RAID 5 — block-level striping, distributed parity ASKED A LOT
Parity is spread across all disks, removing the RAID 4 bottleneck. Survives one disk failure. Usable capacity: (N−1)/N. Min disks: 3. The most widely used level.
A single small write in RAID 5 costs 4 physical I/Os: read the old data block, read the old parity, write the new data block, write the new parity. New parity is computed as P_new = P_old ⊕ D_old ⊕ D_new. This "read-modify-write" penalty is why RAID 5 is poor for write-heavy workloads like OLTP databases, and why RAID 10 is often preferred there.
RAID 6 — dual distributed parity
Two independent parity blocks (P and Q, using Reed–Solomon) per stripe. Survives two simultaneous failures. Usable capacity: (N−2)/N. Min disks: 4.
RAID 6 ki zaroorat kyun padi? Disks itni badi ho gayi hain (10+ TB) ki rebuild mein kai din lagte hain. Un dino mein doosri disk fail ho jaana ab realistic hai — aur RAID 5 mein us case mein sab kuch gaya. Isliye RAID 6. Ye modern reasoning hai, textbook mein nahi hoti — bolo toh impact padta hai.
RAID 10 vs RAID 01 COMMON
| RAID 10 (1+0) | RAID 01 (0+1) | |
|---|---|---|
| Structure | Mirror first, then stripe — a stripe of mirrored pairs | Stripe first, then mirror — a mirror of two stripe sets |
| Survives | One disk from each mirror pair — up to N/2 failures | Only guaranteed against 1 failure; a second in the other stripe set kills everything |
| Rebuild | Copy from one partner disk — fast | Rebuild the entire stripe set — slow |
| Preferred | Yes — almost always | Rarely used |
Yaad rakhne ka tareeka: "Mirror ko andar rakho." RAID 10 mein har pair apna backup rakhta hai — ek pair ki disk gayi toh sirf woh pair affected. RAID 01 mein poora half backup hai — ek disk gayi toh poora half dead maana jaata hai. Isliye 10 > 01.
The comparison table — memorise this ASKED A LOT
| Level | Technique | Min disks | Usable capacity | Fault tolerance | Read | Write |
|---|---|---|---|---|---|---|
| 0 | Striping | 2 | 100% | None | Excellent | Excellent |
| 1 | Mirroring | 2 | 50% | 1 disk per pair | Very good | Fair (2 writes) |
| 2 | Bit striping + Hamming | 3 | Varies | 1 disk | Good | Poor |
| 3 | Byte striping + parity disk | 3 | (N−1)/N | 1 disk | Good (sequential) | Fair |
| 4 | Block striping + parity disk | 3 | (N−1)/N | 1 disk | Good | Poor (parity bottleneck) |
| 5 | Block striping + distributed parity | 3 | (N−1)/N | 1 disk | Very good | Fair (4 I/O penalty) |
| 6 | Block striping + dual parity | 4 | (N−2)/N | 2 disks | Very good | Poor (6 I/O penalty) |
| 10 | Mirror + stripe | 4 | 50% | 1 per mirror pair | Excellent | Very good |
You have 8 disks of 2 TB each. Compute usable capacity for RAID 0, 1, 5, 6 and 10. Which would you choose for (a) a video editing scratch drive, (b) a bank's transaction database, (c) a cold archive?
Show solution
| Level | Formula | Usable | Tolerates |
|---|---|---|---|
| RAID 0 | 8 × 2 | 16 TB | 0 failures |
| RAID 1 (4 pairs) | 8/2 × 2 | 8 TB | 1 per pair |
| RAID 5 | (8−1) × 2 | 14 TB | 1 failure |
| RAID 6 | (8−2) × 2 | 12 TB | 2 failures |
| RAID 10 | 8/2 × 2 | 8 TB | 1 per mirror pair |
(a) Video editing scratch drive → RAID 0. Needs maximum sequential throughput and capacity; the data is temporary and re-creatable from the source footage. Losing it costs a re-import, not a disaster.
(b) Bank transaction database → RAID 10. OLTP is write-heavy with many small random writes, which is exactly where RAID 5's four-I/O write penalty hurts most. RAID 10 has no parity computation, fast rebuilds, and excellent random write performance. Paying 50% capacity is acceptable here.
(c) Cold archive → RAID 6. Reads are rare, writes rarer still, so the write penalty doesn't matter. What matters is surviving a second failure during a multi-day rebuild of large disks. RAID 6 gives 12 TB usable with two-disk tolerance.
Is tarah ke questions mein interviewer capacity number nahi, aapki reasoning dekhta hai. Hamesha teen cheezein bolo: workload (read-heavy ya write-heavy?), data ki value (re-create ho sakta hai?), aur capacity budget. Phir choice justify karo.
Hamming code
Error detection aur correction. Aapke slides mein RAID 2 ke context mein hai — yahan zero se poora, encode + decode dono.
Why parity alone is not enough
A single parity bit can detect an odd number of bit errors, but it cannot tell you which bit flipped — so it cannot correct anything. And it misses any even number of errors entirely.
The Hamming distance between two codewords is the number of bit positions in which they differ. Compute it as the number of 1s in their XOR.
For a code with minimum distance d:
— to detect up to s errors you need d ≥ s + 1
— to correct up to t errors you need d ≥ 2t + 1
Intuition: agar do valid codewords ke beech kam se kam 3 ka distance hai, toh ek bit flip hone par jo mila woh original ke 1 distance par hai aur kisi bhi doosre valid codeword se 2 distance par. Toh sabse nazdeek wala valid codeword hi original hai — correction possible. Isliye 1-bit correction ke liye distance 3 chahiye: 2(1)+1 = 3.
How many redundancy bits? ASKED A LOT
2r ≥ m + r + 1
Total codeword length = m + r
Why that formula: the r parity bits together form an r-bit "syndrome". It must be able to name every possible single-bit error position (m + r of them) plus the "no error" case (1 more). So 2r distinct values must cover m + r + 1 outcomes.
| Data bits (m) | Required r | Check: 2ʳ ≥ m + r + 1 | Total length |
|---|---|---|---|
| 1 | 2 | 4 ≥ 4 ✓ | 3 |
| 4 | 3 | 8 ≥ 8 ✓ | 7 |
| 7 | 4 | 16 ≥ 12 ✓ | 11 |
| 8 | 4 | 16 ≥ 13 ✓ | 12 |
| 16 | 5 | 32 ≥ 22 ✓ | 21 |
| 32 | 6 | 64 ≥ 39 ✓ | 38 |
| 64 | 7 | 128 ≥ 72 ✓ | 71 |
Trick: r ko 1 se badhate jao jab tak 2ʳ ≥ m + r + 1 na ho jaaye. Chhota r pehle try karo. Ye trial-and-error hi standard method hai — koi closed form nahi.
Where the parity bits go
Parity bits occupy positions that are powers of 2: positions 1, 2, 4, 8, 16, …. Data bits fill all remaining positions. Positions are numbered from 1, starting at the left in the standard convention (some books number from the right — the method is identical, just be consistent and say which you used).
Which bits does each parity bit cover? ASKED A LOT
Parity bit at position 2k checks every position whose binary representation has a 1 in bit-position k. Equivalently:
- P1 (position 1) → check 1, skip 1, check 1, skip 1, … → positions 1, 3, 5, 7, 9, 11 …
- P2 (position 2) → check 2, skip 2, check 2, skip 2, … → positions 2, 3, 6, 7, 10, 11 …
- P4 (position 4) → check 4, skip 4, … → positions 4, 5, 6, 7, 12, 13, 14, 15 …
- P8 (position 8) → check 8, skip 8, … → positions 8, 9, 10, 11, 12, 13, 14, 15 …
Yahi poora Hamming code ka jaadu hai, aur ise samajhna zaroori hai — rat-na nahi.
Har position ka binary number likho. Position 11 = 1011 hai, matlab usme bit0, bit1 aur bit3 set hain — toh position 11 ko P1, P2 aur P8 check karenge. Bas.
Aur decode karte waqt kya hota hai? Jo bhi parity bits fail karenge, unke position numbers jod do — wahi error ki position hai! Kyunki agar position 11 ka bit flip hua, toh exactly wahi parity bits fail karenge jo 11 ko cover karte hain — P1, P2, P8 — aur 1 + 2 + 8 = 11. Position khud bata deti hai apna naam. Ye design genius hai.
Encode the 7-bit data word 1011001 using Hamming code with even parity.
Show solution
Step 1 — how many parity bits? m = 7. Try r = 4: 2⁴ = 16 ≥ 7 + 4 + 1 = 12 ✓. (r = 3 gives 8 ≥ 11 ✗.) So r = 4, total length 11.
Step 2 — lay out the data. Data bits d1…d7 = 1, 0, 1, 1, 0, 0, 1 go into non-power-of-2 positions in order.
| Position | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Bit | P1 | P2 | d1 | P4 | d2 | d3 | d4 | P8 | d5 | d6 | d7 |
| Value | ? | ? | 1 | ? | 0 | 1 | 1 | ? | 0 | 0 | 1 |
Step 3 — compute each parity bit (even parity → total number of 1s must be even).
P1 covers positions 1, 3, 5, 7, 9, 11 → values ?, 1, 0, 1, 0, 1
For even parity, P1 = 1
P2 covers positions 2, 3, 6, 7, 10, 11 → values ?, 1, 1, 1, 0, 1
P4 covers positions 4, 5, 6, 7 → values ?, 0, 1, 1
P8 covers positions 8, 9, 10, 11 → values ?, 0, 0, 1
Step 4 — assemble the codeword.
| Position | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Value | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 |
1 0 1 0 0 1 1 1 0 0 1 — parity bits shown at positions 1, 2, 4, 8.
Har parity bit ke liye same 3 steps: (1) uski covered positions likho (2) un positions pe jo data bits hain unke 1s gino (3) even parity chahiye toh — count odd hai toh parity = 1, even hai toh parity = 0. Parity bit ko khud count mein mat jodo, woh toh aap set kar rahe ho.
The receiver gets 1 0 1 0 0 1 1 1 0 1 1 (positions 1→11). Using even parity, find and correct the error.
Show solution
| Position | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Received | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 |
Step 1 — recompute each parity check, this time INCLUDING the parity bit itself. For even parity, each group's total must be even. Write 0 if OK, 1 if failed.
C1 — positions 1, 3, 5, 7, 9, 11 → 1, 1, 0, 1, 0, 1
C2 — positions 2, 3, 6, 7, 10, 11 → 0, 1, 1, 1, 1, 1
C4 — positions 4, 5, 6, 7 → 0, 0, 1, 1
C8 — positions 8, 9, 10, 11 → 1, 0, 1, 1
Step 2 — build the syndrome. Read the checks as a binary number, C8 C4 C2 C1 (most significant first):
Or equivalently: add the positions of the failing parity bits → 2 + 8 = 10
Step 3 — correct it. The error is in position 10. Flip that bit: 1 → 0.
| Position | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Corrected | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 |
Step 4 — extract the data. Remove positions 1, 2, 4, 8:
This matches the original data word from Q16.1. ✓
Syndrome = 0 → no error. Syndrome = k (non-zero) → the bit at position k is wrong; flip it. No lookup table, no search — the syndrome is the address of the faulty bit.
Sabse common galti decode karte waqt: encoding mein aap parity bit ko count se chhodte ho (kyunki woh abhi banana hai), par decoding mein parity bit ko count mein shaamil karte ho (kyunki woh ab receive ho chuka hai). Ye difference bhool jaana sabse badi galti hai. Encode = "kya hona chahiye", decode = "kya mila".
SEC-DED — single error correction, double error detection
Plain Hamming code corrects one error but silently mis-corrects when two errors occur (the syndrome points at some innocent third bit). Fix: add one extra overall parity bit P0 covering the entire codeword.
| Overall parity P0 | Syndrome | Conclusion |
|---|---|---|
| Correct | 0 | No error |
| Wrong (odd # of errors) | ≠ 0 | Single error at the syndrome position → correct it |
| Correct (even # of errors) | ≠ 0 | Double error detected → cannot correct, request retransmission |
| Wrong | 0 | Error is in P0 itself |
SEC-DED hi ECC RAM mein use hota hai. Servers mein "ECC memory" ka matlab yahi hai — ek bit flip ho toh chup-chaap theek kar do, do bit flip hon toh machine ko batao (aur usually machine check exception raise karo). Ye batana interview mein connection banata hai theory aur real hardware ke beech.
(a) How many redundancy bits are needed for a 16-bit data word? (b) Encode the 4-bit data word 1011 with even parity. (c) A 7-bit Hamming codeword 1001101 is received (even parity, positions numbered 1→7 left to right). Find and correct the error.
Show solution
(a) m = 16. Try r = 5: 2⁵ = 32 ≥ 16 + 5 + 1 = 22 ✓. Try r = 4: 16 ≥ 21 ✗. So r = 5, total codeword length 21.
(b) Encoding 1011 — m = 4, so r = 3 (2³ = 8 ≥ 4 + 3 + 1 = 8 ✓). Total length 7.
| Position | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|
| Bit | P1 | P2 | d1 | P4 | d2 | d3 | d4 |
| Value | ? | ? | 1 | ? | 0 | 1 | 1 |
- P1 covers 1, 3, 5, 7 → data at 3, 5, 7 = 1, 0, 1 → two 1s (even) → P1 = 0
- P2 covers 2, 3, 6, 7 → data at 3, 6, 7 = 1, 1, 1 → three 1s (odd) → P2 = 1
- P4 covers 4, 5, 6, 7 → data at 5, 6, 7 = 0, 1, 1 → two 1s (even) → P4 = 0
Verify: C1 → positions 1,3,5,7 = 0,1,0,1 → sum 2 ✓ · C2 → 1,1,1,1 → sum 4 ✓ · C4 → 0,0,1,1 → sum 2 ✓. All even, so the encoding is right.
(c) Decoding 1001101.
| Position | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|
| Received | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
- C1 — positions 1, 3, 5, 7 → 1, 0, 1, 1 → sum 3, odd → fails → C1 = 1
- C2 — positions 2, 3, 6, 7 → 0, 0, 0, 1 → sum 1, odd → fails → C2 = 1
- C4 — positions 4, 5, 6, 7 → 1, 1, 0, 1 → sum 3, odd → fails → C4 = 1
Error is at position 7. Flip it: 1 → 0.
Data bits (positions 3, 5, 6, 7) = 0 1 0 0
Always re-run the parity checks on your corrected codeword. C1: positions 1,3,5,7 = 1,0,1,0 → sum 2 ✓ even. C2: 0,0,0,0 → 0 ✓. C4: 1,1,0,0 → 2 ✓. All pass — the correction is right.
File systems
Slides mein kam hai, par interviews mein aata hai — especially inode aur hard vs soft link.
File attributes & operations
Attributes: name, identifier (a unique number, the real name inside the FS), type, location, size, protection, timestamps.
Operations: create, write, read, reposition (seek), delete, truncate. The OS keeps an open-file table with a file pointer, open count, disk location and access rights per open file.
Allocation methods COMMON
| Contiguous | Linked | Indexed | |
|---|---|---|---|
| Layout | Blocks in one continuous run | Each block points to the next | An index block lists all block addresses |
| Directory stores | Start block + length | First + last block | Index block address |
| Sequential access | Excellent | Good | Good |
| Direct (random) access | Excellent — O(1) | Terrible — must walk the chain | Good — one extra read |
| External fragmentation | Yes — the main problem | No | No |
| Growth | Hard — may need to move the file | Easy | Easy (up to index size) |
| Space overhead | None | 1 pointer per block | One whole index block per file |
| Reliability | Good | Poor — one bad pointer loses the rest | Good |
FAT (File Allocation Table) is a variation of linked allocation: all the "next block" pointers live in one table at the start of the volume, so random access only requires walking the in-memory table rather than reading each data block.
The inode COMMON
Unix uses a multilevel indexed scheme. An inode holds metadata plus block pointers:
Design ka logic: zyadatar files chhoti hoti hain. 12 direct pointers × 4 KB block = 48 KB — matlab 48 KB tak ki file ke liye koi extra index block padhna hi nahi padta. Aur bade files ke liye indirect levels hain. Common case fast, rare case possible. Ye system design ka classic principle hai — bolo toh acchha lagta hai.
Block size = 4 KB, block pointer = 4 bytes, inode has 12 direct, 1 single indirect, 1 double indirect, 1 triple indirect pointer. What is the maximum file size?
Show solution
Pointers per index block = 4 KB / 4 B = 1024.
| Level | Blocks addressed | Bytes |
|---|---|---|
| 12 direct | 12 | 12 × 4 KB = 48 KB |
| Single indirect | 1024 | 1024 × 4 KB = 4 MB |
| Double indirect | 1024² = 1,048,576 | ≈ 4 GB |
| Triple indirect | 1024³ = 1,073,741,824 | ≈ 4 TB |
The triple-indirect term dominates so completely that the others are rounding errors — but write them all out; examiners want the full breakdown.
Hard link vs Soft (symbolic) link ASKED A LOT
| Hard link | Soft link (symlink) | |
|---|---|---|
| Points to | The inode directly | The pathname (it's a small file containing a path) |
| Own inode? | No — shares the target's inode | Yes — it's a separate file |
| Delete the original | File survives — link count just drops | Link becomes dangling/broken |
| Across filesystems | No — inode numbers are per-filesystem | Yes |
| To a directory | Not allowed (would create cycles) | Allowed |
| Command | ln file link | ln -s file link |
Hard link = ek hi ghar ke do darwaze. Dono asli hain, dono se ek hi ghar mein pahunchte ho. Ek darwaza band kar do, ghar zinda hai. Ghar tabhi jaata hai jab saare darwaze band ho jaayein (link count = 0).
Soft link = ek kaagaz jispe pata likha hai. Kaagaz apne aap mein ek cheez hai. Agar ghar hi gira diya jaaye, toh kaagaz bacha rahega par uspe likha pata bekaar ho jaayega — dangling link.
Isliye rm actually "unlink" hai — woh file delete nahi karta, sirf link count ghatata hai. Count 0 hone par hi blocks free hote hain. (Aur agar koi process ne file open rakhi hai, toh woh bhi ek reference hai — isliye running program ki file delete karne par space turant free nahi hota.) Ye ek badhiya deep answer hai.
Free-space management
- Bit vector / bitmap — one bit per block, 1 = free. Easy to find contiguous runs (scan for consecutive 1s), but the whole map must be kept in memory to be fast. For a 1 TB disk with 4 KB blocks: 228 bits = 32 MB of bitmap.
- Linked list — each free block points to the next. No wasted space, but traversing to find n contiguous blocks requires reading each one — very slow.
- Grouping — the first free block stores the addresses of n free blocks; the last of those points to the next group. Finds many free blocks quickly.
- Counting — store (first free block, count of contiguous free blocks). Compact when free space is clustered.
Journaling COMMON
A crash in the middle of a multi-step update (e.g. "remove block from free list" then "add it to the file") leaves the file system inconsistent. A journaling file system first writes the intended changes to a log (journal), then applies them. After a crash, replay or discard incomplete journal entries.
- Write-ahead logging: log first, then modify. On recovery, any transaction with a commit record is redone; any without is discarded.
- Recovery becomes seconds instead of an hours-long
fsckover the whole disk. - Metadata-only journaling (ext3 default) is fast but can leave stale data in files; full data journaling is safer but writes everything twice.
Journaling ka idea database ke transactions se aaya hai — atomicity. Ya toh poora change ho, ya bilkul na ho. Ek line mein: "pehle likho ki kya karne wale ho, phir karo — taaki crash ke baad pata ho ki kya adhoora reh gaya."
Formula sheet
Interview se ek raat pehle sirf ye page dekh lena kaafi hai.
Scheduling
Waiting Time = Turnaround − Burst
Response Time = First CPU allocation − Arrival
Throughput = processes completed / total time
RR: max wait ≤ (n − 1) × q · context switches ≈ Σ⌈BTᵢ/q⌉ − 1
RR overhead fraction = switch_time / (q + switch_time)
HRRN Response Ratio = (W + S) / S
SJF burst prediction: τₙ₊₁ = α·tₙ + (1−α)·τₙ
Memory & paging
Logical address = m bits → page number = (m − d) bits
Number of pages = logical space / page size
Number of frames = physical memory / frame size
Page table size = number of pages × PTE size
EAT with TLB (TLB time counted): EAT = h(t + m) + (1 − h)(t + 2m)
EAT, n-level paging: EAT = h(t + m) + (1 − h)(t + (n+1)m)
EAT with page faults: EAT = (1 − p)·m + p·(fault service time)
Avg internal fragmentation per process ≈ page_size / 2
Deadlock
Available = Total − Σ Allocation
Safety check: find i with Need[i] ≤ Work → Work += Allocation[i]
n processes, each needing max k instances of one resource type:
deadlock-free ⟺ m ≥ n(k − 1) + 1
deadlock possible ⟺ m ≤ n(k − 1)
Disk
One rotation = 60 / RPM seconds
Avg rotational latency = ½ × (60 / RPM)
Transfer time = data size / transfer rate
or = (sectors read / sectors per track) × rotation time
Head movement: sum of |current − next| over the schedule
SCAN goes to cylinder 0 / max; LOOK does not
RAID & Hamming
RAID 0 usable = N · RAID 1 & 10 = N/2
RAID 5 = N − 1 · RAID 6 = N − 2
RAID 5 small-write penalty = 4 physical I/Os
Hamming: 2r ≥ m + r + 1
Parity bits at positions 1, 2, 4, 8, 16 … (powers of 2)
Pᵢ at position 2k covers positions with bit k set
Syndrome = Σ (positions of failing parity checks) = error position
Detect s errors: d ≥ s + 1 · Correct t errors: d ≥ 2t + 1
File systems
Max file size = (direct × B) + (P × B) + (P² × B) + (P³ × B)
Bitmap size = disk size / block size, in bits
Rapid-fire Q&A
50 questions, one-paragraph answers. Ye woh hain jo actually poochhe jaate hain — bol kar practice karo, padh kar nahi.
Processes & threads
What is the difference between a process and a thread?
Show answer
A process is an independent program in execution with its own address space; a thread is a unit of execution within a process. Threads of one process share code, data, heap and open files, but each has its own stack, registers and program counter. Consequences: thread creation and context switching are cheaper (no address-space change), thread communication is just shared memory, but there is no isolation — one thread crashing takes the whole process down, and shared data needs explicit synchronisation.
What does a context switch save?
Show answer
The program counter, stack pointer, general-purpose registers, processor status word, and memory-management state (page table base register or base/limit) — all written into the outgoing process's PCB, then loaded from the incoming one. The bigger cost is usually indirect: cache pollution, TLB flush (unless ASID-tagged), and cold branch predictors. During a switch, zero useful work happens.
Difference between a mode switch and a context switch?
Show answer
A mode switch changes the CPU privilege level (user ↔ kernel) but the same process continues — this is what a system call does. A context switch changes which process is running, requiring PCB save/restore and scheduler involvement. Every context switch involves a mode switch, but not vice versa. Mode switches cost hundreds of nanoseconds; context switches cost microseconds plus cache effects.
Can a process have zero threads?
Show answer
No. A process is defined by having at least one thread of execution — the main thread. If the main thread exits, the process terminates. A process with zero threads has nothing to execute, so it would be indistinguishable from a terminated process.
Why is fork() followed by exec() rather than a single "spawn" call?
Show answer
The gap between fork and exec gives the child a window to configure its environment while it is still a copy of the parent — redirect file descriptors (this is how shell redirection and pipes work), change the working directory, drop privileges, set signal handlers. A single spawn call would need dozens of parameters to express all of that. Copy-on-write makes the seemingly wasteful copy nearly free.
How do you kill a zombie process?
Show answer
You can't — it is already dead; kill -9 has no effect since there is no running code to signal. The zombie exists only as a PCB entry holding an exit status. It disappears when the parent calls wait(). If the parent is buggy and never does, kill the parent: the zombie is then re-parented to init (PID 1), which reaps it automatically.
Scheduling
Which scheduling algorithm gives minimum average waiting time?
Show answer
SJF (and its preemptive form SRTF) is provably optimal for average waiting time. It's not usable directly because burst times aren't known in advance; systems approximate it with exponential averaging of past bursts, or observe behaviour dynamically via MLFQ. SJF also risks starving long jobs, which HRRN and aging address.
What is the convoy effect?
Show answer
Under FCFS, one long CPU-bound process holds the CPU while many short I/O-bound processes queue behind it. Average waiting time balloons, but the worse problem is that all the I/O devices sit idle during that time, because the processes that would drive them are stuck in the ready queue. Utilisation drops across the whole system, not just the CPU.
How do you choose the Round Robin time quantum?
Show answer
It must be large relative to the context-switch time or overhead dominates (0.1 ms switch with 1 ms quantum wastes ~9% of the CPU), and small enough that response time stays interactive. The standard rule of thumb: pick q so that roughly 80% of CPU bursts complete within one quantum. Typical values are 10–100 ms. If q exceeds the longest burst, RR degenerates to FCFS.
What is aging?
Show answer
Gradually increasing the priority of processes that have been waiting a long time, so a low-priority process cannot be starved indefinitely by a stream of high-priority arrivals. It converts an unbounded wait into a bounded one. HRRN builds aging directly into its formula, since the response ratio grows with waiting time.
Synchronisation
Mutex vs semaphore?
Show answer
A mutex is a locking mechanism with ownership — only the thread that locked it may unlock it, which enables priority inheritance. A semaphore is a signalling mechanism with a counter and no ownership — any thread may signal, and the classic use is one thread notifying another that an event occurred. A binary semaphore looks like a mutex but lacks ownership semantics.
What are the three requirements of a critical-section solution?
Show answer
Mutual exclusion (only one process in its critical section at a time), progress (if the critical section is free, a process wanting to enter must be able to, and the decision can't be postponed indefinitely), and bounded waiting (a bound on how many times others can enter before a waiting process gets its turn). Most broken solutions fail progress, not mutual exclusion.
What is a spinlock and when should you use one?
Show answer
A lock where a waiting thread busy-waits in a loop instead of blocking. It wastes CPU while spinning, but avoids the two context switches that blocking costs. Use it when the lock is held for a very short time and you have multiple cores (on a uniprocessor, spinning can never succeed — the lock holder isn't running). Kernel code uses spinlocks heavily for short critical sections.
What is priority inversion?
Show answer
A high-priority thread is blocked waiting on a lock held by a low-priority thread, and a medium-priority thread preempts the low-priority one — so effectively the high-priority thread waits behind a medium-priority one. The fix is priority inheritance: temporarily raise the lock holder's priority to that of the highest waiter. This requires knowing the owner, which is why it works with mutexes but not semaphores. The 1997 Mars Pathfinder resets were caused by exactly this.
What is a monitor, and how does it differ from a semaphore?
Show answer
A monitor is a language-level construct in which only one process can be active inside at a time, with mutual exclusion supplied automatically by the compiler. Semaphores are low-level and require the programmer to place every wait and signal correctly. One behavioural difference matters: a semaphore remembers a signal (the counter increments even with no waiter), whereas a condition variable's signal is lost if nobody is waiting — which is why you always use while (cond) wait(); rather than if.
Deadlock
What are the four conditions for deadlock, and can you break just one?
Show answer
Mutual exclusion, hold and wait, no preemption, and circular wait. All four must hold simultaneously, so breaking any single one prevents deadlock. In practice, circular wait is the one that is broken — by imposing a global ordering on resource acquisition. That's why kernel code documents lock ordering.
Does a cycle in a resource allocation graph always mean deadlock?
Show answer
Only if every resource type in the cycle has a single instance. With multiple instances, a cycle is a necessary but not sufficient condition — an instance held by a process outside the cycle may be released and break it. No cycle, however, always means no deadlock.
Is an unsafe state the same as a deadlocked state?
Show answer
No. Unsafe means deadlock is possible if every process demands its maximum claim; it does not mean deadlock has occurred or will occur. Every deadlocked state is unsafe, but not every unsafe state is deadlocked. Deadlock avoidance is conservative — it refuses requests that would lead to unsafe states even though those states might have been fine.
Why do real operating systems ignore deadlock?
Show answer
Because prevention and avoidance impose a cost on every resource request — restricted request patterns, lower utilisation, runtime safety checks — while deadlocks are rare. Linux and Windows use the "ostrich algorithm": assume it won't happen, and if the system hangs, the user reboots. The economics favour a rare reboot over a permanent tax. Databases, where a deadlock is likely and a rollback is cheap, do use detection and recovery.
Deadlock vs starvation vs livelock?
Show answer
Deadlock: processes blocked forever in a circular wait; no CPU used; never resolves itself. Starvation: a process is runnable but never gets scheduled or granted a resource due to an unfair policy; fixable with aging. Livelock: processes are actively running and changing state in response to each other, but making no progress — CPU is busy, work is zero. Randomised backoff typically resolves livelock.
Memory
Internal vs external fragmentation?
Show answer
Internal fragmentation is unused space inside an allocated block, because the block is larger than requested — it occurs with fixed partitions and with paging (the last page of a process is partly empty). External fragmentation is free memory that exists but is scattered in non-contiguous holes, so a large request fails despite sufficient total free space — it occurs with dynamic partitioning and segmentation. Compaction fixes external fragmentation; paging avoids it entirely.
Why does paging eliminate external fragmentation?
Show answer
Because a process's pages don't need contiguous frames — any free frame will do for any page. Contiguity was the reason free space had to be in one usable run. The trade-off is that the last page of each process is usually partly empty, giving internal fragmentation of about half a page per process, which is small and predictable.
What is a TLB and why does it matter so much?
Show answer
A small associative cache of recent page-number → frame-number translations. Without it, every memory reference needs an extra memory access to read the page table, halving performance — and with multilevel paging it's worse (n+1 accesses). Hit ratios above 99% are typical because of locality. On a context switch the TLB must be flushed or tagged with an ASID, which is why ASIDs matter for switch cost.
Why multilevel paging?
Show answer
A single-level page table for a 32-bit address space with 4 KB pages and 4-byte entries is 4 MB per process, and must be contiguous in memory. Multilevel paging lets you omit inner tables for unused regions of the address space — and real processes use a tiny fraction of theirs. The cost is one extra memory access per level on a TLB miss.
Paging vs segmentation?
Show answer
Paging uses fixed-size blocks decided by hardware and is invisible to the programmer; it solves physical memory management and causes internal fragmentation. Segmentation uses variable-size blocks that match logical program units (code, stack, data) and is visible to the programmer; it enables natural protection and sharing but causes external fragmentation. Modern systems combine both — segmented paging.
What is demand paging?
Show answer
Loading a page into memory only when it is actually referenced, rather than loading the whole program up front. Pages not in memory are marked invalid; touching one causes a page fault, the OS fetches it from disk and restarts the faulting instruction. This lets a program larger than physical memory run, and lets more processes fit in memory.
What is Belady's anomaly?
Show answer
The counter-intuitive situation where increasing the number of frames increases the number of page faults. It affects FIFO but not LRU or Optimal, because those are stack algorithms: the set of pages resident with n frames is always a subset of the set with n+1 frames, which makes the anomaly impossible. The standard demonstration string is 1 2 3 4 1 2 5 1 2 3 4 5 — 9 faults with 3 frames, 10 with 4.
Why isn't true LRU implemented in real systems?
Show answer
Both implementations — timestamp counters and a stack of page numbers — require work on every single memory reference, which needs hardware support that commodity CPUs don't provide. So systems use approximations based on a hardware-maintained reference bit: the clock (second-chance) algorithm, or enhanced clock using both reference and modify bits.
What is thrashing and how do you fix it?
Show answer
A process spends more time paging than executing, because it doesn't have enough frames to hold its active locality. It's self-reinforcing: low CPU utilisation makes the OS admit more processes, which steals more frames, which causes more faults. The fix is counter-intuitive — reduce the degree of multiprogramming by suspending processes. The working-set model sizes each process's frame allocation to its locality; the page-fault-frequency scheme adjusts allocation based on measured fault rate.
What is copy-on-write?
Show answer
After fork(), parent and child share the same physical pages, all marked read-only. The first write by either triggers a protection fault; the OS then copies just that one page and lets the write proceed. This makes fork nearly free, which matters because the child usually calls exec() immediately and would have discarded a full copy anyway.
Storage & I/O
What are the components of disk access time?
Show answer
Seek time (moving the arm to the right cylinder — largest and most variable), rotational latency (waiting for the sector to arrive under the head, averaging half a rotation), and transfer time (actually reading the bytes). Only transfer time scales with data size, which is why large sequential transfers are dramatically more efficient than many small random ones.
SCAN vs LOOK vs C-SCAN?
Show answer
SCAN sweeps to the physical end of the disk, reverses, and services on the way back. LOOK is the same but only travels as far as the last request in each direction, so it never wastes movement. C-SCAN services in one direction only and jumps back to the far end without servicing, which gives more uniform wait times at the cost of extra head movement. C-LOOK is C-SCAN without the trip to the physical end.
What's wrong with SSTF?
Show answer
It can starve requests far from the current head position, because newly arriving nearby requests always win. It's also greedy, not optimal — choosing the locally nearest request can produce a worse total than a planned sweep. On the standard example (queue 98 183 37 122 14 124 65 67, head at 53), SSTF gives 236 cylinders while LOOK gives 208.
Why is RAID 5 bad for write-heavy workloads?
Show answer
Each small write requires four physical I/Os: read the old data block, read the old parity, write the new data, write the new parity (computed as old parity XOR old data XOR new data). For random-write-heavy workloads like OLTP databases this is a severe penalty, which is why RAID 10 — which has no parity computation and rebuilds from a single partner disk — is usually preferred there.
Why does RAID 6 exist if RAID 5 already tolerates a failure?
Show answer
Because modern disks are large enough that rebuilding a failed drive takes many hours or days, and the rebuild itself stresses every remaining disk. A second failure during that window is realistically likely, and in RAID 5 that means total data loss. RAID 6's dual parity survives two simultaneous failures at the cost of one more disk of capacity and a heavier write penalty.
Which RAID level uses Hamming code, and why is it obsolete?
Show answer
RAID 2 — bit-level striping with Hamming ECC on dedicated parity disks. It's obsolete because modern disk drives already perform their own internal error detection and correction on every sector, so the array-level Hamming code is redundant overhead. It also required spindle synchronisation across drives. Parity-based levels (3–6) achieve the same fault tolerance with far fewer disks.
Hard link vs soft link?
Show answer
A hard link is another directory entry pointing at the same inode; the file survives until the link count reaches zero, but hard links can't cross filesystems or point to directories. A soft link is a separate file whose contents are a pathname; it can cross filesystems and point to directories, but breaks (dangles) if the target is removed. This is why rm is really unlink — it decrements a count rather than deleting data.
Why does the Unix inode use direct plus indirect pointers?
Show answer
Most files are small. Twelve direct pointers cover ~48 KB with 4 KB blocks, so small files need no extra index reads at all. Indirect, double-indirect and triple-indirect pointers extend the maximum file size to terabytes, at the cost of one extra read per level — but only for the rare large file. It's the "optimise the common case, keep the rare case possible" principle.
What does journaling protect against?
Show answer
Filesystem inconsistency after a crash that interrupts a multi-step metadata update. The intended changes are written to a log first (write-ahead logging); after a crash, committed transactions are replayed and uncommitted ones discarded. Recovery takes seconds instead of a full-disk fsck. Metadata-only journaling is faster; full data journaling is safer but writes everything twice.
Fundamentals
Interrupt vs trap vs system call?
Show answer
An interrupt is asynchronous and comes from hardware (keyboard, disk, timer). A trap/exception is synchronous and caused by the executing instruction itself (divide by zero, page fault). A system call is a synchronous, deliberate request by the program for a kernel service. All three use the same underlying mechanism — save state, switch to kernel mode, dispatch through a vector table — only the trigger differs.
What is DMA and what does it save?
Show answer
Direct Memory Access lets a controller transfer data between a device and memory without the CPU moving each byte. The CPU sets up the transfer and gets one interrupt per block instead of one per byte or word. It's essential for high-throughput devices like disks and network cards. The DMA controller "steals" memory bus cycles from the CPU, which slows it slightly — far less than per-byte interrupt handling would.
What is dual-mode operation and why is it needed?
Show answer
A hardware mode bit distinguishes kernel mode from user mode. Certain instructions — I/O, setting the timer, loading base/limit or page-table registers, changing the mode bit — are privileged and trap if attempted in user mode. Without it, any user program could perform I/O directly, disable the timer to hog the CPU, or read other processes' memory, so all protection would collapse.
Monolithic vs microkernel?
Show answer
A monolithic kernel puts the file system, drivers, networking and scheduler in one address space, so they call each other as function calls — fast, but a bug in any component can crash the system. A microkernel keeps only IPC, scheduling and basic memory management in the kernel and pushes everything else into user-space servers — reliable and extensible, but each service request becomes message passing with several mode switches, so it is slower. Linux is monolithic but modular; Windows NT and macOS are hybrids.
Multiprogramming vs multitasking vs multiprocessing?
Show answer
Multiprogramming keeps several jobs in memory and switches when the running one blocks for I/O — the goal is CPU utilisation. Multitasking (time sharing) adds a time quantum so switching also happens on a timer — the goal is response time. Multiprocessing means the system has more than one CPU or core, giving true parallelism. The first two are about one CPU; the third is about several.
What is spooling and what did it enable?
Show answer
Simultaneous Peripheral Operation On-Line — using disk as a buffer so a slow device (printer) doesn't block the CPU. Its deeper significance is that it created a job pool on disk: instead of running jobs in the fixed order they sat on tape, the OS could now choose any job. That choice is what made job scheduling possible in the first place.
What are the three schedulers?
Show answer
Long-term decides which jobs are admitted, controlling the degree of multiprogramming; it runs infrequently and aims for a good mix of CPU-bound and I/O-bound jobs. Medium-term swaps processes out to disk and back, reducing multiprogramming when memory is tight. Short-term (CPU scheduler) picks which ready process runs next; it runs every few milliseconds and must be fast.
Difference between the scheduler and the dispatcher?
Show answer
The scheduler is policy — it decides which process should run. The dispatcher is mechanism — it performs the context switch, switches to user mode, and jumps to the correct location in the program. The time the dispatcher takes is called dispatch latency, and it must be minimal since it is pure overhead.
Real-time systems — hard vs soft?
Show answer
In a hard real-time system, missing a deadline is a system failure (airbag controller, pacemaker) — so virtual memory is usually disallowed because a page fault introduces unpredictable delay. In a soft real-time system, a missed deadline degrades quality but the system continues (video playback, VoIP). The key insight: real-time means predictable, not fast. A system that always takes exactly 50 ms is real-time; one that usually takes 1 ms but sometimes 200 ms is not.
What is the working set model?
Show answer
The working set WS(Δ) is the set of distinct pages a process referenced in its most recent Δ references — an approximation of its current locality. If the sum of all processes' working set sizes exceeds available frames, the system will thrash, so the OS suspends a process. It's adaptive: as a process moves between localities, its working set shrinks or grows and frames can be reallocated accordingly.
Why is a page fault recoverable but a divide-by-zero usually isn't?
Show answer
Both are synchronous traps, but a page fault has a defined remedy: fetch the page from disk, fix the page table entry, and re-execute the same instruction, which then succeeds transparently. A divide-by-zero has no such remedy — the OS delivers SIGFPE and the process typically dies. The restartability requirement puts a real constraint on CPU design: a faulting instruction must be re-executable with no partial side effects.
7-day plan
Agar time kam hai, is order mein padho. Har din 2–3 ghante.
| Day | Sections | What to actually do |
|---|---|---|
| 1 | §01–§03 + §04 | Read through. Then draw the process memory layout and the 5-state diagram from memory on paper. Do all fork() questions. |
| 2 | §06 CPU scheduling | Solve every Gantt chart question with the solution hidden. Redo any you got wrong the next morning. |
| 3 | §07–§08 | Write the producer-consumer code from memory. Explain out loud why swapping the waits deadlocks. |
| 4 | §0A Deadlocks | Do the Banker's example twice — once reading, once blind. Memorise m ≥ n(k−1)+1. |
| 5 | §0B–§0C | All address-split and page-table-size numericals. Do the EAT calculations until they're automatic. |
| 6 | §0D Virtual memory | Run the 20-reference string through FIFO, LRU and OPT yourself. Then do Belady's anomaly. |
| 7 | §0E–§10 + §12–§13 | Disk scheduling (all six), RAID table, one Hamming encode + one decode. Then the whole rapid-fire section out loud. |
- §12 Formula sheet — read it twice
- §13 Rapid-fire — all 50, spoken aloud
- Process memory layout, 5-state diagram, 4 deadlock conditions, mutex vs semaphore — these four come up most
- One Gantt chart, one Banker's, one page replacement, one disk scheduling — just to keep the mechanics warm
Do practical tips jo actually farak paate hain:
1. Bol kar practice karo. OS ka interview zubaani hota hai. Padh kar sab samajh aata hai, par bolte waqt atak jaate ho. Har concept ko 60 second mein bina dekhe explain karne ki practice karo — apne aap se, deewar se, kisi se bhi.
2. Numericals mein table banao, mental math mat karo. Gantt chart, Banker's, page replacement — teeno mein log isliye galti karte hain ki woh dimaag mein calculate karne lagte hain. Kaagaz pe column banao. Interviewer ko bhi accha lagta hai jab aap structured dikhte ho.
3. "Pata nahi" bolna theek hai — par uske baad rukna nahi. "Exact algorithm yaad nahi hai, par idea ye hai ki…" — ye answer poore silence se kaafi behtar hai. OS mein bahut kuch hai, sab yaad rakhna possible nahi.