Skip to content

Level 4 · Chapter 4.8

Inside UNIX and Windows

The two families of operating systems that run nearly everything: the history from UNIX to Linux, BSD and macOS and from MS-DOS to Windows NT, monolithic, hybrid and Mach-based kernels, POSIX next to Win32 and the NT native API, processes and threads counted on real systems, memory, files and security models compared, and where each runs today.

Two families of operating systems cover almost everything. Every phone, server, laptop and desktop in use today runs a descendant of one or the other: Linux and Android, macOS and iOS on the UNIX side, Windows on the other. This chapter compares them (history, structure, system calls, memory, files, processes) with current systems and real measurements.

Two family trees

UNIX was written at Bell Labs from 1969 by Ken Thompson and Dennis Ritchie, first in PDP-7 assembly, then, from 1973, in C, the language Ritchie created for it. AT&T licensed it cheaply to universities with its source code, and the University of California at Berkeley turned it into BSD, adding paged virtual memory, long file names and the TCP/IP networking that the internet grew up on. AT&T's own line became System V. The split between the two made UNIX software hard to port until the POSIX standard defined a common interface.

From there the tree branches into today's systems:

  • In 1987, MINIX, a small UNIX-like system for teaching, was published. A Finnish student who studied it, Linus Torvalds, started writing his own kernel in 1991: Linux. Combined with the GNU project's tools, it became a complete system, and later the kernel of Android.
  • BSD lives on in FreeBSD, NetBSD and OpenBSD, and in Apple's systems: NeXT's NeXTSTEP combined the Mach kernel with BSD, and when Apple bought NeXT it became Mac OS X in 2001, now macOS, iOS and their siblings. Apple has had macOS certified against the UNIX 03 standard since 2007, so it is officially a UNIX; Linux isn't certified, but behaves like one.

Windows has two lines. The first, MS-DOS and the Windows 3.x, 95, 98 and Me built on it, is extinct. The second is Windows NT, a new 32-bit system designed by a team led by Dave Cutler, who came from DEC's VMS, and released in 1993. Windows 2000, XP, Vista, 7, 8, 10 and 11 are all NT; Windows 10's support ended in October 2025, leaving Windows 11. NT was portable from the start (the first versions ran on x86, MIPS and Alpha), and today it runs on x86-64 and ARM64.

The systems identify themselves. On the Mac this chapter was written on:

$ sw_vers
ProductName:    macOS
ProductVersion: 26.6.2
BuildVersion:   25G83
$ sysctl kern.version
kern.version: Darwin Kernel Version 25.6.0: Fri Jul 31 19:18:48 PDT 2026; root:xnu-12377.161.14~5/RELEASE_ARM64_T6020

and in the Linux virtual machine on the same computer:

$ cat /proc/version
Linux version 6.12.76-linuxkit (root@buildkitsandbox) (gcc (Alpine 15.2.0) 15.2.0, GNU ld (GNU Binutils) 2.45.1) #1 SMP Thu Apr 30 11:19:05 UTC 2026

macOS's kernel is Darwin's XNU, here build 12377; the Linux kernel is version 6.12, built with GCC 15.2.

Three kernel structures

The classic picture of UNIX is a single kernel containing the file system, the block cache, device drivers, process management, IPC, scheduling and memory management, with the shell and all programs outside it. Linux is still built that way: a monolithic kernel, one large program running in kernel mode, in which every part can call every other part directly. It isn't rigid: drivers and file systems can be compiled as loadable modules and added to the running kernel. But once loaded, a module is as privileged as the rest, and a bug in any driver can crash the whole system.

Windows NT is called a hybrid. Its structure: a hardware abstraction layer at the bottom; above it the kernel proper (trap handling, scheduling, synchronization) and the executive (processes and threads, virtual memory, the object manager, the I/O manager, the cache manager, the security reference monitor), together in ntoskrnl.exe; drivers beside them, all in kernel mode. NT's early design kept more in user mode, closer to a microkernel; NT 4.0 moved the window manager and graphics into the kernel, as win32k.sys, for speed.

XNU is also a hybrid, of a different kind: the Mach microkernel from Carnegie Mellon, which provides tasks, threads, virtual memory and message ports; a BSD layer on top of it for processes, the POSIX system calls, files and networking; and I/O Kit, a driver framework in a restricted C++. Unlike a real microkernel, all three run together in kernel mode. The one kernel design none of the three adopted is the true microkernel, with drivers and file systems as separate user-mode processes, which is what MINIX 3 does.

