← All chapters
Chapter 35· 16 min read · illustrated

Case Study: Windows

The same OS goals, reached from a different lineage — and the differences that trip up cross-platform code

We have just spent a whole chapter inside Linux, so you already carry a strong mental model of how a modern kernel schedules threads, manages memory, and exposes files. Windows solves every one of those same problems — but it grew from a completely different family tree, and it made different choices at almost every fork in the road. This chapter is a contrast study: rather than re-teach the concepts, we hold Windows up next to what you already know and ask, at each layer, what did they do differently, and why?

That difference is worth understanding on its own merits, because Windows is not a footnote. It runs the overwhelming majority of the world’s desktops, a huge slice of enterprise servers and Active Directory domains, and nearly all PC gaming. If you ship software to real users, some of them are on Windows, and the moments your code breaks there almost always trace back to one of the design choices below.

The through-line is lineage. Linux is a reimplementation of Unix ideas from the 1970s. Windows NT — the kernel under every modern Windows, from Windows 10 and 11 to Windows Server — descends from Digital’s VMS, brought to Microsoft by David Cutler’s team in the late 1980s. Two different starting points, two different sets of instincts, converging on the same job: turn hardware into a safe, shared, programmable machine.

01

Why study Windows — the other dominant OS

It is easy, coming from a Linux-heavy CS education, to treat Windows as the thing your relatives use and your servers avoid. That instinct will cost you. Windows sits under most of the world’s desktops, a large fraction of corporate back offices — Active Directory, Exchange, the .NET server stack — and effectively all of PC gaming, where the graphics and input stack is a first-class engineering target. The engineer who understands only one OS is guessing every time their software crosses to the other.

The deeper reason to study it is that Windows reaches the same destinations as Linux by a different road, and comparing the two roads teaches you which parts of "how an OS works" are fundamental and which are merely one design’s habit. Both must schedule threads, isolate processes, page memory, and name files — but Linux inherited the Unix reflex that everything should look like a file and a small kernel should do the minimum, while NT inherited VMS’s reflex toward a richly-typed object model and a layered kernel with clear internal seams.

Windows NT
The kernel and OS architecture underneath all modern Windows (client and Server); "NT" is the lineage, not an old product.
VMS heritage
NT’s design DNA came from Digital’s VMS via David Cutler’s team — a layered, object-oriented, enterprise-minded tradition distinct from Unix.
Where it dominates
Consumer desktops, enterprise/Active Directory environments, and PC gaming — three markets you cannot ignore as a shipping engineer.

How to read this chapter: For every topic, keep Linux (ch34) in one hand and Windows in the other. The value is in the diff — same problem, different answer, and the reason for the difference.

Tap to enlarge
02

NT architecture — the layered, hybrid kernel

Recall the Linux picture: one large monolithic kernel where the scheduler, memory manager, file systems, and drivers all live in a single privileged address space and call each other as ordinary functions (ch4, ch34). NT is drawn differently. It is a layered, hybrid design with deliberate internal seams — it keeps most services in kernel mode for speed, like a monolith, but organises them into named components with defined interfaces, like a microkernel. Hence "hybrid".

From the bottom up: the HAL (Hardware Abstraction Layer) sits directly on the hardware and hides the differences between chipsets, interrupt controllers, and timers, so the rest of the kernel is written against a clean, uniform machine. Above it sits the microkernel — confusingly just called "the Kernel" — a small layer responsible for the rawest primitives: thread scheduling, interrupt and exception dispatch, and multiprocessor synchronisation. Above that is the Executive, the broad band where most of the OS actually lives.

The Executive is a collection of cooperating managers, each owning one domain: the Object Manager (section 05), the Memory Manager, the Process Manager, the I/O Manager that drives the layered driver stack, the Security Reference Monitor that enforces access checks, and more. This is the VMS instinct showing through — a clean division of labour with names on the boxes — where Linux would simply have subsystems compiled into one blob without such formal internal boundaries.

The last piece is what makes Windows Windows: environment subsystems in user mode. Historically NT could present more than one "personality" to applications — a Win32 subsystem, a POSIX subsystem, an OS/2 one — each a user-mode server plus DLLs that translate its API onto the native NT calls below. Win32 long ago became the dominant (effectively mandatory) personality, but the architecture that allowed multiple APIs on one kernel is exactly what later made WSL and other subsystems feasible (section 07).

