Later the rootfs is mounted and initrd is unmounted from the Linux OS
It is not exactly "unmounted"; most Linux distributions haven't used a mountable initrd for years. Instead, they use an "initramfs" which is an archive that gets extracted into a RAM filesystem (tmpfs) that's initially mounted at "/". Even though it is still specified using the initrd=
option, it behaves completely differently from the original initrd mechanism.
My question is when the initrd is unmounted, why the directories /etc, /lib, /bin, /usr,.. and their contents are still present?
Because they are not actually "/etc" or "/lib" from the initramfs point of view. The initramfs doesn't mount "/" directly – the initramfs itself is "/" – instead it mounts the real root filesystem at something like "/mnt" or "/newroot", so those directories start off as "/newroot/lib" and such.
One of the final steps done by the initramfs is to either "pivot_root()" or "mount(MS_MOVE)", an operation that makes "/newroot" become the new "/", essentially exchanging the two mounts (the old "/" which was the initramfs is moved to a sub-mount).
The minimal "/bin" and "/etc" and "/lib" that were unpacked from the initramfs do disappear during this process.
In other words, the process is approximately:
- The kernel mounts an empty tmpfs at
/
.
- The kernel unpacks the initramfs.cpio archive into the in-memory
/
, which contains some minimal files in /bin, /lib, etc.
- The kernel runs
/init
, which is the "core" of the initramfs.
- The initramfs /init script reads "root=" from the kernel command line (or uses some other method) and mounts the real root filesystem at
/newroot
or something similar. At this point, your files are at /newroot/lib, /newroot/home, and so on.
- The initramfs optionally mounts other necessary things under /newroot, such as a tmpfs at "/newroot/run", or a NFS-based /usr as "/newroot/usr".
- The initramfs /init turns
/newroot
into /
using, I believe, one of two methods:
- it deletes all files that were unpacked into the initramfs "/", then moves the "/newroot" mount onto "/" (overlaying the now-empty tmpfs which stays there forever) and uses chroot() to make it permanent – this is what's done by Arch Linux's initramfs using the switch_root tool;
- or it calls pivot_root() to "swap" the initramfs / with /newroot, turning the latter into the new / and moving the initramfs to something like /run/initramfs which can then be unmounted – not sure if this method is still used.
- Finally, now that "/" is the real root, /init (still running from memory) executes the real root filesystem's
/sbin/init
.
Often the initramfs /init is a simple shell script (though not always; it could even be a full copy of systemd). You can take a look at the Arch mkinitcpio /init. This internally uses the "overlay using mount(MS_MOVE)" method; see e.g. switch_root from util-linux.