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:

EventFrom → 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 programKernel → 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).

CapabilityWhy the OS needs it
Run privileged CPU instructionsFor example, loading a new page table, or turning interrupts on and off
Access all of memory, including every processTo load programs, copy data between processes, manage memory
Program the MMU and page tablesTo 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 threadsTo share the CPU (Section 3)
Create and destroy processesTo 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:

ForbiddenWhat happens if tried
Run a privileged instructionThe CPU raises an exception; the OS typically ends the program
Read or write another process's memoryPage fault → the OS ends the program
Touch device registers or I/O portsFault
Read or write kernel memoryFault
Change its own privilege levelImpossible
Turn off interrupts or the timerImpossible; 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:

  1. The program calls a normal library function.
  2. The library puts a system call number into a register (for example, "read file") and the arguments in other registers or memory.
  3. It runs a special CPU instruction. On modern 64-bit x86 this is SYSCALL. (Older systems used a software interrupt such as int 0x80 on Linux or int 0x2E on Windows, and 32-bit CPUs had SYSENTER.)
  4. The CPU raises the privilege to Ring 0 and jumps to the fixed entry point the kernel configured at boot.
  5. The kernel looks up the number in its system call table and runs the matching function.
  6. The kernel checks everything: Is the pointer valid? Does this process have permission? Never trust the program.
  7. It does the work, puts the result in a register, and executes a return instruction (SYSRET on 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 thisSyscall behind it
Open a fileCreate or open a file object
Allocate big memoryReserve virtual memory
Start a programCreate a process
Send a network messageWrite to a socket
Wait for a keyWait on an event
Get the timeRead 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:

OperationWhat happensRough cost
Mode switch (a system call: user → kernel → user)Privilege changes; registers saved; the thread keeps running on the same core and same address spaceFairly cheap, on the order of hundreds of nanoseconds up to a microsecond or so
Thread context switchSave one thread's state, load another's; scheduler runsUsually a few microseconds
Process context switchAll of the above plus changing page tables and losing cached translationsMore 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 costExplanation
Cold cachesThe new thread's data is not in the CPU caches (Chapter 2), so it runs slow for a while until caches warm up
TLB flushThe TLB (the page-translation cache, Chapter 3) holds entries for the old process. Many are useless for the new one
Pipeline and branch predictor disruptionThe CPU's guesses about the code were for the old thread
Security fixesAfter 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

TopicRemember this
Privilege levelsx86 has rings 0 to 3; Windows and Linux use Ring 0 (kernel) and Ring 3 (user); ARM calls them exception levels
Kernel modeCan run any instruction, reach all memory and devices; a bug here crashes the whole PC
User modeRestricted; can only use its own memory and must ask the kernel for anything else
System callsThe guarded door: a special instruction jumps to a fixed kernel entry point, which checks and does the work
Switching costMode and context switches take microseconds, plus hidden costs such as cold caches and TLB loss; batching helps