HAL
Hardware Abstraction Layer — isolates chipset/platform differences so the kernel above is hardware-neutral. Linux achieves the same via arch-specific code rather than a single named layer.
Kernel (microkernel)
The lowest software layer: thread scheduling, interrupt/exception dispatch, and CPU synchronisation primitives.
Executive
The bulk of the OS: cooperating managers (Object, Memory, Process, I/O, Security) with defined interfaces — the layered heart of NT.
Environment subsystem
A user-mode "personality" (Win32, historically POSIX/OS-2) that maps a public API onto native NT calls; the seam that later enabled WSL.
Hybrid kernel
Monolithic in practice (services run in kernel mode for speed) but organised with microkernel-style internal structure and layering.

The contrast in one line: Linux: one big kernel, "everything is a file", minimal internal ceremony. NT: a layered kernel of named managers, an object model, and swappable user-mode personalities on top. Same job, opposite aesthetic.

Tap to enlarge
03

Processes & threads — and why there is no fork

On both systems the thread, not the process, is the unit the scheduler actually runs — a process is really just a container: an address space, a handle table, a security token, and one or more threads inside it. That much matches your Linux model. The creation story, however, is where the lineage split is starkest.

Linux creates a new process by cloning the caller. fork() (built on the clone syscall, ch8) duplicates the parent — same code, same open descriptors, a copy-on-write copy of memory — and then the child typically calls exec() to replace itself with a new program. Windows has no fork. The primitive is CreateProcess(), which builds a fresh, empty process from scratch and loads a named executable into it in one call. You do not inherit the parent by default; you explicitly opt in to passing along handles, environment, and working directory through the call’s many parameters.

This is not a minor API quirk — it reflects a genuine philosophical difference, and it has real consequences. The Unix fork/exec pattern makes it trivial to tweak the child’s environment between the two calls (redirect a descriptor, drop privileges) and underlies the elegance of shell pipelines. The Windows model avoids ever duplicating a whole address space, which fits a threads-first world, but it is why porting fork-heavy Unix code to Windows is painful, and why tools like Cygwin have to emulate fork awkwardly and slowly.

Windows adds a grouping primitive Linux only later matched with cgroups: the job object. A job groups one or more processes so you can impose shared limits and accounting on them together — total CPU time, memory, active process count — and kill the whole group atomically. It is how sandboxes, CI runners, and Windows containers cap and clean up a subtree of processes.

Scheduling is priority-based and preemptive. Every thread has a priority on a 0–31 scale (higher wins); the scheduler always runs the highest-priority ready thread and preempts a lower one the instant a higher becomes runnable. To keep interactive apps responsive it applies dynamic boosts — for example, nudging a thread’s priority up when the I/O it waited on completes, then letting it decay. The goals are exactly Linux’s (fairness plus snappy foreground apps); the mechanism is a strict priority ladder with boosts rather than Linux CFS’s virtual-runtime bookkeeping.

Listing processes and threads from PowerShell — the Windows analogue of ps.powershell
# One line per process, with the owning process id
Get-Process | Select-Object Name, Id, Threads

# Or the classic command-prompt tool
tasklist
CreateProcess()
Builds a new, empty process and loads an executable into it in one step. No parent clone — the opposite of fork().
No fork()
Windows never adopted address-space duplication as a primitive; a VMS-lineage choice that makes fork-based Unix code hard to port.
Thread = scheduled unit
Same as Linux: the process is a container; threads are what the CPU actually runs.
Job object
Groups processes to enforce shared limits (CPU, memory, count) and terminate them together — Windows’ answer to Linux cgroups.
Priority scheduler
Preemptive, 0–31 priority levels, always runs the highest-ready thread, with dynamic boosts for responsiveness.

Cross-platform gotcha: If a codebase leans on fork() — pre-forking web servers, copy-on-write memory tricks, os.fork() in Python — assume it will not port cleanly to native Windows. It is the single most common source of "works on Linux, mysterious on Windows".

Tap to enlarge
04

Memory management — same ideas, different knobs

Everything you learned about virtual memory in Part D applies to Windows essentially unchanged: each process gets its own private virtual address space, the MMU and page tables translate virtual pages to physical frames on demand, and cold pages are evicted to disk when RAM runs short. The concepts are identical — what differ are the names and the tuning knobs, which is exactly where a Linux engineer gets briefly lost reading a Windows performance dashboard.

The biggest naming difference is the swap area. Linux pages out to a dedicated swap partition or swap file; Windows pages to a page file, normally C:\pagefile.sys, managed by the Memory Manager. Same role, different name and default location. When Windows says a machine is "committing" memory, it means the system has promised backing store (RAM plus page file) for pages the process reserved — a two-step reserve-then-commit model that is more explicit than the typical Linux allocate-and-hope-you-touch-it overcommit behaviour.

