How the Kernel Works — The Layer Between Your Code and the Hardware

How the Kernel Works — The Layer Between Your Code and the Hardware

2026-03-22

Your program can't talk to hardware directly. It can't read from disk, send a network packet, allocate memory, or create a process on its own. Every one of these operations goes through the kernel — the software that sits between your code and the machine.

The kernel is the only software that runs in privileged mode (ring 0 on x86, EL1 on ARM). It has direct access to all hardware, all memory, and all CPU features. Your program runs in user mode (ring 3 / EL0), where the CPU restricts what instructions can execute and what memory can be accessed. This boundary is the foundation of everything: security, isolation, multitasking.

What Does the Kernel Do?

The kernel has five primary responsibilities:

Process management — creating processes (fork), scheduling them across CPU cores, switching between them (context switches), and cleaning up when they exit.

Memory management — maintaining page tables for virtual memory, handling page faults, allocating physical frames, and managing the swap.

File systems — organizing data on disk. open(), read(), write(), close() — these all go through the kernel's VFS (Virtual File System) layer, which dispatches to the appropriate filesystem driver (ext4, APFS, btrfs).

Networking — the entire TCP/UDP stack runs in the kernel. When your application calls connect() or send(), the kernel builds the packet, manages the TCP state machine, handles retransmissions, and passes data to the network driver.

Device management — drivers for disk, network, GPU, USB, and every other piece of hardware. The kernel provides a uniform interface (read/write/ioctl) regardless of the underlying device.

What Is a Syscall?

A syscall (system call) is the interface between user space and kernel space. It's a controlled entry point: your program asks the kernel to do something, the CPU switches to privileged mode, the kernel performs the operation, and control returns to your program.

Common syscalls:

SyscallWhat it does
openOpen a file, return a file descriptor
readRead bytes from a file descriptor
writeWrite bytes to a file descriptor
closeClose a file descriptor
mmapMap a file or anonymous memory into the address space
forkCreate a child process
execReplace the current program
exitTerminate the process
socketCreate a network socket
connectConnect a socket to a remote address
send/recvSend/receive data on a socket
killSend a signal to a process
ioctlDevice-specific control operations

Linux has approximately 450 syscalls. Most programs use only 20-30. The rest are for specialized operations — namespaces (containers), io_uring (async I/O), eBPF (kernel programmability), and hardware-specific features.

How Does a Syscall Work?

When your program calls write(fd, buf, len):

  1. Library wrapper — the C library (glibc, musl) or language runtime puts the syscall number and arguments into CPU registers.
  2. Trap instruction — a special instruction (syscall on x86-64, svc on ARM64) transfers control to the kernel. The CPU switches from user mode to kernel mode.
  3. Kernel entry — the kernel's syscall handler reads the syscall number from the register, looks up the corresponding function in the syscall table, and calls it.
  4. Kernel execution — the kernel performs the operation (writes bytes to the file, navigating through VFS → filesystem driver → block device driver → disk).
  5. Return — the kernel puts the return value in a register and executes a return instruction (sysret on x86-64). The CPU switches back to user mode.
  6. Library unwrap — the C library reads the return value. If it indicates an error, it sets errno.

The entire round trip — user → kernel → user — takes 100-500 nanoseconds on modern hardware. That's fast, but not free. A program that makes millions of syscalls per second (like a network server handling thousands of connections) can spend significant time in the transition overhead alone. This is why techniques like io_uring batch syscalls, and why eBPF moves logic into the kernel to avoid the round trip entirely.

User Space vs Kernel Space

The boundary between user space and kernel space is enforced by the CPU hardware, not just software convention.

User space (ring 3):

  • Cannot access hardware directly
  • Cannot execute privileged instructions (e.g., modifying page tables, disabling interrupts)
  • Cannot access kernel memory
  • Must use syscalls for anything beyond pure computation

Kernel space (ring 0):

  • Full access to all hardware
  • Can execute any instruction
  • Can read/write any memory address
  • Manages all resources (CPU, memory, devices, network)

A bug in user space crashes one process. A bug in kernel space can crash the entire machine — a kernel panic (Linux) or Blue Screen of Death (Windows). This is why kernel code is held to higher standards and why the kernel is much smaller than the combined user-space software it supports.

Monolithic vs Microkernel

Not all kernels are organized the same way.

Monolithic kernels (Linux, Windows NT, macOS/XNU) — the filesystem, networking stack, device drivers, and scheduler all run in kernel space. Fast (no context switches between kernel components) but a driver bug can crash the system.

Microkernels (Minix, seL4, QNX) — only the essential components (scheduling, IPC, memory management) run in kernel space. Filesystems, drivers, and networking run as user-space processes. More isolated (a driver crash doesn't crash the kernel) but slower (communication between components requires IPC).

In practice, monolithic kernels dominate. Linux powers servers, Android, embedded systems, and cloud infrastructure. The performance cost of microkernels is hard to justify for most workloads. But microkernels are used where reliability is critical: QNX runs in cars and medical devices. seL4 is formally verified — mathematically proven to be free of certain classes of bugs.

How Does the Kernel Start?

When you power on a machine:

  1. Firmware (UEFI/BIOS) initializes hardware and loads the bootloader.
  2. Bootloader (GRUB, systemd-boot) loads the kernel image into memory.
  3. Kernel init — the kernel initializes memory management, the scheduler, device drivers, and filesystems.
  4. PID 1 — the kernel starts the first user-space process (systemd on Linux, launchd on macOS). From this point, everything is managed by user-space software running on top of the kernel.

The kernel never exits. It runs continuously, handling interrupts (hardware events like disk I/O completion or network packets arriving), scheduling processes, and serving syscalls. It's the foundation that makes everything else possible.

How Does This Connect to Networking?

The networking lessons covered TCP, UDP, and QUIC as protocols. The kernel is where TCP and UDP actually run. When you call connect(), the kernel's TCP implementation performs the three-way handshake. When you call send(), the kernel builds the TCP segment, fragments it into IP packets, and passes them to the network driver.

eBPF is a way to extend the kernel without modifying it — injecting custom code that the kernel verifies for safety and runs at kernel speed. It's the evolution of the user/kernel boundary: instead of crossing it with syscalls, you bring your logic into the kernel.

Next Steps