A tiny linux shell and init system

(Created 2026-03-24)

Introduction

How does a Linux computer work? Or I guess a better question is how do you get get from a kernel to user space? This article will briefly explain my journey, from compiling the Linux kernel up to the present moment.

Prerequisites

Not many things are needed for following along. GCC, QEMU, a compiled Linux kernel and Make should be enough. If you want to include a text editor like nano in the final system, you’ll either have to install it on your machine first, or have a compiled binary of it somewhere and then modify the build scripts slightly. Some libraries are also required, but most systems should have them by default already. The shell, init system and build scripts are available on my Github at: https://github.com/signallate/tiny-linux-sh

Compiling Linux

This has been coverd all over the internet already, in way better ways than I could cover, so I won’t write another guide for this. The best way is to visit the official Linux kernel Github. The build guide is at: https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/quickly-build-trimmed-linux.rst Make sure not to install the kernel (unless that’s the plan)! Only a compiled binary is needed, so be careful what commands you run and don’t break your system! After cloning the repo with something like:

git clone ...

and doing something like:

make defconfig
make -j $(nproc --all)

You can safely stop. Make should tell you where the built kernel image is, just remember that location for later.

Cloning the scripts

Now, after you’ve left the directory where you installed the kernel, clone the Github repo containing the shell, init system and scripts.

git clone https://github.com/signallate/tiny-linux-sh.git
cd tiny-linux-sh

Preparing for building

A few things are needed for building the final system. First, create these directories in the folder you just cloned:

mkdir build
mkdir kernel
mkdir rootfs

Now, move the compiled Linux kernel into the ‘kernel’ directory. It should be called something like ‘bzImage’. The rootfs directory will remain empty, as it’s just a mounting point. Now, I recommend you look through the scripts before running them. Make sure the commands that use ‘sudo’ won’t harm your particular system in any way, and make sure the proper binaries exist on your system. For example, in the ‘setup.sh’ script you may see this line:

cp /usr/lib/libc.so.6 "$PWD/rootfs/usr/lib/"

The problem is that libc may not be in /usr/lib/libc.so.6 so you’ll have to check. You can do that with:

whereis libc.so.6

Another problem is that you might see that your libc is named something different. The best way to check is to use ldd. For example:

ldd /bin/ls

Will show you what dynamic libraries the ‘ls’ program requires. Those need to be copied in the final system as well in order for ‘ls’ to run. Of course, dynamic libraries won’t work without a dynamic linker. This line in ‘setup.sh’ copies the linker over. It should be placed straight in rootfs/lib64 for it to work:

cp /usr/lib64/ld-linux-x86-64.so.2 "$PWD/rootfs/lib64/"

Building and running

Everything is now ready for building. If the paths are correct, all you’ll have to do now is just:

./makefs.sh 
./setup.sh 
make build
./copytools.sh

sudo will be requested for mounting and umounting. If a script won’t run, give it execute permissions with:

sudo chmod +x script.sh

Now that the thing is built, run it in QEMU with:

make run

This should open a QEMU window and after a bit of waiting you should see the shell being executed and then the prompt itself. It should now be possible to simply execute commands as you would on your own system. If anything errors out saying it couldn’t find x file, it’s probably a dynamic library missing and you’ll have to copy it manually. You should be able to then mount the generated rootfs image with:

sudo mount rootfs.img rootfs/
sudo chmod -R 755 rootfs

Then open the folder and copy over any dynamic libraries needed. After you’re done, make sure to umount rootfs.

Conclusion

Linux distros do not operate on magic, contrary to popular belief (myself included). This project helped me realize that fact, and also helped me understand the kernel at a deeper level. I’m sure this will be useful to me for many other projects to come.

Thanks for reading!