The Linux® initial RAM disk (initrd) is a temporary root file system
that is mounted during system boot to support the two-state boot
process. The initrd contains various executables and drivers that permit
the real root file system to be mounted, after which the initrd RAM
disk is unmounted and its memory freed. In many embedded Linux systems,
the initrd is the final root file system. This article explores the
initial RAM disk for Linux 2.6, including its creation and use in the
Linux kernel.
What's an initial RAM disk?
The initial RAM disk (initrd) is an initial root file system that is mounted prior to when the real root file system is available. The initrd is bound to the kernel and loaded as part of the kernel boot procedure. The kernel then mounts this initrd as part of the two-stage boot process to load the modules to make the real file systems available and get at the real root file system.
The initrd contains a minimal set of directories and executables to achieve this, such as the
In the case of desktop or server Linux systems, the initrd is a transient file system. Its lifetime is short, only serving as a bridge to the real root file system. In embedded systems with no mutable storage, the initrd is the permanent root file system. This article explores both of these contexts.
Anatomy of the initrd
The initrd image contains the necessary executables and system files to support the second-stage boot of a Linux system.
Depending on which version of Linux you're running, the method for creating the initial RAM disk can vary. Prior to Fedora Core 3, the initrd is constructed using the loop device. The loop device is a device driver that allows you to mount a file as a block device and then interpret the file system it represents. The loop device may not be present in your kernel, but you can enable it through the kernel's configuration tool (
Listing 1. Inspecting the initrd (prior to FC3)
You
can now inspect the /mnt/initrd subdirectory for the contents of the
initrd. Note that even if your initrd image file does not end with the
.gz suffix, it's a compressed file, and you can add the .gz suffix to
gunzip it.
Beginning with Fedora Core 3, the default initrd image is a compressed cpio archive file. Instead of mounting the file as a compressed image using the loop device, you can use a cpio archive. To inspect the contents of a cpio archive, use the following commands:
Listing 2. Inspecting the initrd (FC3 and later)
The
result is a small root file system, as shown in Listing 3. The small,
but necessary, set of applications are present in the ./bin directory,
including
Listing 3. Default Linux initrd directory structure
Of
interest in Listing 3 is the init file at the root. This file, like the
traditional Linux boot process, is invoked when the initrd image is
decompressed into the RAM disk. We'll explore this later in the article.
Tools for creating an initrd
Let's now go back to the beginning to formally understand how the initrd image is constructed in the first place. For a traditional Linux system, the initrd image is created during the Linux build process. Numerous tools, such as
Manually building a custom initial RAM disk
Note: Change RDSIZE to your required filesystem size. E.g. with RDSIZE =8192 you will get a 8MB ramdisk. BLKSIZE=1024
Note: Don't forget to create/copy some basic /dev/xxx nodes to ramdisk.
Note: If BusyBox or applications in ramdisk are linked dynamically, don't forget to copy dynamic libraries (*.so) to ramdisk (to correct directory) as well.
Creating a utility to generate ramdisk
Because there is no hard drive in many embedded systems based on Linux, the initrd also serves as the permanent root file system. Listing 4 shows how to create an initrd image. I'm using a standard Linux desktop so you can follow along without an embedded target. Other than cross-compilation, the concepts (as they apply to initrd construction) are the same for an embedded target.
Listing 4. Utility (mkird) to create a custom initrd
To create an initrd, begin by creating an empty file, using
The next step is creating the necessary subdirectories that make up your root file system: /bin, /sys, /dev, and /proc. Only a handful are needed (for example, no libraries are present), but they contain quite a bit of functionality.
To make your root file system useful, use BusyBox. This utility is a single image that contains many individual utilities commonly found in Linux systems (such as ash, awk, sed, insmod, and so on). The advantage of BusyBox is that it packs many utilities into one while sharing their common elements, resulting in a much smaller image. This is ideal for embedded systems. Copy the BusyBox image from its source directory into your root in the /bin directory. A number of symbolic links are then created that all point to the BusyBox utility. BusyBox figures out which utility was invoked and performs that functionality. A small set of links are created in this directory to support your init script (with each command link pointing to BusyBox).
The next step is the creation of a small number of special device files. I copy these directly from my current /dev subdirectory, using the
The penultimate step is to generate the linuxrc file. After the kernel mounts the RAM disk, it searches for an
Finally, your root file system is complete. It's unmounted and then compressed using
Using opensource utility to generate ramdisk
genext2fs -b <RDSIZE> -d src -f dev.txt flashdisk.img
This example builds a filesystem from all the files in src , then device nodes are created based on the contents of the device file dev.txt.
An example device file follows:
drwx /dev crw- 10,190 /dev/lcd brw- 1,0 /dev/ram0
This device list builds the /dev directory, a character device node /dev/lcd (major 10, minor 190) and a block device node /dev/ram0 (major 1, minor 0)
#
# General setup
#
...
CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_SOURCE=""
...
#
# UBI - Unsorted block images
#
...
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_COUNT=1
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
...
Note: The ramdisk size e.g. 8192 above has to be configured for your individual setup.
Initramfs
To use initramfs a cpio archive is embedded directly into the kernel. I.e. you don't create an additional (ramdisk) image. Instead, the initial file system is directly incorporated into the kernel. With this, the kernel size increases by the file system size. It's like you embed above ramdisk directly into the kernel.
Create target file system
Note: To be able to detect initramfs by kernel properly, the top level directory has to contain a program called init. This can be done by e.g. using a soft link from top level init to /bin/busybox
What's an initial RAM disk?
The initial RAM disk (initrd) is an initial root file system that is mounted prior to when the real root file system is available. The initrd is bound to the kernel and loaded as part of the kernel boot procedure. The kernel then mounts this initrd as part of the two-stage boot process to load the modules to make the real file systems available and get at the real root file system.
The initrd contains a minimal set of directories and executables to achieve this, such as the
insmod tool to install kernel modules into the kernel.In the case of desktop or server Linux systems, the initrd is a transient file system. Its lifetime is short, only serving as a bridge to the real root file system. In embedded systems with no mutable storage, the initrd is the permanent root file system. This article explores both of these contexts.
Anatomy of the initrd
The initrd image contains the necessary executables and system files to support the second-stage boot of a Linux system.
Depending on which version of Linux you're running, the method for creating the initial RAM disk can vary. Prior to Fedora Core 3, the initrd is constructed using the loop device. The loop device is a device driver that allows you to mount a file as a block device and then interpret the file system it represents. The loop device may not be present in your kernel, but you can enable it through the kernel's configuration tool (
make menuconfig) by selecting Device Drivers > Block Devices > Loopback Device Support. You can inspect the loop device as follows (your initrd file name will vary):Listing 1. Inspecting the initrd (prior to FC3)
# mkdir temp ; cd temp # cp /boot/initrd.img.gz . # gunzip initrd.img.gz # mount -t ext -o loop initrd.img /mnt/initrd # ls -la /mnt/initrd |
Beginning with Fedora Core 3, the default initrd image is a compressed cpio archive file. Instead of mounting the file as a compressed image using the loop device, you can use a cpio archive. To inspect the contents of a cpio archive, use the following commands:
Listing 2. Inspecting the initrd (FC3 and later)
# mkdir temp ; cd temp # cp /boot/initrd-2.6.14.2.img initrd-2.6.14.2.img.gz # gunzip initrd-2.6.14.2.img.gz # cpio -i --make-directories < initrd-2.6.14.2.img |
nash (not a shell, a script interpreter), insmod for loading kernel modules, and lvm (logical volume manager tools).Listing 3. Default Linux initrd directory structure
# ls -la # drwxr-xr-x 10 root root 4096 May 7 02:48 . drwxr-x--- 15 root root 4096 May 7 00:54 .. drwxr-xr-x 2 root root 4096 May 7 02:48 bin drwxr-xr-x 2 root root 4096 May 7 02:48 dev drwxr-xr-x 4 root root 4096 May 7 02:48 etc -rwxr-xr-x 1 root root 812 May 7 02:48 init -rw-r--r-- 1 root root 1723392 May 7 02:45 initrd-2.6.14.2.img drwxr-xr-x 2 root root 4096 May 7 02:48 lib drwxr-xr-x 2 root root 4096 May 7 02:48 loopfs drwxr-xr-x 2 root root 4096 May 7 02:48 proc lrwxrwxrwx 1 root root 3 May 7 02:48 sbin -> bin drwxr-xr-x 2 root root 4096 May 7 02:48 sys drwxr-xr-x 2 root root 4096 May 7 02:48 sysroot # |
Tools for creating an initrd
Let's now go back to the beginning to formally understand how the initrd image is constructed in the first place. For a traditional Linux system, the initrd image is created during the Linux build process. Numerous tools, such as
mkinitrd, can be used to automatically build
an initrd with the necessary libraries and modules for bridging to the
real root file system. The mkinitrd utility is actually a shell script, so you can see exactly how it achieves its result. There's also the YAIRD (Yet Another Mkinitrd) utility, which permits customization of every aspect of the initrd construction.Manually building a custom initial RAM disk
Creation
To create an (initially empty) initrd use the following steps:Note: Change RDSIZE to your required filesystem size. E.g. with RDSIZE =8192 you will get a 8MB ramdisk. BLKSIZE=1024
host > dd if=/dev/zero of=<filename> bs=<BLKSIZE> count=<RDSIZE>
host > mke2fs -vm0 <filename> < RDSIZE >
host > tune2fs -c 0 <filename>
host > dd if=<filename> b=<BLKSIZE> count=< RDSIZE > | gzip -v9 > ramdisk.gz (or)
host > dd if=/dev/zero of=<filename> bs=<BLKSIZE> count=<RDSIZE>
host > mke2fs -F -m 0 -b <BLKSIZE> <filename> < RDSIZE >| gzip -v9 > ramdisk.Now, we have a (empty) gzipped ramdisk image with (extracted) size of < RDSIZE >.
Filling
To fill empty ramdisk created above with all files needed for ramdisk, mount the image and fill it. Content would be e.g. BusyBox and/or other applications and/or libraries.host > mkdir mnt
host > gunzip ramdisk.gz
host > mount -o loop ramdisk mnt/
host > ... copy stuff you want to have in ramdisk to mnt...
host > umount mnt
host > gzip -v9 ramdiskThe resulting ramdisk.gz is now ready for usage. Note its size is smaller than <count> cause of compression.
Note: Don't forget to create/copy some basic /dev/xxx nodes to ramdisk.
Note: If BusyBox or applications in ramdisk are linked dynamically, don't forget to copy dynamic libraries (*.so) to ramdisk (to correct directory) as well.
Creating a utility to generate ramdisk
Because there is no hard drive in many embedded systems based on Linux, the initrd also serves as the permanent root file system. Listing 4 shows how to create an initrd image. I'm using a standard Linux desktop so you can follow along without an embedded target. Other than cross-compilation, the concepts (as they apply to initrd construction) are the same for an embedded target.
Listing 4. Utility (mkird) to create a custom initrd
#!/bin/bash # Housekeeping... rm -f /tmp/ramdisk.img rm -f /tmp/ramdisk.img.gz # Ramdisk Constants RDSIZE=4000 BLKSIZE=1024 # Create an empty ramdisk image dd if=/dev/zero of=/tmp/ramdisk.img bs=$BLKSIZE count=$RDSIZE # Make it an ext2 mountable file system /sbin/mke2fs -F -m 0 -b $BLKSIZE /tmp/ramdisk.img $RDSIZE # Mount it so that we can populate mount /tmp/ramdisk.img /mnt/initrd -t ext2 -o loop=/dev/loop0 # Populate the filesystem (subdirectories) mkdir /mnt/initrd/bin mkdir /mnt/initrd/sys mkdir /mnt/initrd/dev mkdir /mnt/initrd/proc # Grab busybox and create the symbolic links pushd /mnt/initrd/bin cp /usr/local/src/busybox-1.1.1/busybox . ln -s busybox ash ln -s busybox mount ln -s busybox echo ln -s busybox ls ln -s busybox cat ln -s busybox ps ln -s busybox dmesg ln -s busybox sysctl popd # Grab the necessary dev files cp -a /dev/console /mnt/initrd/dev cp -a /dev/ramdisk /mnt/initrd/dev cp -a /dev/ram0 /mnt/initrd/dev cp -a /dev/null /mnt/initrd/dev cp -a /dev/tty1 /mnt/initrd/dev cp -a /dev/tty2 /mnt/initrd/dev # Equate sbin with bin pushd /mnt/initrd ln -s bin sbin popd # Create the init file cat >> /mnt/initrd/linuxrc << EOF #!/bin/ash echo echo "Simple initrd is active" echo mount -t proc /proc /proc mount -t sysfs none /sys /bin/ash --login EOF chmod +x /mnt/initrd/linuxrc # Finish up... umount /mnt/initrd |
/dev/zero
(a stream of zeroes) as input writing to the ramdisk.img file. The
resulting file is 4MB in size (4000 1K blocks). Then use the mke2fs
command to create an ext2 (second extended) file system using the empty
file. Now that this file is an ext2 file system, mount the file to
/mnt/initrd using the loop device. At the mount point, you now have a
directory that represents an ext2 file system that you can populate for
your initrd. Much of the rest of the script provides this functionality.The next step is creating the necessary subdirectories that make up your root file system: /bin, /sys, /dev, and /proc. Only a handful are needed (for example, no libraries are present), but they contain quite a bit of functionality.
To make your root file system useful, use BusyBox. This utility is a single image that contains many individual utilities commonly found in Linux systems (such as ash, awk, sed, insmod, and so on). The advantage of BusyBox is that it packs many utilities into one while sharing their common elements, resulting in a much smaller image. This is ideal for embedded systems. Copy the BusyBox image from its source directory into your root in the /bin directory. A number of symbolic links are then created that all point to the BusyBox utility. BusyBox figures out which utility was invoked and performs that functionality. A small set of links are created in this directory to support your init script (with each command link pointing to BusyBox).
The next step is the creation of a small number of special device files. I copy these directly from my current /dev subdirectory, using the
-a option (archive) to preserve their attributes.The penultimate step is to generate the linuxrc file. After the kernel mounts the RAM disk, it searches for an
init file to execute. If an init
file is not found, the kernel invokes the linuxrc file as its startup
script. You do the basic setup of the environment in this file, such as
mounting the /proc file system. In addition to /proc, I also mount the
/sys file system and emit a message to the console. Finally, I invoke ash (a Bourne Shell clone) so I can interact with the root file system. The linuxrc file is then made executable using chmod.Finally, your root file system is complete. It's unmounted and then compressed using
gzip.Using opensource utility to generate ramdisk
genext2fs -b <RDSIZE> -d src -f dev.txt flashdisk.img
This example builds a filesystem from all the files in src , then device nodes are created based on the contents of the device file dev.txt.
An example device file follows:
drwx /dev crw- 10,190 /dev/lcd brw- 1,0 /dev/ram0
This device list builds the /dev directory, a character device node /dev/lcd (major 10, minor 190) and a block device node /dev/ram0 (major 1, minor 0)
Kernel options
To make initrd work, you have to configure kernel properly:#
# General setup
#
...
CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_SOURCE=""
...
#
# UBI - Unsorted block images
#
...
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_COUNT=1
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
...
Note: The ramdisk size e.g. 8192 above has to be configured for your individual setup.
Initramfs
To use initramfs a cpio archive is embedded directly into the kernel. I.e. you don't create an additional (ramdisk) image. Instead, the initial file system is directly incorporated into the kernel. With this, the kernel size increases by the file system size. It's like you embed above ramdisk directly into the kernel.
Creation
Cause initramfs is directly embedded in the the kernel, its creation is simpler. No dd & mount & gzip stuff like with ramdisk above. You simply have to fill a directory on your host with the target filesystem you like and then pass the path to this directory to the kernel build process.Create target file system
host > mkdir target_fs
host > ... copy stuff you want to have in initramfs to target_fs...Note: cpio system used for initramfs can't handle hard links. If you e.g. created your BusyBox using hard links, you will get a quite large initramfs cause each command is taken with its size and not as hard link. In cpio initramfs use symbolic/soft links instead.
Note: To be able to detect initramfs by kernel properly, the top level directory has to contain a program called init. This can be done by e.g. using a soft link from top level init to /bin/busybox
/init -> /bin/busyboxif you use BusyBox in your initramfs.
Kernel options
The only difference from creating an initrd is to give the kernel the path to the target file system you like to embed:# # General setup # ... CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="<path_to>/target_fs>"...
# # UBI - Unsorted block images # ... CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_COUNT=1 CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 ...