The concept with no exact Linux name is the working set: the set of a process’s pages currently resident in physical RAM. It is close to what Linux tools call RSS (resident set size), but Windows treats it as an actively managed quantity — the Memory Manager grows a process’s working set when memory is plentiful and trims it (writing pages back and reclaiming frames) when the system is under pressure, balancing working sets across all processes. Page replacement, then, is framed around per-process working sets rather than Linux’s more global LRU-ish reclaim.

Page file (pagefile.sys)
Windows’ backing store for paged-out memory — the equivalent of the Linux swap partition/file.
Working set
The pages of a process resident in RAM right now; roughly Linux’s RSS, but actively grown and trimmed by the Memory Manager.
Reserve vs commit
Windows separates reserving address-space range from committing (guaranteeing) backing store — more explicit than Linux overcommit.
Trimming
Reclaiming frames from a process by shrinking its working set under memory pressure — Windows’ page-replacement mechanism.

Reading the dashboard: When you see "Commit Charge", "Working Set", and "Page File" in Task Manager, translate: committed memory ≈ promised RAM+swap, working set ≈ RSS, page file ≈ swap. The underlying virtual-memory machine (Part D) is the same one.

Tap to enlarge
05

The Object Manager, handles & the registry

Here is the most characteristically NT idea of all, and the cleanest contrast with Unix. Unix’s famous slogan is "everything is a file": you open a file, a device, a pipe, or a socket and get back a small integer file descriptor, and a handful of calls (read, write, close) work on all of them. Windows generalises that idea one level up: everything is an object. Files, processes, threads, synchronisation primitives (events, mutexes, semaphores), registry keys, timers, and more are all kernel objects, created and tracked by the Executive’s Object Manager.

You never touch an object directly. When you create or open one, the kernel returns a handle — an opaque, per-process token (an index into your process’s private handle table) that stands in for the object. This is the direct analogue of a Unix file descriptor, but broader: the same handle mechanism, the same close call (CloseHandle), and the same reference-counting-until-last-handle-closes lifecycle apply uniformly to a file, a thread, and a mutex alike. The Object Manager also maintains a single named hierarchy for objects, so things like devices and shared events can live at paths in a unified namespace.

Because objects are typed and centrally managed, the kernel gets uniform machinery for free: every open goes through one security check (the Security Reference Monitor validates your token against the object’s ACL — section 06), lifetimes are handled by one reference-counting scheme, and tools can enumerate every handle in the system. That uniformity is the payoff for the extra structure, and it is very much the VMS/layered instinct rather than the Unix minimalism.

The other Windows-defining store is the registry: a single, hierarchical, transactional database of system and application configuration, organised into hives (HKEY_LOCAL_MACHINE for machine-wide settings, HKEY_CURRENT_USER for the logged-in user, and others) whose keys and values hold everything from driver settings to app preferences. Contrast this with the Unix world’s scattered plain-text config — /etc for the system, dotfiles in your home directory for apps. Centralised and queryable versus distributed and grep-able: another values-difference, with real trade-offs in tooling, backup, and corruption blast-radius.

Object Manager
The Executive component that creates, names, secures, and reference-counts every kernel object in a unified namespace.
Kernel object
A typed kernel entity — file, process, thread, event, mutex, timer, registry key — the unit Windows manages uniformly.
Handle
An opaque per-process token (index into the handle table) referring to an object; the broader cousin of a Unix file descriptor.
"Everything is an object"
Windows’ generalisation of Unix’s "everything is a file" — one create/handle/close model across many resource types.
Registry
A central hierarchical config database (hives like HKLM, HKCU) — the counterpart to Unix’s scattered /etc files and dotfiles.

The mental swap: When you read Windows code and see HANDLE and CloseHandle everywhere, map it to fd and close from ch28 — then remember it applies to threads and mutexes too, not just files. Leaking handles is the Windows version of leaking file descriptors.

Tap to enlarge
06

NTFS — the MFT, journaling, ACLs & streams

Windows’ native file system is NTFS, and its heart is the Master File Table (MFT). Where a Unix file system keeps an array of inodes (ch28), NTFS keeps the MFT: a table with one record per file and directory. But the MFT record is more all-encompassing than an inode — it holds the file as a set of attributes (its name, timestamps, security descriptor, and data), and for very small files the data itself lives resident inside the MFT record, so a tiny file needs no separate data block at all. Larger files store their data in runs of clusters that the record points to.

NTFS is journaling, the same crash-consistency idea we met with ext4 and databases (ch30). Before it changes critical metadata it writes the intent to a log ($LogFile); after a crash it replays or rolls back the log so the file-system structures are never left half-updated. This is precisely the write-ahead-logging idea you will recognise from durable storage — a shared solution arrived at independently, because the problem (a power cut mid-update) is universal.

