Linux – Difference in execution of init with embedded vs. external initramfs

bootinitramfskernellinux

I'm building a very minimal Linux system that just consists of the kernel (v4.1-rc5) and an initramfs populated with busybox (v1.23.2). It works fine for the most part, but I observe a difference in behavior of command execution in /init whether I'm using an embedded initramfs vs. an external one.

The /init script is:

#!/bin/sh

dmesg -n 1

mount -t devtmpfs none /dev
mount -t sysfs none /sys
mount -t proc none /proc
echo "Welcome"
while true
do
    setsid cttyhack /bin/sh
done

Then I either set the CONFIG_INITRAMFS_SOURCE option in the kernel .config to the directory containing all the folders for the initramfs, or I run

find . | cpio -H newc -o | gzip > ../rootfs.cpio.gz

to build it.

When I then compile the kernel, either with or without CONFIG_INITRAMFS_SOURCE set, I end up with two variants of my system:

  1. bzImage with initramfs embedded

  2. bzImage + rootfs.cpio.gz (external initramfs)

when I now start those using qemu

qemu-system-x86_64 -enable-kvm -kernel bzImage

or

qemu-system-x86_64 -enable-kvm -kernel bzImage -initrd rootfs.cpio.gz

I get the following difference in behavior:

with version 2 (external initramfs) everything works fine, "Welcome" is displayed and I get a prompt. With version 1 however (embedded initramfs) I get the warning

unable to open an initial console

"Welcome" is not displayed, and I get my prompt.

As far as I understand the process, those two versions of initramfs should contain the same files, since I build it (or have the kernel build it) from an identical folder.

I wonder if anyone can help me with an explanation for this behavior?

* UPDATE *

as mikeserv said in the comments, The kernel includes a minimal embedded initramfs per default. This is still present when using an external one, but gets overwritten if you embed your own. I found that contrary to the specification, this is indeed not empty, but contains a dev folder, a root folder and the /dev/console device. This device then gets used when using an external initramfs, but overwritten if you embed your own. So you have to include the /dev/console device in your initramfs source mknod -m 622 initramfs_src/dev/console c 5 1 when embedding your own.

Thanks a lot to mikeserv, frostschutz and JdeBP for helping me get my head around that!

Best Answer

Are they really identical?

The built-in one you can find in /usr/src/linux/usr/initramfs_data.cpio.gz or extract it from the bzImage as described here: https://wiki.gentoo.org/wiki/Custom_Initramfs#Salvaging

If you use that built-in one and use it as external one instead, does it work?

If it's still different, is the kernel itself identical? (compare /proc/config.gz for both)

There should be some difference. I'm not aware that the kernel cares where the initramfs came from. I'd sooner suspect qemu of using different settings when passing the -initrd parameter...

On a sidenote, your /init looks like its spawning infinite shells to me. setsid is not exec. Am I wrong?

Related Question