Skip to main content

Command Palette

Search for a command to run...

Lecture 1 - OS Design Principles

Updated
15 min read
Lecture 1 - OS Design Principles

Disclaimer:

⚠️ Ye who have ventured here, hath abandoned all hope...

The following content contains intense cybersecurity themes and may not be suitable for the faint-hearted

Students and beginners, proceed at your own thrill — this is not a walk in the park

Design Architecture 1: CPU + RAM

The Goal: Establish a basic hardware execution environment where the processor can read and execute instructions.

First we will make a dummy diagram of the current architecture which contains CPU and RAM

In the above diagram, the following components will perform the following functions.

Note: The current scenario illustrates the 1970s computer architecture without any modern computer hardware/software design functionalities like Parallel Processing, Multi Processing, Threading, Interrupts, etc. We will learn each and every design paradigm as we move on

CPU

  • Can execute instructions from a fixed memory address (called the reset vector)

RAM

  • Can store instructions and data, but is volatile (empties at power cycle)

Power Cycle

  • A power cycle means turning a device off completely and then turning it back on.

  • It's used to reset the hardware and bring the system back to a known initial state.

  • This violently purges all temporary memory (RAM), CPU registers, caches, and hardware latches

  • It's like giving the entire system a fresh start — especially useful when the system is frozen, behaving unexpectedly, or needs to reinitialize hardware.

Anatomy of a Power Cycle

Here is the exact CPU sequence during a power cycle:

1. Power is removed

  • When the system turns off, the CPU loses power.

  • All its internal states (registers, cache, control logic) are erased.

  • The CPU becomes completely inactive.

2. Power is restored

  • Once power is turned on again:

  • The Power Supply Unit (PSU) stabilizes and sends a Power Good electrical signal to the motherboard.

  • This tells the CPU: "Voltage levels are stable - you can start now."

3. CPU reset sequence begins

  • The CPU automatically starts executing code from a fixed memory address called the reset vector (e.g., 0xFFFF0 in early 16-bit CPUs like the 8086).

4. CPU state after power cycle

When the CPU restarts:

  • All registers are set to default values.

  • Instruction pointer (IP) is locked to the reset vector.

  • Caches and buffers are empty.

  • The CPU operates as if it's being used for the first time (a completely blank state).

Current Design

  • When CPU powers on

    • Upon power-on, the CPU immediately attempts to fetch its first instruction from the hardcoded reset vector (e.g., 0xFFFF0)

Problem - Volatility Trap

  • Because RAM is volatile, when it is powered on, its contents are random ("indeterminate") until explicitly initialized. It's not guaranteed to be zero.

  • Therefore, the memory at the reset vector, where the CPU expects its very first instruction, contains random garbage.

  • If the first instruction is garbage or invalid, the CPU usually triggers a fault, like an illegal instruction exception and crashes immediately.

  • The CPU cannot safely execute empty or random RAM. We need a valid instruction already in RAM at the reset vector.

  • How do we ensure that the RAM contains a valid instruction at the reset vector?

Solution - Manual Bootstrapping

  • The Approach: The operator must manually inject valid code into RAM before letting the CPU execute.

  • The Steps:

    1. The operator flips physical toggle switches on a front panel, or uses a paper tape, punched card reader, or console input to feed binary instructions directly into the RAM.

    2. Once this small "bootstrap" code is in RAM, the CPU is released to start executing from the reset vector.

    3. This bootstrap program's job is to read a larger program from a tape or disk into the rest of the RAM.

Limitations

  • Because RAM gets wiped out on every power cycle, an operator has to manually punch this code in every single time the computer restarts.

  • Is it possible to automate the bootstrap process so that manual code entry is no longer required on every boot?

Design Architecture 2: CPU + RAM + ROM

The Goal: Automate the bootstrap process so manual code entry is no longer required on every boot.

To solve the volatility trap of early computers, a new hardware component - ROM (Read-Only Memory) was introduced into the earlier design of the CPU and RAM.

CPU

  • Executes instructions from a fixed memory address (the reset vector).

  • Can be later configured to execute arbitrary code from any memory location.

RAM

  • Can store instructions/data, but is volatile (empties at power cycle)

  • Used by the CPU to execute code once it has been loaded from ROM.

ROM

  • Stores fixed code that cannot be easily modified.

  • Usually contains the initial bootloader or firmware.

  • The code stored in ROM contains the instructions that tell the CPU to move data into the RAM.

Current Design

  • Instead of punching code into RAM, we embed the bootstrap code permanently into ROM. We map the CPU's reset vector to point to the ROM chip.

  • Execution: When the CPU powers on, it starts executing instructions from the reset vector. It runs the bootloader straight from ROM, which then loads the instructions into RAM.

  • The Handover: Once the ROM contains the bootstrap code and loads the instructions into RAM, the CPU is ready to execute from the workspace.

  • Advantage: No need to directly punch the code into the RAM every time the system restarts.