Security is where NTFS diverges sharply from Unix. Recall Unix permissions (ch28): three permission sets — owner, group, other — each with read/write/execute bits. Compact, but coarse. NTFS uses ACLs (access control lists): every file and directory carries a list of entries (ACEs), each naming a user or group and precisely allowing or denying a fine-grained set of rights (read, write, delete, take ownership, change permissions, and more). This is far more expressive than rwx — you can grant one specific user delete but not write — at the cost of being more complex to reason about. It is the model enterprise and Active Directory environments depend on.

A quirk worth knowing is alternate data streams (ADS): an NTFS file can hold more than one stream of data. The normal contents are the unnamed default stream, but additional named streams can hang off the same file, invisible to a normal directory listing. Windows uses this for real features — the "downloaded from the internet" mark that triggers a security warning is the Zone.Identifier stream — but because the extra streams are hidden, malware has also abused them, and naive backup or file-copy code that assumes one stream per file can silently lose data.

Alternate data streams are real and inspectable from PowerShell.powershell
# List every stream attached to a downloaded file
Get-Item .\installer.exe -Stream *

# The Zone.Identifier stream is why Windows warns "this file came from the internet"
Get-Content .\installer.exe -Stream Zone.Identifier
MFT
Master File Table — one record per file/directory; the NTFS counterpart to the Unix inode array, but attribute-based and able to store tiny files inline.
Journaling ($LogFile)
Logs metadata intent before applying it so a crash can be replayed/rolled back — the same write-ahead-log idea as ext4 and databases (ch30).
ACLs vs rwx
NTFS grants fine-grained per-user/per-group allow/deny rights via access control lists; far more expressive (and complex) than Unix owner/group/other rwx (ch28).
Alternate data streams
One NTFS file can carry multiple named data streams; used for the internet-zone mark, hidden from normal listings, sometimes abused.

Callback to durability: NTFS journaling is the file-system twin of the database write-ahead log from ch30. Different vendor, different decade, identical insight: never overwrite in place without first recording what you intend to do, so a crash mid-write is always recoverable.

Tap to enlarge
07

Windows for engineers & the big picture

Bring this back to your keyboard. The API almost all Windows software targets is Win32 (functions like CreateProcess, WriteFile, CreateFileW) — the dominant environment subsystem from section 02. NT once shipped a POSIX subsystem too, and later a fuller "Subsystem for UNIX-based Applications", so that Unix-flavoured code could run more natively; those were always second-class and are now historical. The lesson is that Windows’ layered-personality architecture always made "run another OS’s programs" a design goal, not an afterthought.

That lineage pays off today in WSL, the Windows Subsystem for Linux, which lets you run a real Linux userland on Windows. It comes in two very different generations, and knowing which you are on matters. WSL1 was a translation shim: a subsystem that intercepted Linux syscalls and reimplemented them on top of the NT kernel — clever, but imperfect where Linux and NT semantics diverge. WSL2 took the honest route: it runs a genuine Linux kernel inside a lightweight, tightly-integrated Hyper-V virtual machine (tying back to the virtualization chapter, ch31), giving full Linux syscall compatibility at the cost of VM boundaries around the file system and network.

For cross-platform code, a short list of Windows differences causes most of the pain, and every one traces back to a design choice in this chapter. Keep this checklist:

  • Paths: Windows uses backslashes and drive letters (C:\Users\me), Unix uses forward slashes rooted at /. Always use your language’s path library (os.path, pathlib, filepath.Join) instead of hand-concatenating separators.
  • Line endings: Windows tooling defaults to CRLF (\r\n), Unix to LF (\n). Configure Git’s autocrlf and your editor, or scripts and diffs break in confusing ways.
  • Filename case: NTFS preserves case but is case-insensitive by default, so File.txt and file.txt collide — code that relies on case-sensitive names (common on Linux) can fail only on Windows.
  • No fork(): as in section 03, fork-based process patterns need a rewrite around CreateProcess or a higher-level spawn abstraction.
  • Handles, not just fds: resource leaks show up as leaked HANDLEs; ACLs, not rwx, govern file access — permission bugs look different.

Step back to the big picture. Windows and Linux answer the same questions this course has asked throughout — how to schedule threads, isolate processes, page memory, name and protect files — and they reach workable answers from opposite temperaments: Unix minimalism and "everything is a file" versus VMS-lineage layering, a typed object model, and ACL-based security. Neither is "correct"; each is coherent. Understanding both is what lets you reason about a system instead of memorising one vendor’s vocabulary.

What is next: With two full operating systems in view, the remaining question is how to make either one fast. Chapter 36 turns to performance: measuring where the time actually goes, and applying everything from scheduling to memory to I/O to make real systems quick.

Tap to enlarge