What are Linux Early User Space Init-Free Embedded Linux Course

Linux Early User Space Init
Trace exactly what the kernel does in the seconds between finishing its own startup and handing control to your init program
free linux kernel development course
free linux development course
free embedded linux course
early user space
init process

By this point in our free linux kernel development course you have built a kernel, booted it, and even learned to diagnose a panic when no root filesystem was available. This lecture fills in the missing piece: what the kernel actually does, step by step, in the moment it successfully transitions from its own internal startup into running your first user-space program. Understanding this sequence turns “the kernel booted” from a black box into something you can reason about and control precisely.

What You Will Learn

  • Where the kernel-to-user-space transition actually lives in the kernel source
  • The exact order the kernel tries candidate init programs on a mounted root filesystem
  • How rdinit= and init= differ and when each is used
  • How to override the init program for debugging without touching your root filesystem image
  • A small original demo showing a custom init script taking over PID 1

Prerequisites

  • A kernel that successfully mounts a root filesystem or ramdisk, from the previous lectures in this series
  • Basic shell scripting familiarity

Where This Happens In The Kernel

The transition from kernel initialization to user space is orchestrated from init/main.c, in a function called rest_init(). This function creates the very first user-visible process, PID 1, and hands it off to run the code in kernel_init(). Everything you think of as “the system starting up” — mounting filesystems, running your init program, eventually reaching a login prompt or your application — happens downstream of this one handoff point.

From rest_init To A Running Init Program
init/main.c: rest_init()
│
▼
creates PID 1, runs kernel_init()
│
▼
Is there a ramdisk?
│ │
yes no
│ │
▼ ▼
try /init prepare_namespace()
│ │
found? mounts root= device
│ │
yes ▼
│ try /sbin/init
▼ │
exec /init try /etc/init
try /bin/init
try /bin/sh

Path One: A Ramdisk With /init

If the kernel was handed a ramdisk (an initramfs or initrd), kernel_init() first looks for a program named /init at the root of that ramdisk and, if found, executes it immediately. This is the mechanism modern distributions use to run early setup logic — mounting the real root filesystem, loading essential modules, handling encrypted storage — before ever reaching the “real” init system.

Path Two: Mounting A Real Root Filesystem

If there is no ramdisk, or the ramdisk has no /init to execute, the kernel falls back to prepare_namespace(), defined in init/do_mounts.c. This function reads the root= command-line parameter to determine which block device and partition to mount as the root filesystem, typically in one of these forms:

root=/dev/<disk><partition-number>      # e.g. root=/dev/sda1
root=/dev/<disk>p<partition-number>      # e.g. root=/dev/mmcblk0p1

Once that filesystem is mounted, the kernel searches for a program to execute as PID 1 in a fixed, fallback order, stopping at the first one that exists and is executable:

/sbin/init
/etc/init
/bin/init
/bin/sh

That last fallback, /bin/sh, is worth remembering — it means even a nearly empty root filesystem with just a shell binary and its libraries is enough to get an interactive prompt, which is exactly why rdinit=/bin/sh works so well as a diagnostic tool, as we saw in the previous panic-debugging lecture.

Overriding Init: rdinit= Versus init=

Both parameters let you override which program runs as PID 1, but they target different boot paths and are easy to confuse:

Parameter Applies To Example
rdinit= The ramdisk path — overrides the default /init lookup rdinit=/bin/sh
init= The mounted-filesystem path — overrides the /sbin/init fallback search init=/bin/sh

Using the wrong one is a common source of confusion: setting init=/bin/sh when you actually booted from a ramdisk has no effect at all, because the ramdisk path never consults init= in the first place — it only looks at rdinit=.

Try It: A Custom Init Script

To see this mechanism directly, you can override init on a real root filesystem boot with a tiny diagnostic script instead of the real init system. Place this at /ep_init.sh on your target root filesystem and make it executable:

#!/bin/sh
echo "ep_init: this process is PID $$"
echo "ep_init: mounting /proc and /sys"
mount -t proc proc /proc
mount -t sysfs sysfs /sys
echo "ep_init: dropping to an interactive shell"
exec /bin/sh

Then boot with init= pointing directly at it, bypassing the normal /sbin/init search entirely:

=> setenv bootargs console=ttyS2,115200 root=/dev/mmcblk0p2 rootwait init=/ep_init.sh
=> bootz 0x80200000 - 0x80f00000
...
ep_init: this process is PID 1
ep_init: mounting /proc and /sys
ep_init: dropping to an interactive shell
/ #

Notice the script genuinely runs as PID 1 — confirmed by $$ printing 1 — which is exactly the behavior kernel_init() guarantees for whatever program it hands off to.

Common Mistakes And Troubleshooting

  • Using init= with a ramdisk boot: it silently has no effect; use rdinit= for ramdisk boots instead.
  • Init program not executable: the kernel silently skips a candidate that exists but lacks the execute bit, moving on to the next fallback — always chmod +x your init script.
  • Missing shared libraries for a custom init binary: a compiled init program that depends on a dynamic libc will fail to start if that libc is not present on the root filesystem; consider static linking for minimal images.
  • Forgetting to mount /proc and /sys: the kernel does not mount these for you — your init program is responsible for mounting them itself, as shown in the demo script.

Best Practices

  • Keep a minimal rdinit=/bin/sh or init=/bin/sh override in your notes for every board — it is the fastest way to get a diagnostic prompt when something later in boot is broken.
  • Understand which of the two override parameters applies to your current boot path before assuming an override “did not work.”
  • Have your init program mount /proc and /sys as early as possible, since many diagnostic tools depend on them.

Summary And Key Takeaways

  • The kernel-to-user-space handoff happens in rest_init(), which creates PID 1 and runs kernel_init().
  • A ramdisk boot looks for /init first, overridable with rdinit=.
  • A real root filesystem boot walks /sbin/init, /etc/init, /bin/init, then /bin/sh in order, overridable with init=.

Conclusion

Once you can trace the exact path from rest_init() to your first running program, “the kernel booted” stops being a mystery and becomes a sequence you can debug, override, and reason about with confidence. This understanding rounds out this section of our free linux development course. In the next lecture, we look at the other half of what the kernel is telling you throughout this whole process: how kernel log messages are categorized and how to make sense of them.

Frequently Asked Questions

What happens if none of /sbin/init, /etc/init, /bin/init, or /bin/sh exist?

The kernel has no program left to hand off to and panics, since a kernel with no user-space process to run is considered an unrecoverable state.

Can I use init= to point at a compiled C program instead of a shell script?

Yes. Any executable works, whether a shell script or a compiled binary, as long as it is present on the mounted root filesystem and marked executable.

Does the kernel automatically mount /proc and /sys for me?

No. Mounting /proc, /sys, and other pseudo-filesystems is the responsibility of whatever program runs as PID 1, typically among its very first actions.

Why does rdinit=/bin/sh work even on an almost-empty ramdisk?

Because it bypasses the normal search order entirely and executes exactly the program you named, as long as that program and any libraries it depends on are present in the ramdisk.

Is PID 1 special compared to other processes?

Yes. PID 1 has unique responsibilities in Linux, including reaping orphaned child processes, and the kernel treats it differently — for example, it cannot be sent most signals in the default way other processes can.

Go Deeper Into Embedded Linux Boot Internals

This lecture is part of EmbeddedPathashala’s free linux development course, built for students who want real depth, not shortcuts.

Leave a Reply

Your email address will not be published. Required fields are marked *