System call interfaces

UNIX's system calls are small in number and documented: POSIX's first part defined about 60, and the UNIX systems of the early 2010s had about 200; Linux on 64-bit ARM now numbers its calls up to 450. Programs may call them directly, and Linux keeps their numbers stable forever, as the system calls chapter described. In the simulator, which follows the x86-64 Linux numbering:

Live · A Linux process asks who it is

Try it: Press Step to run one instruction, Run to animate or Continue to finish; the L2–L7 buttons zoom in and out one level at a time.

program· ▸ is the next instruction
  1. mov eax, 39 ; getpid(): number 39 on x86-64 Linux
  2. syscall
  3. mov ebx, eax
  4. mov eax, 110 ; getppid(): number 110
  5. syscall
  6. mov r12d, eax
  7. mov eax, 60 ; exit(0)
  8. xor edi, edi
  9. syscall
step 0
Loading emulator…
The process as the operating system sees it: an address space of code, globals, heap and stack.

getpid returns 1000 and getppid returns 1: the simulated process's parent is init. The same calls have different numbers on every system, even on the same processor family:

callx86-64 LinuxARM64 LinuxmacOS (BSD calls, both architectures)
write1644
getpid3917220
getppid11017339
execve5922159

macOS's numbers are BSD's, dating back decades: fork is 2 and exit 1. Its SDK's sys/syscall.h defines 459 of them, numbered up to 557, and XNU has a second set, the Mach traps, for Mach's own services. But Apple, unlike Linux, supports only calls made through its system library.

Windows goes further. The documented interface is the Win32 API (now covering 64-bit Windows too), a large library of functions in DLLs such as kernel32.dll. Its philosophy is the opposite of UNIX's minimal set: Win32 is comprehensive, often with several ways to do one thing, and includes functions that aren't system calls at all, like CopyFile. Below it is the native API of ntdll.dll (NtCreateFile, NtWriteFile), which makes the actual system calls, and whose numbers change between Windows builds. A Windows program never sees them. Compiled for Windows, opening a file looks like this:

open_it:
    sub   rsp, 0x38
    mov   qword ptr [rsp + 0x30], 0x0      ; 7th argument: template file
    mov   dword ptr [rsp + 0x28], 0x0      ; 6th: flags and attributes
    mov   dword ptr [rsp + 0x20], 0x3      ; 5th: OPEN_EXISTING
    mov   edx, 0x80000000                  ; 2nd: GENERIC_READ (1st, the name, is already in rcx)
    xor   r8d, r8d                         ; 3rd: no sharing
    xor   r9d, r9d                         ; 4th: default security
    call  qword ptr [rip + __imp_CreateFileW]

Seven arguments, four in registers (rcx, rdx, r8, r9 in the Windows calling convention) and three on the stack, and an indirect call through the import table, which the loader fills with the address of CreateFileW in kernel32.dll. That function will call NtCreateFile in ntdll.dll, which executes syscall. The UNIX equivalent, open(name, O_RDONLY), takes two arguments.

Where UNIX has file descriptors, Windows has handles: numbers that name kernel objects of every kind (files, processes, threads, events, mutexes, registry keys) through a per-process handle table, with a security descriptor on each object. UNIX's "everything is a file" has a Windows counterpart: everything is an object.

Processes and threads

UNIX creates processes with fork and exec, described in the processes chapter. Windows creates a process and loads its program in one call, CreateProcess, with ten parameters. Windows keeps no parent-child hierarchy, although the handle a creator receives gives it control over the child.

The thread models differ in form more than in substance. In NT, a process is a container (an address space, a handle table, a security token), and threads are what the scheduler runs. Linux has no separate thread object: each thread is a task, like a process, created by clone with flags that share the address space, files and signal handlers, and a "process" is a group of tasks sharing one ID. XNU follows Mach: a task holds the address space and resources, threads run in it, and a BSD process structure sits on top of each task.

