Table of Contents
You use an operating system every day. But do you know what actually happens between pressing the power button and your first process running?
Fireship’s video compresses every major OS concept into about 15 minutes, using the boot-to-shutdown lifecycle as a narrative backbone — turning what textbooks spread across dozens of chapters into a single coherent story. This post follows the same arc.
TL;DR
An operating system is the software layer between hardware and applications, managing CPU, memory, storage, and I/O. The best way to understand it is to follow a computer from power-on to shutdown — each phase maps to a specific OS subsystem.
What It Is
An operating system intermediates between applications and hardware. Its four core responsibilities:
- Resource management — who gets CPU time, how much memory, which disk blocks
- Isolation — one crashing program shouldn’t take down the system
- Abstraction — applications don’t need to know which CPU model or disk format they’re running on
- Interface — standard APIs (syscalls, file system, network) that applications can target
Why It Matters
Without an OS, every application would need to handle CPU scheduling, memory allocation, and hardware drivers itself — a practically impossible requirement. The OS absorbs that complexity so developers can focus on application logic.
Understanding OS internals makes you a better systems programmer: you can reason about why fork() is cheap but exec() is expensive, why many small files are slower than one large file, why context switches create performance bottlenecks.
How It Works: Boot to Shutdown
Firmware
When you press the power button, the first thing the CPU executes isn’t Linux or Windows — it’s firmware, a low-level program burned into a chip on the motherboard.
Modern machines use UEFI (Unified Extensible Firmware Interface); older machines use BIOS. Firmware’s job:
- POST (Power-On Self Test): verify CPU, RAM, and storage are functional
- Initialize hardware devices
- Locate the bootloader and hand over control
UEFI is a significant upgrade over legacy BIOS: it supports disks over 2TB (GPT partition tables), provides a graphical interface, and enables Secure Boot.
Bootloader
Firmware finds the bootloader on the boot disk (GRUB on Linux systems) and hands control to it. The bootloader’s only job:
- Read the OS kernel image from disk
- Load the kernel into RAM
- Transfer control to the kernel
This takes a few seconds and is the bridge between the firmware world and the operating system world.
Kernel
The kernel is the OS core, running in kernel mode with direct access to all hardware. It owns everything that follows:
Kernel
├── Process Management
├── Memory Management
├── File System (VFS)
├── Device Drivers
└── System Call Interface
Linux is a monolithic kernel — all subsystems share one memory space, maximizing call efficiency. macOS’s XNU is a hybrid kernel, running some components in user space for stability.
Process Management
Once the kernel starts, it begins creating processes. Each process is an independent execution instance with its own:
- Virtual address space
- Open file descriptors
- Process ID (PID)
- At least one thread
Processes are isolated from each other — a crash in one process doesn’t directly kill others. Threads are the execution units within a process; all threads in the same process share memory, making them efficient for cooperative parallel work (but requiring locks to prevent data races).
CPU Scheduling
A machine might have dozens of processes “running” simultaneously, but CPU cores are finite. The scheduler decides who runs and for how long:
- Preemptive scheduling: the scheduler can forcibly interrupt a running process and give the CPU to another
- Time slice: each process typically gets a few milliseconds of CPU time per turn
- Priority: real-time tasks (audio playback, input handling) run before background work
Linux uses the CFS (Completely Fair Scheduler), which tracks each process’s “virtual runtime” and always schedules the one that has run the least — preventing any process from starving indefinitely.
Memory Management
Each process sees a virtual address space, not physical memory addresses. The OS uses paging to maintain a mapping table between virtual addresses and physical page frames.
Three benefits of this design:
- Isolation: process A cannot read process B’s memory even on the same machine
- Demand paging: physical page frames are only allocated when memory is actually accessed, speeding up startup
- Swap: when physical RAM is full, the OS evicts infrequently-used pages to disk to free space for active processes
Inter-Process Communication (IPC)
Processes are isolated, but sometimes they need to cooperate. The OS provides several IPC mechanisms:
| Mechanism | Use Case |
|---|---|
| Pipe | Parent-child processes, one-way data flow (cmd1 | cmd2) |
| Unix Socket | Bidirectional local communication |
| Shared Memory | High-throughput data exchange |
| Signal | Lightweight notifications (SIGTERM, SIGKILL) |
| Message Queue | Asynchronous message passing |
File System
To applications, all persistent data is accessed as “files.” The OS provides a unified interface via the Virtual File System (VFS) — the underlying storage can be ext4, APFS, NTFS, tmpfs, or NFS, but the API looks the same to applications.
Linux’s “everything is a file” philosophy exposes hardware devices (/dev/sda), process information (/proc/1234/status), and kernel settings (/sys/) all as readable/writable file paths — one consistent interface for the entire system.
System Calls
Applications run in user mode and cannot directly access hardware or kernel data structures. Any time an application needs OS services (reading a file, opening a socket, spawning a process), it must make a system call that switches to kernel mode:
open() // open a file, get a file descriptor
read() // read data from fd into a buffer
write() // write buffer data to fd
fork() // clone the current process
exec() // replace the current process image with a new program
exit() // terminate the process, release resources
Every syscall involves a user mode → kernel mode context switch, which has measurable overhead. This is why high-performance I/O frameworks like epoll and io_uring are designed explicitly to reduce syscall count.
strace is the go-to tool for intercepting syscalls and seeing exactly what a process is asking the OS to do:
strace -e openat,read,write ls /tmp
Boot to Shutdown at a Glance
graph LR
A[Power Button] --> B[UEFI / BIOS]
B --> C[Bootloader]
C --> D[Kernel]
D --> E[Process Management]
D --> F[Memory Management]
D --> G[VFS / File System]
E --> H[User Processes]
H -->|syscall| D
How OS Differs from VMs and Containers
| Operating System | Virtual Machine | Container | |
|---|---|---|---|
| Kernel | Own kernel | Own kernel | Shares host kernel |
| Isolation layer | Hardware | Hypervisor | cgroups + namespaces |
| Startup time | Seconds | Seconds–minutes | Milliseconds–seconds |
| Overhead | Low | High | Very low |
Containers (Docker, Podman) aren’t “lightweight VMs” — they’re isolated processes sharing the host kernel, using Linux cgroups for resource limits and namespaces for visibility isolation. This is why Docker on macOS and Windows requires a Linux VM underneath: neither OS has a Linux kernel.
Summary
The OS has a clear logical chain: firmware initializes hardware → bootloader loads the kernel → kernel establishes process and memory management → processes access OS services via syscalls.
Each layer solves one specific problem: firmware abstracts hardware variation, the kernel abstracts resource contention, VFS abstracts storage backends, and the syscall interface abstracts privileged mode switching. Understanding this abstraction chain lets you reason about program behavior at the OS level rather than treating it as a black box.
References
Answers come from this article only. Click any prompt below or open the chat at the bottom right.
🇺🇸 English
You use an operating system every single day. But have you ever stopped to think about what actually happens in that gap between pressing the power button and your first program lighting up on screen? There's a whole sequence of events happening in there, and most of us treat it like a black box. Today, we're going to open that box.
There's a Fireship video that pulls off something pretty clever. It takes every major operating system concept — the kind of stuff textbooks spread across dozens of chapters — and compresses it into about fifteen minutes. And the trick that makes it work is the narrative spine: it follows a computer's life from power-on to shutdown. Each stage of that journey maps onto a specific piece of the OS. So that's exactly the path we're going to walk today.
Let's start with the big picture. What even is an operating system? At its heart, it's the software layer sitting between your applications and the raw hardware. And it has four core jobs. First, resource management — deciding who gets CPU time, how much memory each program gets, which chunks of disk go where. Second, isolation — making sure that when one program crashes, it doesn't drag the whole machine down with it. Third, abstraction — so your app doesn't need to know which exact CPU model or disk format it's running on. And fourth, providing an interface — a standard set of APIs that applications can reliably build against.
Now, why does any of this matter? Well, imagine a world with no operating system. Every single app you write would have to handle its own CPU scheduling, its own memory allocation, its own hardware drivers. That's practically impossible. The OS absorbs all that complexity so developers can just focus on their actual application. And once you understand what's happening underneath, you become a genuinely better systems programmer. You start to understand why forking a process is cheap but exec is expensive, why a thousand tiny files are slower than one big file, why context switches quietly become performance bottlenecks. So let's follow the machine from boot to shutdown.
It all starts the instant you press the power button. And here's the first surprise: the very first thing your CPU runs is not Linux, not Windows. It's firmware — a low-level program burned right into a chip on the motherboard. Modern machines use something called UEFI; older ones used BIOS. And firmware has a specific to-do list. It runs the Power-On Self Test, or POST, which is basically the machine checking its own pulse — is the CPU okay, is the RAM there, is the storage responding? Then it initializes the hardware devices, and finally it goes hunting for the bootloader to hand off control. UEFI, by the way, was a big leap over the old BIOS. It handles disks bigger than two terabytes, it gives you a graphical interface, and it enables Secure Boot.
So firmware finds the bootloader sitting on your boot disk — on Linux that's usually GRUB — and passes the baton. And the bootloader has exactly one job, but it's a crucial one. It reads the operating system kernel image off the disk, loads that kernel into RAM, and then transfers control over to it. That's it. It takes a few seconds, and it's really the bridge between the world of firmware and the world of the operating system proper.
And now we arrive at the kernel — the beating heart of the OS. The kernel runs in what's called kernel mode, which means it has direct, unrestricted access to all the hardware. Everything from here on out lives under the kernel's roof: process management, memory management, the file system, device drivers, and the system call interface. Now, there's an interesting design distinction here. Linux is what we call a monolithic kernel — all of those subsystems share one single memory space, which makes calls between them lightning fast. macOS, on the other hand, uses a hybrid kernel called XNU, where some components run out in user space instead, trading a bit of speed for extra stability.
Once the kernel is up, it starts creating processes. Think of a process as an independent instance of a running program. Each one gets its own virtual address space, its own set of open files, its own process ID, and at least one thread of execution. The magic here is isolation — processes are walled off from each other, so if one crashes, it doesn't directly kill the others. Now, within a process you have threads, and these are the actual units of execution. All the threads inside one process share the same memory, which makes them super efficient for working together on a task — but, and this is important, that shared memory means you need locks to stop them from stepping on each other's data.
Here's the thing, though. Your machine might have dozens of processes all claiming to run at the same time, but you've only got a finite number of CPU cores. So who actually gets to run? That's the scheduler's call. Modern schedulers are preemptive, which means they can forcibly pause a running process mid-execution and hand the CPU to someone else. Each process gets a time slice — usually just a few milliseconds per turn. And there's priority in the mix too: real-time stuff like audio playback or handling your keyboard input jumps ahead of background chores. Linux uses something called the Completely Fair Scheduler, and I love how it thinks about this. It tracks how much "virtual runtime" each process has racked up, and it always picks the one that's run the least. The result? No process ever gets starved out and left waiting forever.
Next up, memory. Here's a beautiful bit of illusion: every process thinks it has its own private address space. It's not looking at real physical memory addresses at all. The OS pulls this off with paging — it maintains a mapping table that translates those virtual addresses to actual physical page frames in RAM. And this design buys you three great things. One, isolation — process A physically cannot peek into process B's memory, even on the same machine. Two, demand paging — the OS only hands out physical memory when a program actually touches it, which makes startup faster. And three, swap — when RAM fills up, the OS quietly moves the least-used pages out to disk to make room for what's active right now.
But wait — if processes are all isolated from each other, how do they ever cooperate? That's where inter-process communication comes in, and the OS offers a whole toolbox. There are pipes, which send data one direction between a parent and child process — that's literally what's happening when you pipe one command into another in your terminal. There are Unix sockets for two-way local chatter. There's shared memory when you need to move a lot of data fast. There are signals — lightweight little pokes, like SIGTERM asking a process to please shut down, or SIGKILL, which is the non-negotiable version. And there are message queues for passing messages asynchronously. Different tools for different conversations.
Now let's talk storage. To your applications, everything persistent is just a "file." And the OS keeps that illusion consistent through the Virtual File System, or VFS. Underneath, the actual storage could be ext4, or APFS, or NTFS, or a RAM disk, or a network file system — but the API looks identical to your app no matter what. Linux takes this idea to an almost philosophical extreme with "everything is a file." Your hardware devices show up as file paths. Live information about a running process shows up as a file you can read. Even kernel settings are just files you can write to. One single, consistent interface for the entire system. It's elegant.
Okay, but here's a wall your applications keep running into. They live in user mode, and user mode is not allowed to touch hardware or kernel data structures directly. So any time an app needs the OS to do something real — read a file, open a network socket, spawn a new process — it has to make a system call, which flips the CPU over into kernel mode. These are the workhorses: open to get a handle on a file, read to pull data in, write to push it out, fork to clone the current process, exec to swap in a whole new program, exit to shut down and give back resources. And here's the catch — every single one of those syscalls involves flipping from user mode to kernel mode and back, and that switch has a real, measurable cost. That's the whole reason high-performance I/O frameworks like epoll and io_uring exist. Their entire design goal is to make fewer syscalls. Oh, and if you ever want to spy on exactly what a program is asking the OS to do, there's a tool called strace that intercepts these calls and shows them to you live. Point it at a command and you can literally watch every file it opens and every byte it reads.
So let's zoom out and see the whole arc as one flow. You press the power button. Firmware — UEFI or BIOS — wakes up and checks the hardware. It hands off to the bootloader. The bootloader loads the kernel. The kernel spins up its two great pillars, process management and memory management, plus the file system. Out of that, your user processes come to life. And whenever those processes need something privileged, they reach back down through a syscall into the kernel. That's the entire lifecycle in one clean line.
Before we wrap, there's one comparison worth making, because people get this wrong all the time — how an OS differs from virtual machines and containers. A regular operating system runs its own kernel, isolates programs at the hardware level, boots in seconds, and has low overhead. A virtual machine also runs its own full kernel, but it isolates through a hypervisor, can take seconds to minutes to boot, and carries heavy overhead. A container is the odd one out: it does not have its own kernel — it shares the host's kernel. It isolates using Linux cgroups and namespaces, boots in milliseconds, and has almost no overhead. So here's the myth to kill: containers are not tiny lightweight VMs. They're just isolated processes sharing the host kernel — cgroups cap their resources, namespaces limit what they can see. And that, by the way, is exactly why Docker on a Mac or Windows machine secretly runs a Linux VM underneath. Neither of those systems ships a Linux kernel, and containers need one.
So let me leave you with the three ideas worth holding onto. First: the operating system is not a mysterious black box — it's a clear, logical chain. Firmware initializes the hardware, the bootloader loads the kernel, the kernel establishes process and memory management, and processes reach the OS through syscalls. Second: every single layer exists to solve one specific problem. Firmware abstracts away hardware variation, the kernel abstracts away resource contention, the VFS abstracts away storage backends, and the syscall interface abstracts away the jump into privileged mode. And third, the payoff: once you can see this abstraction chain, you stop treating your computer as magic. You can actually reason about why your program behaves the way it does, right down at the level of the machine. And honestly, that's the difference between someone who just uses a computer and someone who truly understands one.
🇹🇼 中文
此刻你能看這支影片、聽這段內容,其實都是因為某個作業系統決定了「可以」。你的 CPU 同時在跑幾百支程式,Chrome 莫名其妙吃掉一大堆 RAM,但你一晃滑鼠,游標還是順順地跟著跑。這件事聽起來理所當然,但它其實一點都不正常——它是作業系統這個「最被低估的軟體」,每一秒重複上千次的小奇蹟。
今天我想換一個角度來聊作業系統。不從教科書的章節目錄講起,而是跟著一台電腦,從你按下電源鍵、一路到你氣到直接關機,走完這條時間軸。沿路你會看到 bootloader、privilege ring、virtual memory、file system 這些名詞是怎麼協作的。我們挑其中最核心的四個階段,把它講清楚。
先講一點歷史。第一個作業系統叫 GM-NAAIO,是 1956 年 General Motors 做出來的。動機很單純——有工程師覺得,人類不該把時間浪費在手動把一疊打孔卡片,塞進兩層樓高的 IBM 大型主機裡。那個系統一次只能跑一支程式,沒有記憶體保護、沒有使用者、也沒有「檔案」這個概念。但講個好笑的,它當機的次數,還是比後來的 Windows Millennium Edition 少。七十年過去,我們今天終於可以把整套機制拆開來看。
先講階段一,bootloader。
你按下電源鍵,電力打到主機板,CPU 在「最原始的狀態」下醒過來。這個當下,沒有記憶體管理,甚至連檔案的概念都不存在,就只是一顆核心,在韌體裡「寫死的位址」上開始執行指令。
現代機器上,這個韌體叫 UEFI;更老的機器上叫 BIOS。它的工作很單純:喚醒剛好夠用的硬體,找到一顆磁碟,然後把控制權交棒給 bootloader。
不同系統的 bootloader 名字不一樣。Linux 上叫 GRUB,Mac 上叫 iBoot,Windows 上叫 Bootmgr。但它們的任務都一樣簡單:在磁碟上找到 kernel,把它載入 RAM。這就是那個「交棒」的瞬間。交棒之後,CPU 開始執行 kernel 的程式碼,而且握有完整的硬體權限。
這裡有個關鍵:此刻你電腦裡所有「有趣的東西」——檔案、process、視窗——全都還不存在。kernel 得在接下來短短幾秒內,從零把這一切建起來。
接著是階段二,privilege ring,特權等級。
在 kernel 繼續往下建之前,得先理解 CPU 提供的保護機制。CPU 用多個特權等級來保護自己。x86 上其實有四個 ring,但真正重要的只有兩個。Ring 0 是 kernel 待的地方,基本上想幹嘛都可以。Ring 3 是 user space,可以跑應用程式,但要做別的事,幾乎都得先「請求許可」。
問題在於:現在 kernel 是在 ring 0 裡跑 C 程式碼,完全沒有護欄。只要一個指標指錯,整台機器就「著火」。這也是為什麼有人開玩笑說,kernel 開發者要靠喝酒度日。
但這道由 CPU 本身強制執行的隔離牆,非常關鍵。如果沒有它,每一支程式都能讀取別的程式的記憶體、隨手弄垮整個系統。有了 privilege ring,一支有 bug 的程式,通常就只能弄死它自己。
再來是階段三,virtual memory,我覺得這是整個計算領域裡最大的謊言。
這個騙局是這樣運作的。當程式之後跟系統要某個記憶體位址,那個位址其實根本不存在。它是一個假的「虛擬位址」,會被一塊叫 MMU、記憶體管理單元的硬體,翻譯成真正的實體位址。而 MMU 依賴的資料結構叫 page table,正是 kernel 此刻在建的東西。
記憶體是以一塊一塊叫 page 的單位發出去的,每一塊通常是 4KB。真正有意思的地方是:每一個 process 都有自己的 page table。這代表兩支應用程式可以同時運作,卻不會互相破壞——你的瀏覽器讀不到密碼管理器的記憶體,反過來也一樣。它們活在各自平行的宇宙裡,只有 kernel 能看穿彼此。
為了加速,MMU 還會把最近做過的翻譯結果,快取在一個小結構 TLB 裡。所謂一次翻譯,就是一個虛擬位址對應到一個實體位址的映射。而當程式碰到一個目前不在 RAM 裡的 page,MMU 會拋出一個 page fault,這會喚醒 kernel、從磁碟把那個 page 載進來,然後讓程式像什麼事都沒發生過一樣,繼續執行。
最後是階段四,file system。
在最底層,你的磁碟其實只是「一長排編號的區塊」。file system 就是那個負責「掩蓋這件事」的軟體層。它讓上層看到的是有名字、有目錄結構的檔案,而不是一堆冷冰冰的區塊編號。往下再挖,你還會碰到 inode 這些更底層的實作,那正是「從區塊到檔案」這條翻譯鏈的核心。
把這四個階段串起來,你會發現一個很一致的主題:作業系統,就是一層一層地,在裸機之上疊出「方便的假象」。
那今天收尾,我想留三個核心要點。
第一,這條路徑是「從無到有」的。韌體跟 bootloader 把 CPU 從一個只會執行寫死位址的裸核心,帶進 kernel 的世界,之後的一切才有得談。
第二,每一層抽象,都是為了解決前一層赤裸暴露出來的某個真實問題。privilege ring 解決的是「一支程式壞掉不能拖垮全部」,virtual memory 解決的是「每個 process 要有獨立隔離的空間」,file system 解決的是「人類不想面對一排區塊編號」。
第三,也是最實用的一點:理解這條時間軸,比你死背各章名詞,更能建立起對作業系統的真正心智模型。因為你記住的不是名詞,是它為什麼存在。下次你的電腦順順跑著,你就會知道,那背後是多少層謊言在替你撐著。
Tags
Related Articles
I Read Every Major CS Paper of the Last 100 Years — So What?
Fireship walks through 10 landmark CS papers from Turing to GPT-3, showing how each idea set the stage for the next — and how the path to modern AI was anything but linear