Problem

  • Fixed Code: ROM stores fixed code, so it cannot be modified.

  • No Flexibility: A computing setup that requires customization or flexibility cannot change the bootstrap code burned into the ROM.

  • Result: No scope for customizing the bootstrap process.

  • How do we customize the bootstrap process?

Design Architecture 3: CPU + RAM + ROM + Long Storage Device

The Goal: To gain the flexibility to change or update the bootstrap code without replacing the physical ROM hardware, and to enable the loading of larger, more complex programs.

In this design, we solve the "Fixed Code" problem of Architecture 2 by adding a Long Storage Device (e.g., Hard Drive, SSD, or Magnetic Tape).

CPU

  • Executes instructions from a fixed memory address (the reset vector).

  • Follows the instructions in ROM to fetch the program from the Storage Device and load it into the RAM.

  • Can be later configured to execute arbitrary code from any memory location.

RAM

  • Can store instructions/data, but is volatile (empties at power cycle)

  • Used by the CPU to execute the program loaded from the storage device.

ROM

  • Stores fixed code that cannot be easily modified.

  • Contains the instructions needed to initialize the Long Storage Device.

  • Provides instructions to allow CPU to read from storage device

  • Provides instructions to allow CPU to write to storage device

  • If the ROM software (bootstrap code) is written to support reading from an external storage device, then it can:

    • Access the storage device

    • Read the program or instructions from it.

    • Load those instructions into a designated execution area in RAM (such as 0x7C00 for BIOS systems).

    • Trigger the CPU to start executing the loaded code from RAM.

Long Storage Device

  • Stores the actual bootstrap code or program (like an OS) permanently.

  • Unlike ROM, the data here can be updated or changed by the user at any time.

Current Design

  • Power On: The CPU wakes up and begins executing from a hardcoded address (the Reset Vector, such as 0xFFFFFFF0 for x86).

  • Hardware Mapping: This address points directly to the ROM. No address translation occurs yet because the MMU (Memory Management Unit) is not yet active.

  • The Fetch: The chipset routes reads to the BIOS ROM, which contains the minimal instructions needed to "wake up" the rest of the hardware.

  • The Handover: The CPU executes the ROM code, which finds the program on the Long Storage Device and copies it into RAM.

  • Execution: The CPU stops reading from the ROM and begins running the program (the Operating System) directly from the RAM.

Note: ROM configures the hardware so that the initial bootstrap code is available to the CPU at the reset vector, typically using memory-mapped I/O. We will revisit this section in upcoming lectures to study concepts such as the MMU more thoroughly.

Inference

  • Till now we have satisfied the necessary hardware requirements, to run a program like the Operating System which can control many other small programs

  • Here onwards, we will focus on the historical and technical context of each of the functionalities required to build the Operating System (our universal program)

  • In a modern BIOS system, the ROM isn't just a "loader" - it's an interface. It provides basic services (like "write this text to the screen") that the early bootloader uses before the Operating System is fully awake. For more information refer to the BIOS Boot Sequence at the end of this blog.

Historical Context Note: This architecture allowed computers to move from being single-purpose tools (like a calculator) to general-purpose machines (like a PC) that could run entirely different software simply by swapping the contents of the Storage Device.

File System

Problem

  • To run a different program from another storage medium, we currently need to power cycle the computer.

  • Is there a way to store multiple programs on the same storage device?

Solution

  • For point (2), we can do it, as long as we do the necessary book keeping to keep track of what program is stored at which location (memory address)

Book-Keeping System (File System)

  • To store multiple programs, we'll introduce a simple book-keeping system on the storage device, a mini file system: which has three members:
Fields Description
program_id Program identifier
start_address Starting address of program in storage
end_address Ending address of program in storage
  • The initial book-keeping system will look like this
    <program id><start address><end address>

  • This book-keeping helps us remember where each program lives on the storage device.

  • It's the foundation of our toy file system or a tiny version of FAT32

Meta-Program

Problem

  • Earlier we stored multiple programs into the same file system. Now we have another problem.

  • Can we switch between programs on the same storage device?

Solution

  • We can write a small meta-program that allows switching between different programs and store it alongside the RAM

  • With this, our architecture of RAM will be modified to this

  • This program provides additional capabilities beyond the basic bootstrap logic.

Bootstrap Code

  • The bootstrap code (also known as a bootloader) is a small, fixed program stored in ROM that runs immediately after the CPU powers on.

  • Its main job is to initialize the system and load the actual program from storage into RAM so the CPU can start executing it.

Features of Meta-Program

  • It can perform read/write operations on the storage medium (either directly or through the ROM)

  • It understands the bookkeeping data (i.e., it includes bookkeeping logic) present on the storage medium

  • As an extension of the book-keeping logic, it can load any program using its unique ID

Loading a Program by ID

  1. Using the given program ID, locate its start address and end address on the storage device

  2. Read data sequentially from the start address to the end address, copying it into RAM

  3. Execute a direct jump instruction (JMP <address in RAM>) to transfer control to the newly loaded program

  4. Wait until execution finishes, before accepting the next instruction on program load

