Initramfs concepts
Introduction
For many users, an initramfs system is of no concern. Their system uses a simple
partitioning schema with no exotic drivers or setups (like encrypted file
systems), so the Linux kernel is well able to hand over control to the
init binary on their system. But for many systems, an initramfs is almost
mandatory.
The key concept to understanding what an initramfs is (or is needed for) is to
understand how the Linux boot process works, even in a high-level approach.
Linux boot process
Once the Linux kernel has control over the system (which it gets after being
loaded by the boot loader), it prepares its memory structures and drivers as
good as it can. It then hands over control to an application (usually
init) whose task it is to further prepare the system and make sure that,
at the end of the boot process, all necessary services are running and the user
is able to log on. The init application does that by launching, amongst
other services, the udev daemon who will further load up and prepare the
system based on the detected devices. When udev is launched, all
remaining file systems that have not been mounted are mounted, and the remainder
of services is started.
For systems where all necessary files and tools reside on the same file system,
the init application can perfectly control the further boot process. But
when multiple file systems are defined (or more exotic installations are done),
this might become a bit more tricky:
-
When the /usr partition is on a separate file system, tools and
drivers that have files stored within /usr cannot be used
unless /usr is available. If those tools are needed to make
/usr available, then we cannot boot up the system.
-
If the root file system is encrypted, then the Linux kernel will not be able
to find the init application, resulting in an unbootable system.
The solution for this problem has since long been to use an initrd
(initial root device).
The initial root disk
The initrd is an in-memory disk structure (ramdisk) that
contains the necessary tools and scripts to mount the needed file systems before
control is handed over to the init application on the root file system.
The Linux kernel triggers the setup script (usually called linuxrc but
that is not mandatory) on this root disk, which prepares the system, switches to
the real root file system and then calls init.
Although the initrd method is all that is needed, it had a few drawbacks:
-
It is a full-fledged block device, requiring the overhead of an entire
file system on it, and has a fixed size. Choose an initrd that is too small,
and you won't be able to fit in all needed scripts. Make it too big, and
you're wasting memory.
-
Because it is a real device, it also consumes cache memory in the Linux
kernel and is prone to the memory- and file management methods in use (such
as paging), making it even worse in memory consumption.
To resolve these (for some perhaps hardly called) issues, the initramfs was
created.
The initial ram file system
An initramfs is an initial ram file system based on tmpfs
(a size-flexible, in-memory lightweight file system), which also didn't use a
separate block device (so no caching was done and all overhead mentioned earlier
disappears). Just like the initrd, it contains the tools and scripts needed to
mount the file systems before the init binary on the real root file
system is called. These tools can be decryption abstraction layers (for
encrypted file systems), logical volume managers, software raid, bluetooth
driver based file system loaders, etc.
The content of the initramfs is made by creating a cpio archive. cpio is
an old (but proven) file archiver solution (and its resulting archive files are
called cpio files). You can definitely compare it to tar. The
choice of cpio here was because it is easier to implement (code-wise) and
supported (back then) device files (which tar couldn't).
All files, tools, libraries, configuration settings (if applicable), etc. are
put into the cpio archive. This archive is then compressed using the gzip
utility and stored alongside the Linux kernel. The boot loader will then offer
it to the Linux kernel at boot time so the kernel knows an initramfs is needed.
Once detected, the Linux kernel will create a tmpfs file system, extract the
contents of the archive on it, and then launches the init script located
in the root of the tmpfs file system. This script will then mount the real root
file system (after making sure it can mount it, for instance by loading
additional modules, preparing an encryption abstraction layer, etc.) as well as
vital other file systems (such as /usr and /var).
Once the root file system and the other vital file systems are mounted, the
init script from the initramfs will switch the root towards the real root
file system and finally call the /sbin/init on that system to continue
the boot process.
Creating an initramfs
Introduction and bootloader configuration
To create an initramfs, it is important that you know what additional drivers,
scripts and tools you need to boot your system. For instance, if you use LVM,
then you will need to support LVM tools on the initramfs. Likewise, if you use
software RAID, you need mdadm, etc.
Some tools exist that help you create initramfs' (compressed cpio archives) for
your system. But for those that want total control, you can easily create your
own initramfs as well.
Once created, you will need to adjust the bootloader configuration to tell it
that an initramfs is to be used. For instance, if the initramfs file is stored
as /boot/initramfs-3.2.2-gentoo-r5, then the configuration in
/boot/grub/grub.conf could look like so:
title Gentoo Linux 3.2.2-r5
root (hd0,0)
kernel /boot/kernel-3.2.2-gentoo-r5
initrd /boot/initramfs-3.2.2-gentoo-r5
Using genkernel
Gentoo's kernel building utility, genkernel, can be used to generate an
initramfs, even if you didn't use genkernel to configure and build your
kernel.
To use genkernel for generating an initramfs, it is recommended that you
include all necessary drivers and code that is needed to mount your
/ and /usr file systems in the kernel (and not as
modules). Then, call genkernel as follows:
# genkernel --install --no-ramdisk-modules initramfs
Depending on your system, you might want to add one or more of the following
options:
| Option |
Description |
--disklabel
Add support for LABEL= settings in your /etc/fstab
--dmraid
Add support for fake hardware RAID
--firmware
Add in firmware code found on the system
--gpg
Add in GnuPG support
--iscsi
Add support for iSCSI
--luks
Add support for luks encryption containers
--lvm
Add support for LVM
--mdadm
Add support for software RAID
--multipath
Add support for multiple I/O access towards a SAN
--zfs
Add support for ZFS
When finished, the resulting initramfs file will be stored in your
/boot.
Using dracut
At the time of writing, dracut is not marked stable yet, so you might need to
unmask it before continuing.
The dracut utility is created for the sole purpose of managing initramfs
files. It uses a highly modular approach on which support you want to include
and which not.
When you install dracut, you will need to take care to include support
for the correct DRACUT_MODULES. This is a variable you can set in
/etc/make.conf to include support for specific setups:
DRACUT_MODULES="dmraid lvm syslog -biosdevname -btrfs -caps -crypt -crypt-gpg
-dmsquash-live -gensplash -iscsi -livenet -mdraid -multipath -nbd -nfs -plymouth
-ssh-client"
It is adviseable to set (or unset) those modules you need (and don't need).
Afterwards, emerge dracut to install the utility on your system.
The next step is to configure dracut by editing
/etc/dracut.conf. In the configuration file, which is well
commented, you can add in support for specific modules where needed.
Once configured, create an initramfs by calling dracut as follows:
# dracut
The resulting image supports generic system boots based on the configuration in
/etc/dracut.conf. You can also opt to generate an initramfs
specifically tailored to your system (in which dracut tries to detect the
needed tools, drivers, etc. from your existing system). If you know that the
needed support (code and drivers) is built in in your kernel (and not as
module), then you can even add in --no-kernel:
# dracut --host-only --no-kernel
For more information, check out the dracut and dracut.cmdline
manual pages.