DEV Community

Cover image for # How to Build Your Own Operating System from Scratch in C
Farhad Rahimi Klie
Farhad Rahimi Klie

Posted on

# How to Build Your Own Operating System from Scratch in C

Building your own Operating System (OS) is one of the most challenging and rewarding projects in computer science. It teaches you how computers really work beneath the applications and frameworks we use every day.

In this article, we'll explore the complete roadmap for creating an operating system from scratch using the C programming language.


What Is an Operating System?

An Operating System is software that manages computer hardware and provides services to applications.

Popular operating systems include:

  • Linux
  • Windows
  • macOS
  • FreeBSD

An OS is responsible for:

  • Process management
  • Memory management
  • File systems
  • Device drivers
  • User interfaces
  • Hardware abstraction

Without an operating system, applications cannot easily communicate with hardware.


Prerequisites

Before building an operating system, you should understand:

Programming Languages

C

C is the primary language used for kernel development because it provides:

  • Direct memory access
  • Low-level control
  • High performance
  • Minimal runtime dependencies

Assembly

Assembly is required for:

  • Bootloader development
  • CPU initialization
  • Interrupt handling
  • Context switching

Computer Science Fundamentals

You should know:

Data Structures

  • Arrays
  • Linked Lists
  • Stacks
  • Queues
  • Trees
  • B-Trees
  • Hash Tables
  • Bitmaps

Algorithms

  • Searching
  • Sorting
  • Scheduling algorithms
  • Memory allocation algorithms

Understanding Computer Architecture

Operating systems interact directly with hardware.

Important topics:

CPU

Learn:

  • Registers
  • Instructions
  • Privilege levels
  • Interrupts
  • Exceptions

Memory

Understand:

  • RAM
  • Virtual Memory
  • Paging
  • Segmentation
  • Memory Mapping

Storage

Learn about:

  • Hard Drives
  • SSDs
  • File Systems

Development Environment

Install:

Compiler

GCC Cross Compiler

Example:

x86_64-elf-gcc
Enter fullscreen mode Exit fullscreen mode

Using a cross-compiler prevents your host operating system from interfering with kernel compilation.


Assembler

NASM
Enter fullscreen mode Exit fullscreen mode

Used for writing assembly code.


Emulator

QEMU
Enter fullscreen mode Exit fullscreen mode

Allows testing your operating system safely.

Example:

qemu-system-x86_64
Enter fullscreen mode Exit fullscreen mode

Debugger

GDB
Enter fullscreen mode Exit fullscreen mode

Used for debugging kernel code.


Operating System Architecture

A basic operating system consists of:

+----------------+
| Applications   |
+----------------+
| System Calls   |
+----------------+
| Kernel         |
+----------------+
| Drivers        |
+----------------+
| Hardware       |
+----------------+
Enter fullscreen mode Exit fullscreen mode

Step 1: Create a Bootloader

When a computer starts:

  1. BIOS/UEFI executes.
  2. Bootloader loads.
  3. Bootloader loads kernel into memory.
  4. Kernel starts running.

Simple bootloader example:

[BITS 16]
[ORG 0x7C00]

mov ah, 0x0E
mov al, 'H'
int 0x10

jmp $

times 510-($-$$) db 0
dw 0xAA55
Enter fullscreen mode Exit fullscreen mode

This displays the letter H on the screen.


Step 2: Create the Kernel Entry Point

Kernel entry:

void kernel_main()
{
    while (1)
    {
    }
}
Enter fullscreen mode Exit fullscreen mode

This is the first C function executed by the kernel.


Step 3: Display Text on Screen

Using VGA text mode:

#define VGA_MEMORY ((unsigned short*)0xB8000)

void print_char(char c)
{
    VGA_MEMORY[0] = (0x0F << 8) | c;
}
Enter fullscreen mode Exit fullscreen mode

Usage:

print_char('A');
Enter fullscreen mode Exit fullscreen mode

You should now see a character displayed on the screen.


Step 4: Build a Terminal

A terminal allows user interaction.

Features:

  • Print text
  • Read keyboard input
  • Execute commands

Example:

myOS>
Enter fullscreen mode Exit fullscreen mode

Commands:

help
clear
version
Enter fullscreen mode Exit fullscreen mode

Step 5: Keyboard Driver

The keyboard generates interrupts.

Kernel responsibilities:

  • Read scan codes
  • Convert to characters
  • Send input to terminal

Example:

User presses A
↓
Keyboard Interrupt
↓
Kernel Receives Code
↓
Display A
Enter fullscreen mode Exit fullscreen mode

Step 6: Interrupt Handling

Interrupts allow hardware to communicate with the CPU.

Examples:

  • Keyboard interrupt
  • Timer interrupt
  • Disk interrupt

Interrupt Descriptor Table (IDT):

struct idt_entry
{
    unsigned short offset_low;
    unsigned short selector;
    unsigned char zero;
    unsigned char flags;
    unsigned short offset_high;
};
Enter fullscreen mode Exit fullscreen mode

