Skip to content
ALB Wiki

Inside a Linux System

A Linux system is an arc: firmware hands to a bootloader, the bootloader hands to a kernel, the kernel mounts a rootfs and execs PID 1, PID 1 brings up services, services reach a login. This chapter walks the whole arc — both init paths (SysVinit's readable rc scripts and systemd's unit graph) — and teaches you to read and control the machine's own machinery.

The boot chain — six handoffs

firmware → bootloader → kernel (+initramfs) → rootfs → PID 1 → services → login
  1. Firmware — BIOS or UEFI — POSTs, finds a boot device, loads the bootloader. UEFI reads the ESP's EFI/ tree.
  2. Bootloader — GRUB (usually) — presents the menu, loads vmlinuz + initramfs into memory, hands over with a kernel command line (root=/dev/sda3 ro quiet).
  3. Kernel — decompresses, initializes drivers, mounts the rootfs, runs /sbin/init as PID 1. (The initramfs — a temporary rootfs in RAM — carries drivers the kernel needs to reach the real rootfs.)
  4. PID 1 — init — the ancestor of every process; brings the system to a usable state.
  5. Services — gettys, daemons, network, logging — all spawned by init.
  6. Login — a getty prompts; login checks /etc/passwd+shadow; a shell starts.

Each handoff is inspectable: dmesg is the kernel's own log of stage 3; journalctl or /var/log/messages covers stages 4–6.

PID 1 — the two paths

SysVinit — the readable path

cat /etc/inittab          # the runlevel map — id:3:initdefault:
ls /etc/rc.d/             # rc.S (sysinit) rc.M (multiuser) rc.6 (reboot) rc.inet1 rc.sshd
/etc/rc.d/rc.sshd restart # the interface — scripts you can READ

Inittab defines runlevels — 0 halt, 1 single-user, 3 multiuser (Slackware default), 4 X11, 6 reboot. telinit 3 changes them. Services are rc.* shell scripts, started in order. The beauty: it's all shell — cat /etc/rc.d/rc.M shows you exactly what boot does. Slackware's entire init system fits in a few readable scripts.

systemd — the standard path

systemctl status sshd        # state of a unit
systemctl start sshd         # bring up
systemctl enable sshd        # start at boot
systemctl list-units --type=service
systemctl list-dependencies  # the dependency graph
systemctl isolate multi-user.target   # the "runlevel" equivalent
journalctl -u sshd           # the unit's log
journalctl -b                # this boot's whole log
journalctl -f                # follow live

Systemd replaces scripts with units — declarative files in /etc/systemd/system/ + /usr/lib/systemd/system/ — started by dependency order, not sequence. Faster, parallel, self-logging — and opaque in ways SysVinit never is. .service, .mount, .socket, .timer, .target — the unit types; .targets are the runlevel analogues (multi-user.target ≈ runlevel 3).

The runlevels — the complete map

# SysV: 0 halt · 1 single-user · 2 unused · 3 multiuser · 4 X11 · 5 unused · 6 reboot
runlevel                      # where you are — "N 3"
telinit 1                     # single-user — the maintenance mode
telinit 3                     # multiuser text
telinit 4                     # X11 — the graphical level
telinit 6                     # reboot
init 0                        # halt — the older spelling

# systemd's target equivalents:
systemctl isolate rescue.target          # single-user
systemctl isolate multi-user.target      # = runlevel 3
systemctl isolate graphical.target       # = runlevel 4/5
systemctl get-default                    # the default target
systemctl set-default multi-user.target  # change it
systemctl reboot / poweroff / suspend
shutdown -h now / shutdown -r +5 "message" / shutdown -c

Runlevel 1/rescue = single-user: minimal services, no network, root shell — where fsck, password resets, and surgery happen.

The init scripts — SysV's readable machinery

ls /etc/rc.d/                 # Slackware's scripts
# rc.S  — system init (early)
# rc.M  — multiuser — the main one
# rc.0/6 — halt/reboot
# rc.inet1 — network
# rc.sshd rc.mysqld ... — services
/etc/rc.d/rc.sshd restart    # the interface
/etc/rc.d/rc.local           # the local-hacks script — last in boot
/etc/rc.d/rc.local_shutdown  # and on shutdown

Each is a plain shell script — cat /etc/rc.d/rc.M shows the entire multiuser boot sequence in a page of readable commands. The K/S symlink convention (rc3.d/S10sysklogd = start at priority 10) orders them.

The services' plumbing

gettys — the login prompts: one per virtual console (Ctrl-Alt-F2 switches). Inittab's c1:12345:respawn:/sbin/agetty tty1 or systemd's getty@tty1.service — respawn is the self-healing restart.

Daemons — long-running services — daemonize by detaching from the terminal (setsid, no tty); systemd manages them as units. Logs to syslog (/var/log/messages) or journald.

Cron — the scheduler: crontab -e — 0 3 * * * /usr/local/bin/backup — minute hour day month weekday. /etc/cron.daily/ et al. for the system. systemd timers are the modern unit-based equivalent.

systemd units — the anatomy

# /etc/systemd/system/myservice.service — a complete unit
[Unit]
Description=My service
After=network.target
Wants=network-online.target

[Service]
Type=simple                    # simple|forking|oneshot|notify|dbus|idle|exec
ExecStart=/usr/bin/myservice --flag
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure             # always|on-success|on-failure|no
RestartSec=5
User=mysvc
Group=mysvc
WorkingDirectory=/var/lib/mysvc
Environment=VAR=val
ExecStartPre=/usr/bin/precheck
ExecStartPost=/usr/bin/post
LimitNOFILE=65536
ProtectSystem=strict           # read-only /
PrivateTmp=true                # own /tmp
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target     # enable → symlink into the target's .wants/