Counting them shows how much each system runs. On the Mac, top reported 1,240 processes and 10,462 threads: the desktop, its services and the applications in use. In the Linux VM behind Docker, a listing of every process showed 303, of which 288 were kernel threads (the children of kthreadd: per-CPU workers, RCU callbacks, I/O helpers), and only about a dozen user-space programs: /initd (the VM's init, with 12 threads), containerd, dockerd, a few RPC daemons and a login. With threads counted, there were 428 tasks. A server VM can be almost empty; a desktop OS never is.

Memory

UNIX memory is classically described as three segments per process (code, data growing up, stack growing down), with mmap for mapping files. That's still the core, though a real address space, as /proc/self/maps shows, is dozens of regions: libraries, heap, thread stacks, anonymous mappings. Windows separates reserving address space from committing memory to it: VirtualAlloc can reserve a large range without charging anything against memory, then commit pages as needed, and MapViewOfFile maps files. Both demand-page, both copy on write, both use the mechanisms of the virtual memory chapter.

64-bit Windows processes had 8 TB of user address space up to Windows 8; since Windows 8.1, it has been 128 TB, the same as Linux on x86-64.

Files

The classic pair was the UNIX file system, with 64-byte inodes, and NTFS, with its master file table. Today it's ext4 or XFS on Linux, APFS on Apple's systems, and still NTFS on Windows, as the files chapter describes. The naming models still differ. UNIX has one tree starting at /, with every disk mounted somewhere in it; Windows keeps drive letters, C:\, with backslashes inherited from MS-DOS, although NTFS can also mount volumes into folders. NTFS can store names that differ only in case, but Win32 treats FOO and foo as the same file; macOS's APFS volumes are case-insensitive by default too, while Linux file systems are case-sensitive.

Security models

In UNIX's model, each file has an owner, a group, and three sets of read, write and execute bits, and each process runs as a user and groups; the superuser, root, bypasses the checks. Real systems have added layers on top: access-control lists, Linux's capabilities (which split root's power into pieces), mandatory policies like SELinux and AppArmor, and on macOS System Integrity Protection, which keeps even root from modifying the system, plus per-application sandboxes and privacy permissions.

Windows's model is richer from the start: each process carries an access token with the user's SID, its groups and its privileges, and each object carries a security descriptor with an ACL, whose entries allow or deny specific operations to specific SIDs. Vista added integrity levels, which keep, for example, a browser's low-integrity process from writing to normal files even as the same user, and User Account Control, which gives administrators a limited token until they approve elevation.

Windows's classic synchronization calls are mostly still valid, with one exception: PulseEvent, meant to release all threads waiting on an event, is documented by Microsoft today as unreliable and not to be used.

Where they run today

  • Linux runs most servers and nearly all of the cloud, every one of the 500 fastest supercomputers (true of every TOP500 list since November 2017), and, as Android's kernel, most of the world's phones. It also runs inside the other two: in Docker's VM on this Mac, and in WSL 2 on Windows, which since 2019 has run a real Linux kernel in a lightweight Hyper-V virtual machine.
  • Windows remains the operating system of most desktop and laptop PCs, and of many corporate servers.
  • XNU, as macOS, iOS, iPadOS and their variants, runs on all of Apple's devices.

UNIX and Windows differ more in philosophy than in capability. Both have demand-paged virtual memory, preemptive threads, rich file systems, access control and hardware virtualization. The differences are at the interfaces: a small, stable set of system calls against a vast library over unstable ones; file descriptors against handles; fork against CreateProcess.

Takeaways

  • UNIX (1969) led to BSD, System V and POSIX, then to Linux (1991) and, through NeXTSTEP, macOS and iOS. Windows NT (1993) is the base of every current Windows.
  • Linux is a monolithic kernel with loadable modules; NT a hybrid of HAL, kernel, executive and drivers in kernel mode; XNU combines Mach, BSD and I/O Kit, all in kernel mode.
  • UNIX offers a small set of documented system calls, with different numbers on each system (getppid is 110, 173 or 39). Windows programs use the Win32 API, which calls the native API in ntdll.dll, whose system call numbers change between builds.
  • Linux threads are tasks created by clone; NT and XNU separate the process (or task) from its threads. This Mac ran 1,240 processes and 10,462 threads; the Linux VM 303 processes, 288 of them kernel threads.
  • UNIX names things with file descriptors, Windows with handles to kernel objects. Security is user, group and permission bits plus added layers on UNIX, tokens, SIDs and ACLs on Windows.
  • Linux dominates servers, supercomputers and phones; Windows, PCs; XNU, Apple's devices.

In this level

  1. 4.1Executable files: ELF, PE and Mach-O
  2. 4.2Processes and the address space
  3. 4.3System calls and privilege levels
  4. 4.4Virtual memory and paging
  5. 4.5Files, devices and I/O
  6. 4.6Threads and synchronization
  7. 4.7Hardware virtualization and hypervisors
  8. 4.8Inside UNIX and Windows