Step 7: Memory Management

Memory management is one of the most important kernel subsystems.

Responsibilities:

  • Allocate memory
  • Free memory
  • Protect memory
  • Virtual memory mapping

Simple bitmap allocator:

unsigned char bitmap[1024];
Enter fullscreen mode Exit fullscreen mode

Each bit represents a memory page.


Step 8: Physical Memory Manager

Tracks available RAM pages.

Functions:

void* alloc_page();
void free_page(void* ptr);
Enter fullscreen mode Exit fullscreen mode

Step 9: Virtual Memory

Modern operating systems use paging.

Benefits:

  • Process isolation
  • Security
  • Larger address spaces

Virtual address:

0x400000
Enter fullscreen mode Exit fullscreen mode

May map to:

0x1A3000
Enter fullscreen mode Exit fullscreen mode

Physical memory.


Step 10: Process Management

A process is a running program.

Process structure:

struct process
{
    int pid;
    void* stack;
    void* page_directory;
};
Enter fullscreen mode Exit fullscreen mode

Kernel responsibilities:

  • Create process
  • Destroy process
  • Schedule process

Step 11: Task Scheduler

Scheduler decides which process runs next.

Common algorithms:

Round Robin

P1 → P2 → P3 → P1
Enter fullscreen mode Exit fullscreen mode

Priority Scheduling

High Priority First
Enter fullscreen mode Exit fullscreen mode

Step 12: Context Switching

Switching between processes requires saving CPU state.

Save:

  • Registers
  • Stack Pointer
  • Instruction Pointer

Restore next process state.

This creates multitasking.


Step 13: System Calls

Applications communicate with the kernel using system calls.

Example:

write();
read();
open();
close();
Enter fullscreen mode Exit fullscreen mode

Flow:

Application
↓
System Call
↓
Kernel
↓
Hardware
Enter fullscreen mode Exit fullscreen mode

Step 14: File System

A file system stores data permanently.

Examples:

  • FAT12
  • FAT16
  • FAT32
  • EXT2
  • EXT4

Beginner recommendation:

FAT12
Enter fullscreen mode Exit fullscreen mode

because it is simple to implement.

Basic operations:

open()
read()
write()
delete()
Enter fullscreen mode Exit fullscreen mode

Step 15: Disk Driver

The kernel must communicate with storage devices.

Responsibilities:

  • Read sectors
  • Write sectors
  • Cache data

Example:

read_sector();
write_sector();
Enter fullscreen mode Exit fullscreen mode

Step 16: User Mode and Kernel Mode

Modern CPUs support privilege levels.

Kernel Mode:

Ring 0
Enter fullscreen mode Exit fullscreen mode

User Mode:

Ring 3
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • Security
  • Stability
  • Isolation

Step 17: ELF Executable Loader

ELF is a common executable format.

The loader:

  1. Reads executable.
  2. Maps memory.
  3. Creates process.
  4. Starts execution.

Step 18: Device Drivers

Drivers communicate with hardware.

Examples:

  • Keyboard
  • Mouse
  • Audio
  • Network
  • Graphics

Step 19: Networking

Networking requires implementing:

  • Ethernet
  • ARP
  • IP
  • ICMP
  • UDP
  • TCP

Example stack:

Application
↓
TCP
↓
IP
↓
Ethernet
Enter fullscreen mode Exit fullscreen mode

Step 20: Graphical User Interface

After the kernel is stable:

Build:

  • Framebuffer driver
  • Window manager
  • Fonts
  • Mouse support

Simple GUI architecture:

Desktop
 ├── Window
 ├── Window
 └── Window
Enter fullscreen mode Exit fullscreen mode

Recommended Project Roadmap

Follow this order:

  1. Bootloader
  2. Kernel Entry
  3. VGA Text Output
  4. Keyboard Driver
  5. Terminal
  6. IDT
  7. Timer
  8. Physical Memory Manager
  9. Paging
  10. Heap Allocator
  11. Processes
  12. Scheduler
  13. System Calls
  14. FAT12 File System
  15. ELF Loader
  16. User Programs
  17. Networking
  18. GUI

Useful Resources

Recommended books:

  • Operating Systems: Three Easy Pieces
  • Modern Operating Systems
  • Operating System Concepts
  • Computer Systems: A Programmer's Perspective
  • Intel Software Developer Manual

Recommended websites:

  • OSDev Wiki
  • LittleOSBook
  • BrokenThorn OS Development Series

Final Thoughts

Building an operating system is a long-term project that can take months or even years. Start small. Focus first on booting the machine, printing text, handling the keyboard, and managing memory. Once those fundamentals are working, gradually add multitasking, file systems, networking, and eventually a graphical interface.

The most important advice is simple: build one subsystem at a time. Every modern operating system, from Linux to Windows, started as a small kernel capable of doing only a few basic tasks. With patience and consistency, you can build your own operating system from scratch in C and gain a deep understanding of how computers truly work.

Top comments (0)