Part 1 — What a Linux “OS” actually is
The kernel/OS distinction
“Linux” strictly means the kernel. That is the privileged program that owns the CPU, memory and devices and exposes them through roughly 470 system calls plus pseudo-filesystems. The kernel cannot log you in. It has no shell and cannot read /etc/fstab. It does not know what a “package” is. After initialising hardware, it does exactly one thing toward userspace: it executes a single program as PID 1 and then serves that program’s system calls, and those of its descendants, until shutdown.
Everything else that people mean by “the OS” is userspace software that someone chose, compiled and integrated:
| Layer | Examples | What it contributes |
|---|---|---|
| Firmware | UEFI (EDK II-based vendor firmware), legacy BIOS, U-Boot on embedded, coreboot | Initialises DRAM, CPU and PCIe. Loads the first boot program. Supplies ACPI tables or a Device Tree describing the hardware |
| Bootloader | GRUB 2, systemd-boot, the kernel’s own EFI stub, U-Boot, Limine | Finds the kernel and initramfs, passes the command line and memory map, jumps to the kernel entry point |
| Kernel | Linux 7.3 (vmlinuz/bzImage) + modules in /lib/modules/$(uname -r) | Scheduling, memory, VFS, networking, drivers, security, the syscall ABI |
| Early userspace | initramfs (built by dracut, mkinitcpio, initramfs-tools, or by hand) | Loads storage drivers, unlocks LUKS, assembles RAID/LVM, finds and mounts the real root, switch_root |
| C library | glibc, musl | Turns POSIX/C APIs into syscalls. Dynamic loader (ld-linux-x86-64.so.2), locale, NSS, threads |
| Toolchain | binutils (as, ld), GCC or Clang/LLVM, make, Rust | Produces every binary in the system, including itself |
| Init and service management | systemd, OpenRC, runit, s6, BusyBox init | PID 1: mounts, starts services, reaps orphans, handles shutdown |
| Core userland | GNU coreutils, util-linux, bash, procps, iproute2, kmod, shadow | The commands that make the system usable and administrable |
| Device management | systemd-udevd / eudev / mdev | Reacts to kernel uevents: names devices, loads modules, sets permissions |
| Higher layers | D-Bus, network managers, display servers, package manager | Policy and user-facing integration |
Many people write “GNU/Linux” because, on a typical desktop or server, the GNU toolchain, glibc and coreutils make up much of the base. That label does not hold everywhere. Alpine uses musl, BusyBox and OpenRC.10 Android uses Bionic and its own init. Neither is “GNU”. Yet all three run the same kernel, because the kernel’s contract is the syscall ABI, not any particular userland.
What a distribution actually assembles
A distribution is a set of integration decisions plus the infrastructure to keep them consistent over time:
- Selection. Which versions of which upstream projects.
- Build policy. Compiler flags (
-O2 -fstack-protector-strong -D_FORTIFY_SOURCE=3, PIE, CET), the libc, merged/usr, the filesystem hierarchy. - Kernel configuration. A distro
.configthat enables thousands of modules so one image boots on most hardware. - Packaging and dependency resolution. rpm/dnf, dpkg/apt, apk, pacman.
- Integration glue. The initramfs generator, udev rules, default systemd units, PAM stacks, SELinux/AppArmor policy.
- Lifecycle. Security updates, ABI tracking, LTS kernels, signing keys for Secure Boot and modules.
So “building your own OS” on Linux is not writing a kernel. It means doing items 1 through 5 by hand at least once, so you know what every file on the root filesystem is for and why it is there. Linux From Scratch was designed to make you do exactly that.
Observe it on your own machine
# Who is PID 1, and what kernel is running?
ps -p 1 -o pid,comm,args
uname -a
cat /proc/cmdline # what the bootloader passed to the kernel
# Which libc and dynamic loader does a binary use?
ldd /bin/ls
readelf -l /bin/ls | grep interpreter
# Firmware path: UEFI or BIOS?
[ -d /sys/firmware/efi ] && echo UEFI || echo BIOS
ls /sys/firmware/acpi/tables 2>/dev/null | head
# The kernel's view of hardware description (ACPI on x86, DT on many ARM boards)
ls /proc/device-tree 2>/dev/null | head