// OPERATING SYSTEMSEight chapters. One machine.
261305 · Chiang Mai University · following Silberschatz, Galvin & Gagne, Operating System Concepts 10th edition. How an OS shares one CPU, one memory and one disk among programs that all think they own the machine.
What an OS is and does
Chapters 1–2. Interrupts, dual-mode operation, system calls, and the four ways to structure a kernel. The vocabulary everything else is written in.
Processes, threads, scheduling
Chapters 3–5. What a process actually is, how threads differ, and the algorithms that decide who gets the CPU next — with the arithmetic the exam asks for.
Concurrency and deadlock
Chapters 6–8. Race conditions, the critical-section problem, mutexes, semaphores, monitors, the classic problems — and the four conditions that produce deadlock.
Two working solvers
A CPU scheduling simulator that draws the Gantt chart and computes waiting and turnaround time for six algorithms, and a Banker's algorithm checker that finds the safe sequence.
The one idea that connects all eight chapters
Every chapter is the same question asked about a different resource: how do you share one thing among many claimants without them noticing, corrupting each other, or getting stuck?
| Chapter | The shared thing | The mechanism |
|---|---|---|
| 1–2 | the whole machine | dual-mode operation, interrupts, system calls |
| 3 | the illusion of a private machine | the process, the PCB, context switching |
| 4 | one process's address space | threads |
| 5 | the CPU | scheduling algorithms |
| 6–7 | shared data | mutexes, semaphores, monitors |
| 8 | everything at once | deadlock prevention, avoidance, detection |
// LEARNChapters
All eight chapters, written for understanding rather than transcription. Open one box at a time — each is one idea you could be asked to explain out loud.
1 Introduction — what an OS actually is
There is no single agreed definition. The working one: the OS is the program that runs at all times on the computer — the kernel — plus system programs and application programs.
What it does
It sits between the user and the hardware and acts as resource allocator and control program. It has to be fair, efficient, and prevent programs from interfering with each other.
Interrupts — how the OS ever gets control back
Once a user program is running, the CPU is its CPU. The only reason the OS ever runs again is an interrupt.
- Hardware interrupt — a device signals the CPU over the system bus.
- Trap / exception — a software-generated interrupt: an error, or a deliberate system call.
- The CPU stops, saves state, jumps through the interrupt vector to the handler, then resumes.
Dual-mode operation
One mode bit in hardware separates user mode (1) from kernel mode (0). Dangerous instructions are privileged and only run in kernel mode.
This is the single most examined diagram in Chapter 1. Without hardware support for two modes, no protection is possible at all.
Storage hierarchy
registers → cache → main memory → SSD → hard disk → tape. Going down: bigger, slower, cheaper, and more persistent. Volatile above the disk line, non-volatile below.
Multiprogramming and multitasking
Multiprogramming keeps several jobs in memory so the CPU always has something to run — when one waits for I/O, another gets the CPU. Multitasking (time sharing) switches so frequently that users can interact with each job. That switching requires CPU scheduling (Ch 5) and, if memory is short, swapping or virtual memory.
2 OS Services & Structures
Services, in two groups
- Helpful to the user: user interface, program execution, I/O operations, file-system manipulation, communications, error detection.
- Efficient operation of the system itself: resource allocation, logging/accounting, protection and security.
System calls — the only door into the kernel
Programs almost never issue system calls directly; they call an API (Win32, POSIX, the Java API) and the run-time library issues the call. Each call has a number; the OS keeps a system-call table indexed by that number.
Three ways to pass parameters: in registers; in a block/table in memory with the address in a register; or pushed on the stack. Registers are fastest but there may be more parameters than registers.
| Category | Examples |
|---|---|
| Process control | fork, exit, wait, abort |
| File management | open, read, write, close |
| Device management | ioctl, read, write |
| Information maintenance | getpid, alarm, sleep |
| Communications | pipe, shm_open, mmap |
| Protection | chmod, umask, chown |
Four kernel structures — know the trade-off
| Structure | Idea | Cost |
|---|---|---|
| Monolithic | Everything in one address space. UNIX, Linux. | Fast, but huge and hard to maintain; one bug can take down the kernel |
| Layered | Layer n uses only layer n−1. | Clean to build and debug; hard to define the layers, and slow from crossing them |
| Microkernel | Move as much as possible into user space; kernel does little but message passing. Mach. | Reliable and extensible, but message passing costs performance |
| Modules | Object-oriented core with loadable kernel modules (LKM). Linux, Solaris. | Best of both — flexible without message-passing overhead |
Real systems are hybrid: Linux is monolithic plus modules; macOS and iOS layer a hybrid Mach/BSD kernel; Windows is mostly monolithic with a microkernel heritage.
3 Processes
A process is a program in execution. The program is passive — a file on disk. The process is active, with a program counter and resources.
The four parts of a process in memory
| Section | Holds | Size |
|---|---|---|
| Text | the program code | fixed |
| Data | global variables | fixed |
| Heap | memory allocated at run time | grows |
| Stack | temporary data: parameters, return addresses, locals | grows and shrinks |
Five states
↑ ↓
waiting
Only one process is running per CPU core at any instant. Many can be ready, many can be waiting.
The PCB — the process's identity card
The Process Control Block stores everything needed to restart a process: state, program counter, CPU registers, scheduling information, memory-management information, accounting information, I/O status.
Creating and ending processes
In UNIX, fork() makes a copy of the parent and returns 0 to the child and the child's pid to the parent. Usually the child then calls exec() to replace its image with a new program, while the parent may wait().
- Zombie — the process has terminated but the parent has not yet called
wait(). - Orphan — the parent terminated first; init/systemd adopts it.
Interprocess communication — two models
| Shared memory | Message passing | |
|---|---|---|
| How | a region both map and read/write directly | send() / receive() through the kernel |
| Speed | faster — kernel involved only at setup | slower — every message is a system call |
| Synchronisation | the programmer's problem | handled by the mechanism |
| Suits | one machine, large data | distributed systems, safety |
The producer–consumer problem is the standard shared-memory example, and it walks straight into Chapter 6 — an unsynchronised bounded buffer has a race condition.
4 Threads & Concurrency
A thread is the unit of CPU use. Threads of one process share text, data, heap and open files, but each has its own thread ID, program counter, register set and stack.
Four benefits
- Responsiveness — the UI keeps running while another thread blocks.
- Resource sharing — free, because they share by default.
- Economy — creating a thread is far cheaper than creating a process.
- Scalability — a multithreaded process can genuinely run in parallel on multiple cores.
Concurrency ≠ parallelism
Concurrency = making progress on more than one task. Parallelism = actually performing more than one at the same instant. A single core can be concurrent but never parallel.
Amdahl's Law — the ceiling on what more cores can buy you, where S is the strictly serial fraction:
If 25% of the work is serial, then even with infinite cores the speedup can never exceed 4×. Serial fraction, not core count, is the limit.
Multithreading models
| Model | Meaning | Weakness |
|---|---|---|
| Many-to-One | many user threads → one kernel thread | one blocking call blocks all of them; no parallelism |
| One-to-One | each user thread → its own kernel thread | true parallelism, but creating threads is expensive. Linux, Windows |
| Many-to-Many | many user threads multiplexed onto ≤ that many kernel threads | flexible but hard to implement |
Implicit threading
Concurrency is hard, so hand it to a library: thread pools, fork-join, OpenMP, Grand Central Dispatch. A thread pool also caps the number of threads, which stops a server from being flattened by requests.
Threading issues
- fork() and exec() — does fork duplicate all threads or only the caller? If exec follows immediately, duplicating them all is wasted.
- Signal handling — which thread receives the signal? The one it applies to, every thread, or a dedicated thread.
- Thread cancellation — asynchronous (kill it now; may leave data inconsistent) vs deferred (it checks a cancellation point and exits cleanly). Deferred is safer.
- Thread-local storage — per-thread data, unlike local variables which vanish per call.
5 CPU Scheduling
The whole chapter rests on one observation: process execution is a CPU–I/O burst cycle. A process computes for a while, then waits for I/O, and repeats. The burst distribution is what scheduling exploits — there are many short bursts and few long ones.
When does the scheduler run?
Four moments. A process:
- switches from running → waiting (e.g. an I/O request)
- switches from running → ready (an interrupt)
- switches from waiting → ready (I/O finished)
- terminates
The dispatcher then does the handover: switch context, switch to user mode, jump to the right location. The time this takes is dispatch latency.
The five criteria — and which way is better
| Criterion | Meaning | Want |
|---|---|---|
| CPU utilisation | keep the CPU busy | maximise |
| Throughput | processes completed per time unit | maximise |
| Turnaround time | submission → completion | minimise |
| Waiting time | total time sitting in the ready queue | minimise |
| Response time | submission → first response | minimise |
waiting = turnaround − burst
response = first time on CPU − arrival
Learn those three lines and most scheduling questions become arithmetic. Note waiting time is not "time before it first runs" — it accumulates every time the process is put back in the ready queue.
The algorithms
- FCFS — first come, first served. Simple, nonpreemptive, and vulnerable to the convoy effect: one long process at the front makes everyone wait. Average waiting time depends heavily on arrival order.
- SJF — shortest job first. Provably optimal for minimum average waiting time. The catch: you cannot know the next burst length, so you predict it by exponential averaging of past bursts.
- SRTF — preemptive SJF. A newly arrived shorter job preempts the running one.
- Round Robin — each process gets a quantum q, then goes to the back of the queue. No process waits more than (n−1)q. Large q degenerates to FCFS; tiny q drowns in context-switch overhead. Typically 10–100 ms.
- Priority — highest priority first; SJF is priority scheduling where the priority is the predicted burst. Risk: starvation of low-priority processes. Fix: ageing — raise priority the longer a process waits.
- Multilevel queue — a separate queue per priority, often by process type. Multilevel feedback queue lets processes move between queues, which is how a real OS separates interactive from CPU-bound work automatically.
Beyond one CPU
Multiprocessor: usually symmetric (SMP), each core self-scheduling from a common queue or its own. Watch for load balancing (push and pull migration) and processor affinity — a process prefers the core whose cache is already warm.
Real-time: soft real-time gives critical processes preference; hard real-time guarantees a deadline. Rate-monotonic scheduling assigns priority by period (shorter period = higher priority); earliest-deadline-first assigns it dynamically.
6 Synchronization Tools
Concurrent access to shared data can leave it inconsistent. That is a race condition — the outcome depends on the order in which threads happen to run.
Three machine instructions, not one. Interleave two threads' copies of those three and an increment silently disappears. This is why counter++ is not atomic.
The critical-section problem
Each process has a critical section where it touches shared data. A correct solution must satisfy all three:
- Mutual exclusion — only one process in its critical section at a time.
- Progress — if no one is in the critical section, someone wanting in must get in; the decision cannot be postponed indefinitely.
- Bounded waiting — a limit on how many others may enter before a waiting process gets its turn.
Peterson's solution is the classic software-only proof of the idea, using a shared turn and a flag[] array. It satisfies all three — but it is not guaranteed on modern hardware, because processors reorder reads and writes. It is taught for the reasoning, not for use.
Hardware support
- Memory barriers — force all earlier memory changes to become visible before continuing.
- Atomic instructions —
test_and_set()andcompare_and_swap()do read-modify-write in one uninterruptible step. - Atomic variables — the usable face of the above, e.g. an integer that increments atomically.
The three tools you must be able to compare
| Tool | What it is | Watch out for |
|---|---|---|
| Mutex lock | acquire() / release() around the critical section. Boolean available. | The simple version spins — busy waiting. A spinlock is fine only if the wait is shorter than a context switch. |
| Semaphore | Integer S with wait() and signal(). Counting (any range) or binary (0/1, like a mutex). | Can be used for ordering as well as exclusion. Easy to misuse — swap the operations and you deadlock. |
| Monitor | A language construct: only one process active inside at a time, with condition variables supporting wait() and signal(). | Safer because the compiler enforces it; signal() on a condition with nobody waiting does nothing — unlike a semaphore, which remembers. |
signal() increments and is remembered. A condition variable's signal() is lost if no one is waiting. Getting these the wrong way round is the classic exam error.Liveness — three ways to be stuck
- Deadlock — each waits for an event only the other can cause.
- Priority inversion — a high-priority process waits on a lock held by a low-priority one. Fixed by priority inheritance.
- Starvation — a process waits forever on a semaphore queue.
7 Synchronization Examples
Three classical problems. They exist to test any newly proposed synchronisation scheme — if your primitive cannot express these cleanly, it is not good enough.
1 · Bounded buffer (producer–consumer)
Producers add to a buffer of n slots, consumers remove. Three semaphores:
full = 0 (slots currently filled)
empty = n (slots currently free)
The producer waits on empty and signals full; the consumer does the reverse. Order matters: take mutex after the counting semaphore, never before, or you deadlock holding the lock.
2 · Readers–writers
Many readers may read at once; a writer needs exclusive access. The variants differ in who gets preference:
- First variant — readers preferred. No reader waits unless a writer already holds it → writers can starve.
- Second variant — writers preferred. A waiting writer blocks new readers → readers can starve.
Some systems provide this directly as a reader–writer lock.
3 · Dining philosophers
Five philosophers, five chopsticks, each needs the two beside them. The naive solution — pick up left, then right — deadlocks if all five pick up their left chopstick simultaneously. That is the whole point of the problem, and it is a live demonstration of Chapter 8's four conditions.
Fixes: allow at most four at the table; pick up both chopsticks only if both are free (in a critical section); or make odd philosophers take left-then-right and even ones right-then-left, breaking the circular wait.
8 Deadlocks
A set of processes is deadlocked when every process in the set is waiting for an event that only another process in the set can cause.
The four necessary conditions — all must hold
| Condition | Meaning | Break it by… |
|---|---|---|
| Mutual exclusion | at least one resource is non-sharable | rarely possible — some resources simply cannot be shared |
| Hold and wait | a process holding one resource waits for another | request everything at once, or release all before requesting more |
| No preemption | a resource is released only voluntarily | take resources away from a waiting process |
| Circular wait | P0 waits for P1, P1 for P2, … Pn for P0 | impose a total ordering on resource types and require requests in increasing order |
All four must hold simultaneously — so preventing deadlock means making sure at least one can never happen. In practice, ordering resources to break circular wait is the usable one.
Resource-allocation graph
Processes are circles, resource types are rectangles with a dot per instance. A request edge P→R, an assignment edge R→P.
- No cycle → definitely no deadlock.
- Cycle, one instance per resource type → definitely deadlock.
- Cycle, several instances → possibly deadlock. A cycle alone is not proof.
Four ways to handle it
- Prevention — structurally forbid one of the four conditions.
- Avoidance — require processes to declare their maximum claim in advance, then only ever enter safe states. This is the Banker's algorithm.
- Detection and recovery — let it happen, notice, then recover.
- Ignore it — pretend deadlocks never occur. What UNIX, Linux and Windows all actually do, on the grounds that they are rare and the cost of prevention is high.
Safe state — the idea behind avoidance
A state is safe if there is a sequence of all processes such that each can obtain its remaining need from what is currently available plus what the earlier processes will release. Then:
unsafe → deadlock is possible, not certain
avoidance = never leave the safe set
Banker's algorithm handles multiple instances per resource type using Available, Max, Allocation and Need, where Need = Max − Allocation. A request is granted only if the resulting state is still safe.
Recovery
Abort processes — all deadlocked ones, or one at a time until the cycle breaks. Or preempt resources, which raises three questions: which victim to choose (cost), how far to roll back, and how to avoid always picking the same victim — starvation.
// SOLVERSScheduler & Banker's
Two working implementations, not lookup tables. Type in any process table and the scheduler runs the real algorithm, draws the Gantt chart and computes every column the exam asks for.
CPU scheduling simulator
Edit the table, pick an algorithm, press Run. Times are unitless — use whatever the question uses.
| Process | Arrival | Burst | Priority |
|---|
Worked textbook examples
Every figure below is produced by the simulator above — load the example and check it yourself.
FCFS — order changes everything
Bursts P1=24, P2=3, P3=3, all arriving at 0.
order P2,P3,P1 → waits 6, 0, 3 → average 3
Same processes, same algorithm, 5.7× difference. That is the convoy effect: one long job at the front holds up everyone behind it.
SJF — the optimal one
Bursts 6, 8, 7, 3 all available at 0 → run shortest first: P4, P1, P3, P2.
SJF is provably optimal for average waiting time. The catch is that it needs the next burst length, which cannot be known — only predicted by exponential averaging.
SRTF — preemptive SJF
Arrivals 0, 1, 2, 3 with bursts 8, 4, 9, 5.
waits = (10−1) + (1−1) + (17−2) + (5−3) = 26 → average 6.5
Round Robin, q = 4
Bursts 24, 3, 3.
average waiting 5.67, average turnaround 15.67
Higher average turnaround than SJF, but much better response time — everyone runs within the first 10 units. That trade is the reason RR exists.
Priority
Bursts 10, 1, 2, 1, 5 with priorities 3, 1, 4, 5, 2 (1 = highest).
Note P4 waits 18 units. Push that pattern far enough and low-priority processes starve — the fix is ageing.
Banker's algorithm
Preloaded with the textbook snapshot: 5 processes, 3 resource types A(10) B(5) C(7). Edit any cell and re-run.
Test a request
Safe means deadlock is impossible. Unsafe does not mean deadlocked — only that deadlock has become possible, which is why avoidance refuses to go there.
// TRAININGDrills
Cards to make the definitions automatic, then a mock exam that explains every answer.
Flashcards
Click to flip. Keyboard: Space flip · ←/→ move · G got it · A again.
Mock exam
Single-answer questions grade the moment you click.
// LAST LOOKCheat Sheet
Every formula, list and number on one screen.
Ch 5 The three formulas
waiting = turnaround − burst
response = first CPU time − arrival
Round robin: no process waits more than (n−1)q. Amdahl: speedup ≤ 1/(S + (1−S)/N).
Ch 5 Algorithms at a glance
| Algorithm | Preemptive? | Strength | Weakness |
|---|---|---|---|
| FCFS | no | simple, fair by arrival | convoy effect |
| SJF | no | optimal average wait | burst length unknowable |
| SRTF | yes | optimal with arrivals | more context switches |
| RR | yes | best response time | higher turnaround; q sensitive |
| Priority | either | expresses importance | starvation → fix with ageing |
| MLFQ | yes | separates I/O-bound from CPU-bound automatically | most parameters to tune |
Ch 6 The three requirements
Mutual exclusion · Progress · Bounded waiting. A solution must satisfy all three.
Semaphore signal() is remembered. Condition-variable signal() is lost if nobody waits. That is the classic mix-up.
Ch 8 The four conditions
Mutual exclusion · Hold and wait · No preemption · Circular wait. All four must hold at once — prevention removes any one, and breaking circular wait by ordering resources is the practical route.
Need = Max − Allocation. Safe ⇒ no deadlock. Unsafe ⇒ deadlock possible, not certain. Cycle in an RAG with one instance per type ⇒ deadlock; with multiple instances ⇒ maybe.
Ch 1–4 Fast facts
| Process sections | text · data · heap · stack |
| Process states | new · ready · running · waiting · terminated |
| Threads share | code, data, files — not registers or stack |
| fork() returns | 0 to the child, the child's pid to the parent |
| Kernel structures | monolithic · layered · microkernel · modules |
| Mode bit | 0 = kernel, 1 = user |
// WHO BUILT THISZann
One of five course consoles for this semester. Same shell, same shortcuts, same offline-first rule.
Author
This course
Built with
Vanilla HTML, CSS and JavaScript. No framework, no build step, no dependencies, no network calls. The scheduling simulator and Banker's checker are real implementations — every worked example on this site is produced by running them.