The sections: [Unit] dependencies, [Service] the process config, [Install] how enable wires it into boot. Type= is the contract — simple (foreground), forking (daemonizes), oneshot (runs once), notify (sd_notify protocol). systemctl daemon-reload after editing; systemctl cat shows the composed unit.

The unit types

.service processes · .socket socket-activation · .timer cron-replacement · .mount mounts · .automount lazy-mounts · .target runlevel-groups · .path inotify triggers · .slice cgroup buckets · .device udev-derived. systemctl list-units --type=service enumerates.

The journal — structured logging

journalctl --list-boots             # every boot, ever — pick one to read
journalctl _PID=1234                # by process
journalctl -g 'pattern'             # grep the journal
journalctl -o json-pretty           # structured output
journalctl --priority=emerg..err    # level band
journalctl _SYSTEMD_UNIT=sshd.service _COMM=sshd

Fields are queryable (_PID, _COMM, _SYSTEMD_UNIT) — journald is a queryable database, not text files.

The boot stages' detail — what actually fires when

0. Firmware    — UEFI/BIOS POST → reads GPT → loads bootloader
1. Bootloader  — GRUB loads kernel + initramfs → jumps to start_kernel
2. Kernel      — init/main.c → start_kernel() → scheduler + mm + drivers
                 → rest_init() → kernel_init → run_init_process("/sbin/init")
3. Initramfs   — early root: unpack → its init loads modules → mounts real root
                 → switch_root → the real root takes over
4. PID 1       — /sbin/init (SysV) or /usr/lib/systemd/systemd
5. Services    — SysV: rc.S → rc.M → gettys
                 systemd: targets → units → getty@tty1.service
6. Login       — getty/agetty → login → PAM → /etc/profile → shell

dmesg shows stage 2-3 verbatim — the BIOS-e820 map, the mount, the Run /sbin/init as init process line. journalctl -b shows stage 4-5. The kernel cmdline (cat /proc/cmdline) is stage 1's record.

The PID-1 contract — what init must do

  • Reap orphans — every reparented child ends at PID 1
  • Never exit — the kernel panics if PID 1 dies
  • Handle SIGCHLD — collect zombies
  • Be the root of the cgroup tree — every process descends
  • Start the world — mount the filesystems, bring up services, spawn login

SysVinit does it via /etc/inittab + rc scripts — readable shell. systemd does it via unit-graph + journald — structured. Both satisfy the contract; the argument is about how.

The kernel's own interfaces

  • dmesg — the kernel ring buffer: hardware detection, driver loads, oopses — boot's transcript
  • /proc — processes + kernel state as files
  • /sys — devices/drivers as a browsable object tree
  • sysctl//proc/sys — live kernel knobs: sysctl -w net.ipv4.ip_forward=1
  • modprobe/lsmod/modinfo — loadable modules — the driver's hotplug layer

Runlevels / targets — the modes

runlevel              # SysV — where you are
telinit 1             # single-user — maintenance mode (the rescue level)
telinit 3             # multiuser text
telinit 6             # reboot
systemctl isolate rescue.target     # systemd's single-user
systemctl reboot / poweroff         # the controlled shutdowns
shutdown -h now                     # traditional halt

Runlevel 1 / rescue.target — single-user mode — is the maintenance door: minimal services, root shell, the place filesystem checks and password resets happen. The kernel command line's single or init=/bin/bash boots straight there — the rescue technique of last resort.

The LFS system's boot — what you built

The LFS SysVinit path installs sysvinit + sysklogd + the boot scripts — /etc/rc.d/init.d/ scripts for cleanfs, modules, network, sysklogd — a minimal but legible init. Chapter 6's /etc/inittab + fstab + rc scripts are the whole machine's waking machinery — you wrote them from nothing.

Where the system goes wrong

SymptomCause
Boots to grub> or rescueBootloader/config — chapter 6 territory
Kernel panic — can't mount rootrootfs driver/filesystem not built-in
Boots, no login promptgetty not spawning — inittab/agetty
Service won't startsystemctl status/journalctl -u — or the rc script's exit code
Slow bootsystemd-analyze blame — or a blocking rc script
dmesg flooded with errorsDriver/hardware — dmesg -l err filters
Can't change runlevelsystemd targets ≠ SysV runlevels — wrong tool for the init

Exercises

  1. dmesg | less — read your kernel's boot transcript.
  2. cat /etc/inittab (SysV) or systemctl list-dependencies (systemd) — the boot plan.
  3. pstree -p | head -20 — the fork family from PID 1.
  4. journalctl -b or less /var/log/messages — this boot's log.
  5. Switch a virtual console (Ctrl-Alt-F2); identify the getty that owns it.
  6. sysctl -a | less — the kernel's tunable mind.

Timeline

HandoffThe mechanism
firmwareBIOS/UEFI → ESP/boot sector
bootloaderGRUB → vmlinuz + initramfs + cmdline
kernelinit → mount rootfs → /sbin/init
PID 1rc scripts or unit graph
servicesgettys, daemons, network, logging
logingetty → login → shell

See also

Categories: Boot · Handbook · Linux · Linux Handbook
This page was last edited 2026-10-26 13:32:06 UTC by ALB Team. What links here