System-Call

Problem

  • When a user program finishes execution or needs to perform a system-level task (like exiting, reading from storage, or displaying output), it must hand control back to the meta-program

  • If the program simply halts the CPU (e.g., using an instruction like HLT), the system stops entirely

  • We need a controlled way for programs to return control to the meta-program without halting the processor

Solution

  • Introduce a standardized mechanism for programs to jump to specific, predefined addresses in the meta-program — effectively creating an API interface between the program and the meta-program

Handling Control using API

  • The meta-program provides a set of known jump destinations (like functions) to handle specific operations such as exiting or I/O

  • The compiler for this computer stack recognizes these function calls (e.g., exit()) and emits the corresponding JMP instruction to the predefined address in the meta-program

  • When the program executes that jump, control safely returns to the meta-program as the meta-program has already blocked that predefined address

  • This mechanism is the early conceptual form of system calls (syscalls) — a structured way for user programs to communicate with and transfer control to the supervisory meta-program (later known as the Operating System kernel).

Interrupts

Problem

  • We can execute only one program at a time

  • If a program is waiting for a hardware response (like disk I/O or network data), the CPU remains idle in the meantime.

  • This wastes potential processing time and reduces overall efficiency.

  • Is there a way to make the CPU do something useful while another program is waiting? Essentially, to run multiple programs seemingly at once, even though there is only one CPU.

Solution

  • Add a timer, bro ☺︎

Timer Functionality

  • A timer is added to the CPU

  • The timer's frequency is configurable, allowing us to decide how often it triggers

  • When the timer trips, it can be configured to make the CPU automatically jump to a fixed memory address

  • We modify the meta-program to include a special piece of code at that address — this code runs every time the timer trips.

  • Inside that special code, we implement logic to shuffle or switch between processes.

Interrupt

  • When the timer trips, it interrupts the CPU from whatever it was doing — hence it's called an interrupt

  • The special code that runs in response is called an interrupt handler, because it handles what happens when the interrupt occurs

Context Switching

Program

  • A program is just passive code stored on disk

Process

  • A process is an active instance of that program running in memory — it has its own CPU registers, stack, and current execution point

Problem

  • How do we shuffle or switch between processes?

States of Process

  • Our process will have mainly two states

    • Running: The process is currently being executed by the CPU

    • Suspended: The process has temporarily paused its execution, waiting to be resumed later

  • As such, we have to keep track of both of the states in order to get a successful shuffle

Solution

  • We save the state of the currently running process and restore the state of a previously suspended process.

Components of Process State

A process's state includes:

  • Execution status: Whether it's running or suspended.

  • CPU registers: Program Counter (PC), Stack Pointer (SP), general-purpose registers, flags, etc.

Steps to shuffle processes

  • Save the state of the current (running) process

    • Copy all relevant CPU registers into memory reserved for that process.

    • Mark the process as suspended.

  • Restore the state of the next (suspended) process

    • Copy the saved CPU registers from memory back into the CPU.

    • Mark the process as running.

  • Resume execution

    • Jump to the program counter (PC) saved in the restored state.

    • Execution continues exactly where the process left off:

      • JMP<address in process memory>
  • This mechanism is called context switching, and it allows the CPU to alternate between processes, giving the appearance of multitasking on a single processor.

Extra Notes

The BIOS Boot Sequence (The Legacy Path)

1. The Wake-Up (ROM + CPU)

The moment you hit the power button, the CPU is "dumb." It goes to that hardcoded Reset Vector (0xFFFFFFF0) which is wired to the ROM.

  • The BIOS (Basic Input/Output System) code starts running.

  • It performs the POST (Power-On Self-Test) to make sure your RAM isn't fried and your keyboard is plugged in.

2. The Search (ROM → Long Storage)

The BIOS has a "Boot Order" stored in its settings. It looks at your Long Storage Device (Hard Drive or SSD) and searches for the very first sector of data, known as Sector 0.

  • This specific 512-byte chunk is called the MBR (Master Boot Record).

3. The Micro-Load (CPU moves Storage → RAM)

This is the "nitty-gritty" part. The BIOS code tells the CPU: "Grab those 512 bytes from the MBR and copy them into RAM at a very specific address: 0x7C00."

  • Why 0x7C00? It’s just a historical convention from the early 1980s, but every BIOS follows it.

4. The Handover (The Jump)

Once those 512 bytes are sitting in RAM, the BIOS gives its final command to the CPU: "JMP 0x7C00." The CPU stops executing the fixed code in the ROM and starts executing the code it just moved into the RAM.

5. The Multi-Stage Loading (The OS takes over)

Since 512 bytes is too small for a whole OS (Windows or Linux), that tiny bit of code in RAM is a "Stage 1 Bootloader." Its only job is to:

  1. Know how to read the rest of the Long Storage Device.

  2. Pull the actual Operating System files into the RAM.

  3. Fully initialize the OS.