Linux Init Program Explained
How PID 1 takes an embedded Linux system from a booted kernel to a running, supervised system — and why choosing the right init matters.
If you’re building a free embedded Linux course mental model of how a board actually comes alive after the kernel prints its last boot message, this lecture is the missing piece. We cover what init actually is, why the kernel refuses to boot without it, and how the three real-world options — BusyBox init, System V init, and systemd — trade off complexity against flexibility. This is part of our free linux kernel development course, and it sets up the next lecture where we build a working BusyBox inittab from scratch.
What You Will Learn
Prerequisites
You should already be comfortable with how a kernel image is built and how a root filesystem is assembled — covered earlier in this free embedded systems course. Basic shell scripting and familiarity with process IDs will help but aren’t required.
Why the Kernel Needs an Init Program
A freshly booted Linux kernel is not a running system — it’s a scheduler, a memory manager, and a pile of drivers sitting idle. Somebody has to actually start things: bring up a console, configure system parameters, launch daemons, and keep the machine alive until it’s told to shut down. That “somebody” is init.
Once the kernel has mounted a root filesystem — whether that’s an initramfs baked into the kernel image or a block device named with root= on the command line — it looks for a single executable to run. For an initramfs that’s /init; for a regular root filesystem it’s /sbin/init. This program is launched with root privilege, and because it’s the very first userspace process, the kernel assigns it process ID 1. If that launch fails for any reason, there’s no fallback: the kernel panics.
The Six Jobs Every Init Program Performs
Regardless of which implementation you choose, init exists to manage the full lifecycle of the system:
- Boot-time setup — mount essential filesystems, set system parameters, prepare the environment.
- Daemon supervision — start background services and, on many systems, login shells via
getty-style helpers. - Orphan adoption — any process whose parent dies gets re-parented to init, so the process tree never has dangling gaps.
- Zombie reaping — init catches
SIGCHLDfrom its direct children and collects their exit status so they don’t linger as zombies. - Restart policy — optionally relaunch daemons that exit unexpectedly.
- Shutdown handling — bring the system down cleanly on request.
Increasingly, init is also expected to react to runtime events — hot-plugged hardware, module loading and unloading — rather than only handling boot and shutdown. That expanded scope is exactly what pushed systemd toward being a full service manager rather than a lightweight bootstrapper.
Seeing Init in a Running Tree
Because every process is either init itself or a descendant of it, pstree makes the relationship easy to see directly on a running board:
# pstree -gn
init(1)-+-networkd(41)
|-syslogd(52)
|-sshd(88)
`-sh(101)---pstree(114)
Notice that init sits at PID 1, and every other process — whether it was launched directly by init or adopted later after being orphaned — traces back to it. This is why an init crash is unrecoverable: there’s no parent above it to restart it.
Comparing the Three Common Init Systems
Embedded Linux builds you’ll actually encounter almost always pick from three implementations. Buildroot defaults to BusyBox init; the Yocto Project defaults to System V init but supports systemd as well.
| Aspect | BusyBox init | System V init | systemd |
|---|---|---|---|
| Design goal | Minimal, single small binary | Script-driven, POSIX-shell based | Full service manager with dependency graph |
| Startup style | Reads one /etc/inittab | Runlevel scripts under /etc/init.d | Parallel unit activation |
| Shell dependency | ash is enough | Needs ash or bash | None — units are declarative |
| Typical footprint | Smallest — no extra binaries | Small, a handful of helper tools | Largest — pulls in glibc and many libraries |
| Best fit | Tight flash budgets, single-purpose devices | Traditional embedded Linux, predictable ordering | Gateways/edge devices that need hot-plug and dependency handling |
The pattern to remember: flexibility and complexity rise together as you move from BusyBox init toward systemd. A battery-powered sensor node with 8 MB of flash has no business running systemd; a Linux-based gateway juggling USB modems and dynamic networking usually benefits from it.
Common Mistakes
- Picking systemd by default without checking flash/RAM budget — it’s the heaviest option by a wide margin.
- Forgetting that a failed
initexec is a boot-blocking kernel panic, not a recoverable error — always test your init binary’s path and permissions before flashing. - Assuming BusyBox init and System V init behave identically just because both use plain-text config — their action semantics differ (covered in the next lecture).
Best Practices
Recommendations
- Match the init system to the product’s actual runtime needs, not habit — most single-purpose embedded devices are over-served by systemd.
- Keep init’s own startup path minimal; push heavier setup into scripts or units it launches, so a bug in application logic can’t take PID 1 down with it.
- Always verify with
pstree -gnorps -efafter a new board bring-up that init is correctly reaping children — orphaned zombies are a classic sign of a misconfigured supervisor.
Summary and Key Takeaways
- Init is the first userspace process, always PID 1, launched directly by the kernel with root privilege.
- Its job spans the full system lifecycle: boot-time setup, daemon supervision, orphan adoption, zombie reaping, restart policy, and shutdown.
- BusyBox init, System V init, and systemd trade off footprint against capability — choose based on your board’s constraints, not defaults.
Ready to Configure Your Own Init?
In the next lecture we build a real BusyBox inittab line by line, add Buildroot-style start/kill scripts, and boot it in QEMU.
Frequently Asked Questions
Why does init always get process ID 1?
PID 1 is reserved by the kernel for the very first process it execs after mounting the root filesystem. Because nothing runs before it, it’s guaranteed to be PID 1, and the kernel treats it specially — for example, signals that would normally kill a process are ignored by PID 1 unless it explicitly handles them.
What happens if the init binary is missing or not executable?
The kernel panics. There is no fallback process to launch, so a missing or corrupt init binary means the board never reaches a usable state — this is one of the first things to check when a new board bring-up hangs at boot.
Can I write my own init from scratch instead of using BusyBox, System V init, or systemd?
Yes — any executable that the kernel can exec as PID 1 qualifies, even a shell script. It’s common for very simple RYO (roll-your-own) embedded builds, but hand-rolled init logic tends to become unmaintainable as more daemons and startup steps are added.
Does Buildroot support System V init or systemd instead of BusyBox init?
Yes, Buildroot’s init system choice is configurable, though BusyBox init is the default because of its minimal footprint. Yocto-based builds default to System V init but can be configured for systemd.
Is systemd too heavy for embedded Linux?
It depends entirely on your flash and RAM budget. For devices with tens of megabytes of storage and dynamic hardware or networking needs, systemd’s dependency-aware startup is often worth the footprint. For tightly constrained single-purpose devices, BusyBox init is usually the better fit.
How do orphaned processes get reassigned to init?
When a process’s parent terminates while it still has running children, the kernel reparents those children to PID 1 (or, on modern kernels, to the nearest subreaper if one is configured). Init then becomes responsible for eventually reaping them via SIGCHLD.

2 Comments