Free embedded Linux course: how PID
BusyBox Init and inittab Explained
1 boots your root filesystem
If you have ever booted a minimal embedded Linux system straight into a shell prompt, you already know how fragile that setup is: nothing restarts a crashed shell, nothing mounts /proc for you, and nothing supervises background services. This lecture is part of our free embedded Linux course and explains how BusyBox init solves this by acting as PID 1 — the very first userspace process the kernel starts, and the ancestor of every other process on the system.
inittab
free embedded linux course
free linux kernel development course
rootfs boot sequence
What You Will Learn
- Why every Linux system — desktop, server, or embedded — needs an init process at PID 1
- The exact syntax of
/etc/inittaband what each field means - The difference between
sysinit,askfirst, andrespawnactions - How to write a boot script (
rcS) that prepares the system for use - How to test your init setup on QEMU and on real embedded hardware
Prerequisites
You should already have a working root filesystem staging directory (with BusyBox built and installed) and basic familiarity with shell scripting. If you haven’t built a rootfs yet, work through the earlier lectures in this free embedded systems course before continuing.
Why init Exists
The Linux kernel’s job ends the moment it hands control to the first userspace program. That program — traditionally called init — is responsible for everything that happens next: mounting filesystems, starting a console, launching background daemons, and restarting anything that crashes. Without init, a kernel panic isn’t the only way your board becomes unresponsive; a single shell exiting would leave you with nothing at all.
BusyBox ships its own compact init implementation, which is more than adequate for embedded targets that don’t need the complexity of systemd. It reads a single configuration file, /etc/inittab, and acts on each line in order.
|
v
Kernel decompresses, mounts rootfs, execs /sbin/init (PID 1)
|
v
BusyBox init reads /etc/inittab
|
+–> sysinit action -> runs /etc/init.d/rcS (mounts, setup)
|
+–> respawn actions -> starts syslogd, getty, etc. (restarted if they exit)
|
+–> askfirst action -> waits for Enter, then starts a login shell
inittab Syntax
Each line in /etc/inittab follows a fixed four-field format:
<id>:<runlevels>:<action>:<process>
BusyBox ignores the id and runlevels fields (they exist for compatibility with SysV init), so in practice most embedded inittabs leave them blank. The action field is what matters:
| Action | Meaning |
|---|---|
sysinit |
Run once, first, before anything else — typically your boot script |
respawn |
Start the process, and restart it automatically if it ever exits |
askfirst |
Print a prompt and wait for Enter before starting the process (usually a shell) |
wait |
Run once and block further inittab processing until it finishes |
once |
Run once, don’t wait, don’t restart |
ctrlaltdel |
Process to run when Ctrl+Alt+Del is received |
shutdown |
Process to run at system shutdown |
A Minimal, Working inittab
Here is an original example — not copied from any book — suitable for a small embedded target:
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/ash
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
The leading - before /bin/ash marks it as a login shell, which sources /etc/profile and $HOME/.profile before dropping you at a prompt. A login shell also enables job control, which is why Ctrl+C works to interrupt a foreground program — without it, a runaway ping would be unkillable from that console.
Writing the rcS Boot Script
/etc/init.d/rcS is where one-time boot setup belongs. A practical, modern version mounts the pseudo filesystems the rest of the system depends on:
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
echo "ep_demo: rootfs initialised" > /dev/kmsg
Make it executable before building the rootfs image:
$ cd ~/staging
$ chmod +x etc/init.d/rcS
Testing on QEMU and Real Hardware
On QEMU, point the kernel command line at your init binary:
qemu-system-arm -M virt -kernel zImage -initrd rootfs.cpio.gz \
-append "console=ttyAMA0 rdinit=/sbin/init" -nographic
On a real board using U-Boot, the equivalent is setting bootargs before booting:
=> setenv bootargs console=ttyS0,115200 rdinit=/sbin/init
=> boot
Common Mistakes and Troubleshooting
- Kernel panics with “no init found”: check that
rdinit=(initramfs) orinit=(disk-based rootfs) points to a path that actually exists and is executable inside the image. - rcS never runs: almost always a missing execute bit. Re-check with
chmod +xbefore repackaging. - Shell exits and nothing happens: you used
onceorwaitinstead ofaskfirst/respawnfor your console entry. - Ctrl+C doesn’t stop foreground programs: the shell wasn’t launched as a login shell (missing leading
-).
Best Practices
- Keep
rcSidempotent and fast — every extra second here delays boot on every single power-up. - Always add a
respawnentry for your console shell/getty so a crashed shell doesn’t brick the board until reboot. - Log rcS failures to
/dev/kmsgor a serial console rather than silently swallowing errors.
Summary and Key Takeaways
- BusyBox init is PID 1 and reads
/etc/inittabto decide what to start and how to supervise it. sysinitruns your boot script once;respawnkeeps critical processes alive;askfirstgates shell startup on user input.rcSis the right place for one-time mounts and setup, not long-running services.
Conclusion
Getting inittab and rcS right is the foundation every other rootfs feature builds on — user accounts, logging daemons, and device management all assume a working init has already brought the system to a stable state. In the next lecture we’ll configure user accounts on top of this foundation.
FAQ
What happens if /etc/inittab is missing?
BusyBox init falls back to a built-in default inittab, which is more permissive than a hand-written minimal one — fine for development, not recommended for production images.
Can I run systemd instead of BusyBox init?
Yes, on higher-end boards with enough storage and RAM, but for constrained embedded targets BusyBox init’s simplicity is usually preferable.
Why does my getty keep restarting in a loop?
That’s expected with a respawn action — it should restart every time the login shell exits, which is the intended supervised behavior.
Where do rcS errors get logged before syslogd starts?
Nowhere by default — redirect critical rcS output to /dev/kmsg or the console so you can see failures during early boot.
Is inittab syntax the same across all init systems?
No — this is BusyBox’s specific format. SysV init and systemd use different configuration mechanisms entirely.
Continue Your Free Embedded Linux Course
Next up: configuring user accounts in your root filesystem.

1 Comment