Ashok G

Linux From the Metal Up

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:

LayerExamplesWhat it contributes
FirmwareUEFI (EDK II-based vendor firmware), legacy BIOS, U-Boot on embedded, corebootInitialises DRAM, CPU and PCIe. Loads the first boot program. Supplies ACPI tables or a Device Tree describing the hardware
BootloaderGRUB 2, systemd-boot, the kernel’s own EFI stub, U-Boot, LimineFinds the kernel and initramfs, passes the command line and memory map, jumps to the kernel entry point
KernelLinux 7.3 (vmlinuz/bzImage) + modules in /lib/modules/$(uname -r)Scheduling, memory, VFS, networking, drivers, security, the syscall ABI
Early userspaceinitramfs (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 libraryglibc, muslTurns POSIX/C APIs into syscalls. Dynamic loader (ld-linux-x86-64.so.2), locale, NSS, threads
Toolchainbinutils (as, ld), GCC or Clang/LLVM, make, RustProduces every binary in the system, including itself
Init and service managementsystemd, OpenRC, runit, s6, BusyBox initPID 1: mounts, starts services, reaps orphans, handles shutdown
Core userlandGNU coreutils, util-linux, bash, procps, iproute2, kmod, shadowThe commands that make the system usable and administrable
Device managementsystemd-udevd / eudev / mdevReacts to kernel uevents: names devices, loads modules, sets permissions
Higher layersD-Bus, network managers, display servers, package managerPolicy 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:

  1. Selection. Which versions of which upstream projects.
  2. Build policy. Compiler flags (-O2 -fstack-protector-strong -D_FORTIFY_SOURCE=3, PIE, CET), the libc, merged /usr, the filesystem hierarchy.
  3. Kernel configuration. A distro .config that enables thousands of modules so one image boots on most hardware.
  4. Packaging and dependency resolution. rpm/dnf, dpkg/apt, apk, pacman.
  5. Integration glue. The initramfs generator, udev rules, default systemd units, PAM stacks, SELinux/AppArmor policy.
  6. 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