The Big Picture: Two Worlds, One Door
Everything in this chapter depends on one hardware idea: the CPU can run in different privilege levels. Programs live in the weak one. The OS core lives in the strong one. A small, guarded door lets programs ask the strong side for help.
You met this in Chapter 2 as privilege rings. Now we see what the OS does with it.
┌─────────────────────────────────────────────┐
│ USER MODE (Ring 3: low privilege) │
│ Your programs: browser, Word, games │
│ │
│ can: compute, use own memory │
│ cannot: touch hardware, other programs │
└──────────────────────┬──────────────────────┘
│ system call
▼ (the guarded door)
┌─────────────────────────────────────────────┐
│ KERNEL MODE (Ring 0: full privilege) │
│ The kernel and most drivers │
│ │
│ can: everything │
└─────────────────────────────────────────────┘
7.1 Privilege Levels Deep Dive
What it is: A privilege level is a setting inside the CPU that says how much power the currently running code has. The CPU keeps track of it in a few bits of a special register.
x86 rings. The x86 design has four rings, numbered 0 to 3. Ring 0 is the most powerful. Ring 3 is the least.
┌─────────────────────────┐
│ Ring 3 (user programs) │
│ ┌───────────────────┐ │
│ │ Ring 2 (unused) │ │
│ │ ┌─────────────┐ │ │
│ │ │ Ring 1 │ │ │
│ │ │ ┌─────────┐ │ │ │
│ │ │ │ Ring 0 │ │ │ │
│ │ │ │ kernel │ │ │ │
│ │ │ └─────────┘ │ │ │
│ │ └─────────────┘ │ │
│ └───────────────────┘ │
└─────────────────────────┘
In practice, Windows and Linux use only two rings: Ring 0 for the kernel and Ring 3 for everything else. Rings 1 and 2 are not used by mainstream desktop systems, because other CPU families have only two levels, and a design with two levels was easier to port.
Other CPUs use other names. ARM (found in phones and many laptops) calls the levels exception levels: EL0 for applications, EL1 for the OS kernel, EL2 for a hypervisor (software that runs virtual machines), and EL3 for the most secure firmware. The idea is the same.
A level below Ring 0. Modern x86 CPUs also support a hypervisor mode with even more authority than the kernel. When virtualization is on (for example, Windows' Virtualization-based Security features), the hypervisor sits "below" the OS. People sometimes call it "Ring -1". Do not worry about it now; later parts of the course return to it.
How does the CPU know its level? The level is stored in the CPU, and every instruction fetch and memory access is checked against it. The page tables (Section 4.3) also carry a User/Supervisor bit, so the MMU knows which pages user code may touch.
How does the level change? Not by a program writing a number. The level changes only through specific, controlled events:
| Event | From → To |
|---|---|
| A system call instruction (Section 7.4) | User → Kernel |
| An interrupt (Chapter 4) | User → Kernel |
| An exception or fault (Chapter 2) | User → Kernel |
| The kernel returns to the program | Kernel → User |
In each case, the CPU jumps to a place chosen by the OS (a handler address stored in a table the kernel set up at boot). The program cannot choose where it lands in the kernel. That is the key to safety.
7.2 Kernel Mode Capabilities
What it is: What code can do when it runs in kernel mode (Ring 0).
| Capability | Why the OS needs it |
|---|---|
| Run privileged CPU instructions | For example, loading a new page table, or turning interrupts on and off |
| Access all of memory, including every process | To load programs, copy data between processes, manage memory |
| Program the MMU and page tables | To create each process's private space (Chapter 3) |
| Set up the interrupt table (IDT) | To receive interrupts (Section 5.3) |
| Talk to devices directly: I/O ports and memory-mapped registers (Chapter 4) | To run drivers |
| Control the timer and schedule threads | To share the CPU (Section 3) |
| Create and destroy processes | To manage the system |
Power with no safety net. In kernel mode, there is nobody above to catch a mistake. A wild memory write can corrupt the OS itself. That is why a kernel or driver bug often means a full crash: on Windows the blue screen (BSOD), on Linux a kernel panic. The OS stops everything because it can no longer trust its own memory.
For this reason:
- Kernel code is written with great care and tested heavily.
- Windows requires drivers to be digitally signed for 64-bit versions.
- As much code as possible is kept out of kernel mode (Section 8).
7.3 User Mode Limitations
What it is: What ordinary programs cannot do, and how they get around it legally.
In user mode, a program may:
- Run normal CPU instructions: add, compare, jump, read and write its own memory
- Call library functions
- Ask the kernel for services
But it cannot:
| Forbidden | What happens if tried |
|---|---|
| Run a privileged instruction | The CPU raises an exception; the OS typically ends the program |
| Read or write another process's memory | Page fault → the OS ends the program |
| Touch device registers or I/O ports | Fault |
| Read or write kernel memory | Fault |
| Change its own privilege level | Impossible |
| Turn off interrupts or the timer | Impossible; the OS keeps control of the CPU |
Why accept these limits? Because they bring things programs want: a crash that harms only itself, a PC that stays responsive even if one program is stuck in a loop, and a system where malicious code is boxed in.
What do programs use instead? The Windows API (Win32 and newer APIs) or the Linux C library. These are libraries that sit in user mode and package up the kernel's services. CreateFile in Windows or open in Linux are such functions. Inside, they make a system call when a real kernel service is needed.
Some things need no kernel at all. Adding two numbers, or looping over a list in your own memory, runs at full speed with no switch.
7.4 System Calls (The Bridge)
What it is: A system call (or syscall) is the controlled way for a user-mode program to ask the kernel to do something it is not allowed to do itself: open a file, allocate memory, send network data, start a process.
The path of a system call. Take a program that reads a file:
USER MODE KERNEL MODE
───────── ───────────
Program calls ReadFile(...)
│
Library (kernel32 / ntdll) prepares
the request: number + arguments
│
Executes the SYSCALL instruction ───────► CPU switches to Ring 0 and
jumps to the kernel's
entry point
│
Kernel checks: valid number?
valid arguments? permission?
│
Does the work (file system,
driver, disk...)
│
Program continues ◄─────────────────────── Kernel returns the result
with the result and switches back to Ring 3
Step by step:
- The program calls a normal library function.
- The library puts a system call number into a register (for example, "read file") and the arguments in other registers or memory.
- It runs a special CPU instruction. On modern 64-bit x86 this is
SYSCALL. (Older systems used a software interrupt such asint 0x80on Linux orint 0x2Eon Windows, and 32-bit CPUs hadSYSENTER.) - The CPU raises the privilege to Ring 0 and jumps to the fixed entry point the kernel configured at boot.
- The kernel looks up the number in its system call table and runs the matching function.
- The kernel checks everything: Is the pointer valid? Does this process have permission? Never trust the program.
- It does the work, puts the result in a register, and executes a return instruction (
SYSRETon x86-64) to go back to Ring 3.
On Windows, programs normally do not call the kernel directly. They call the Win32 API (kernel32.dll, user32.dll), which calls ntdll.dll, which executes the syscall. Microsoft changes system call numbers between versions, so programs go through these libraries and never hard-code the numbers. On Linux, the C library does the same.
Some numbers (about): Linux has several hundred system calls. Windows has many hundreds of native ones. The exact count changes with each version, so do not memorise it.
Examples of what goes through a system call:
| You do this | Syscall behind it |
|---|---|
| Open a file | Create or open a file object |
| Allocate big memory | Reserve virtual memory |
| Start a program | Create a process |
| Send a network message | Write to a socket |
| Wait for a key | Wait on an event |
| Get the time | Read the clock (often optimised to avoid a full kernel trip) |
7.5 Context Switching Cost
What it is: Crossing between user and kernel mode, and switching between threads or processes, is not free. Each crossing costs time. Understanding the costs explains many performance tricks.
Two different costs:
| Operation | What happens | Rough cost |
|---|---|---|
| Mode switch (a system call: user → kernel → user) | Privilege changes; registers saved; the thread keeps running on the same core and same address space | Fairly cheap, on the order of hundreds of nanoseconds up to a microsecond or so |
| Thread context switch | Save one thread's state, load another's; scheduler runs | Usually a few microseconds |
| Process context switch | All of the above plus changing page tables and losing cached translations | More than a thread switch |
(All such numbers vary by CPU and OS; treat them as a rough scale: nanoseconds to microseconds, which is tiny, but they happen hundreds of thousands of times per second.)
Hidden costs. The direct work is only part of the price:
| Hidden cost | Explanation |
|---|---|
| Cold caches | The new thread's data is not in the CPU caches (Chapter 2), so it runs slow for a while until caches warm up |
| TLB flush | The TLB (the page-translation cache, Chapter 3) holds entries for the old process. Many are useless for the new one |
| Pipeline and branch predictor disruption | The CPU's guesses about the code were for the old thread |
| Security fixes | After the Meltdown and Spectre flaws, some systems do extra work on mode switches, which made them a little more expensive |
How software reduces the cost:
- Batching: do one big read instead of a thousand tiny ones (fewer system calls).
- Buffering: a program collects data and writes it in one go.
- Fewer, longer-lived threads and thread pools, instead of creating thousands.
- Asynchronous I/O: one thread handles many requests, so there is less switching.
- Affinity: keep a thread on one core so its cache stays warm.
An analogy. Imagine a chef who has to leave the kitchen to ask the manager for ingredients. Every trip takes time. Asking for one egg at a time is silly; one trip for a whole tray is much better. Programs do the same with system calls.
On Windows: In Performance Monitor (perfmon), add the counter System → Context Switches/sec and System Calls/sec. Watch them while you open a big program. A busy PC may show tens of thousands per second. High numbers are not always bad; they matter when combined with low useful work.
Summary
| Topic | Remember this |
|---|---|
| Privilege levels | x86 has rings 0 to 3; Windows and Linux use Ring 0 (kernel) and Ring 3 (user); ARM calls them exception levels |
| Kernel mode | Can run any instruction, reach all memory and devices; a bug here crashes the whole PC |
| User mode | Restricted; can only use its own memory and must ask the kernel for anything else |
| System calls | The guarded door: a special instruction jumps to a fixed kernel entry point, which checks and does the work |
| Switching cost | Mode and context switches take microseconds, plus hidden costs such as cold caches and TLB loss; batching helps |