Initrd mechanism of Linux

The Linux initrd technology is a very commonly used mechanism. The initrd file format of the linux2.6 kernel has been changed from the original file system image file to the cpio format. The change is not only reflected in the file format, but also in the linux kernel. The handling of initrd is quite different. This article first introduces what is initrd technology, and then introduces the initrd processing flow of Linux 2.4 kernel and 2.6 kernel respectively. Finally, through the analysis of the initrd processing part of the Linux2.6 kernel code, the reader can have a comprehensive understanding of the initrd technology. In order to better read this article, readers are required to have a preliminary understanding of VFS and initrd in Linux.
-Content
1. What is Initrd
2.The processing flow of Initrd by the Linux2.4 kernel
3. Processing flow of Initrd by Linux2.6 kernel
5.Source code analysis of linux2.6 kernel initrd processing6
. Conclusion
1 . What is Initrd
The English meaning of initrd is boot loader initialized RAM disk, which is the memory disk initialized by boot loader. Before the Linux kernel starts, the boot loader will load the initrd file in the storage medium into the memory. When the kernel starts, it will first access the initrd file system in the memory before accessing the real root file system. In the case where the boot loader is configured with initrd, the kernel startup is divided into two stages. In the first stage, "a certain file" in the initrd file system is executed, and tasks such as loading the driver module are completed, and the second stage will execute the real The /sbin/init process in the root filesystem. The "a certain file" mentioned here, the Linux 2.6 kernel will be different from the previous version of the kernel, so the term "a certain file" is temporarily used here, which will be discussed in detail later. The purpose of the first stage startup is to clear all obstacles for the second stage startup, the most important thing is to load the drive module of the root file system storage medium. We know that the root file system can be stored on a variety of media including IDE, SCSI, and USB. If the drivers of these devices are compiled into the kernel, you can imagine how large and bloated the kernel will be.
There are four main purposes of Initrd:
1. An essential part of
linux distribution The linux distribution must adapt to various hardware architectures. It is unrealistic to compile all the drivers into the kernel. The initrd technology is the key to solving this problem. Technology. The Linux distribution only compiles the basic hardware drivers in the kernel. During the installation process, it detects the system hardware and generates an initrd containing the hardware drivers of the installation system, which is nothing more than a feasible and flexible solution.
2. Necessary components of livecd Compared
with linux distributions, livecd may face a more complex hardware environment, so initrd must also be used.
3. Make Linux usb boot disk must use initrd
USB devices are slow to boot, and it takes about a few seconds from the time the driver is loaded until the device is actually available. If the usb driver is compiled into the kernel, the kernel usually cannot successfully access the filesystem on the usb device. Because when the kernel accesses the usb device, the usb device is usually not initialized. So the conventional practice is to load the usb driver in initrd, then sleep for a few seconds, wait for the usb device to be initialized, and then mount the file system in the usb device.
4. Personalized bootsplash can be easily enabled in the linuxrc script.

2. The processing flow
of Linux 2.4 kernel to Initrd In order to make readers understand the changes of Linux 2.6 kernel initrd mechanism clearly, before focusing on Linux 2.6 kernel initrd, a brief introduction to linux 2.4 kernel initrd is given. The format of the initrd of the Linux2.4 kernel is a file system image file, which is called image-initrd in this article to distinguish the initrd of the cpio format of the Linux2.6 kernel described later. The linux2.4 kernel handles initrd as follows:
1. The boot loader loads the kernel and the contents of /dev/initrd into the memory. /dev/initrd is a device initialized by the boot loader and stores the initrd.
2. During the kernel initialization process, the kernel decompresses the contents of the /dev/initrd device and copies it to the /dev/ram0 device.
3. The kernel mounts the /dev/ram0 device as the original root file system in a readable and writable manner.
4. If /dev/ram0 is designated as the real root file system, then the kernel jumps to the last step and starts normally.
5. Execute the /linuxrc file on the initrd. linuxrc is usually a script file, which is responsible for loading the necessary drivers for the kernel to access the root file system and loading the root file system.
6. After the execution of /linuxrc is completed, the real root file system is mounted.
7. If the /initrd directory exists for the real root filesystem, then /dev/ram0 will be moved from / to /initrd. Otherwise if the /initrd directory does not exist, /dev/ram0 will be unmounted.
8. Go through the normal boot process on the real root filesystem and execute /sbin/init. The execution of the initrd of the linux2.4 kernel is an intermediate stage of the kernel startup, that is to say, after the execution of /linuxrc of the initrd, the kernel will continue to execute the initialization code. We will see later that this is the initrd of the linux2.4 kernel and the 2.6 kernel. A notable difference in processing flow.

3. The processing flow of Initrd by the Linux2.6 kernel The
linux2.6 kernel supports two formats of initrd, one is the file system image-image-initrd in the traditional format of the linux2.4 kernel introduced in the third part, and its production method Like the initrd of the Linux2.4 kernel, its core file is /linuxrc. Another format of initrd is cpio format. This format of initrd has been introduced since linux 2.5 and is generated by the cpio tool. Its core file is no longer /linuxrc, but /init. This article calls this initrd as cpio-initrd. Although the linux2.6 kernel supports both initrds in cpio-initrd and image-initrd formats, there are significant differences in their processing procedures. The following describes the processing procedures of the linux2.6 kernel for these two initrds.
The processing flow of cpio-initrd 1. The
boot loader loads the kernel and the initrd file into a specific location in the memory.
2. The kernel judges the file format of initrd, if it is cpio format.
3. Release the contents of initrd to rootfs.
4. Execute the /init file in initrd. At this point, the work of the kernel is all over, and it is completely handed over to the /init file for processing.
The processing flow of image-initrd 1. The
boot loader loads the kernel and the initrd file into a specific location in the memory.
2. The kernel judges the file format of initrd. If it is not in cpio format, it will be treated as image-initrd.
3. The kernel saves the contents of initrd in the /initrd.image file under rootfs.
4. The kernel reads the contents of /initrd.image into the /dev/ram0 device, that is, into a memory disk.
5. The kernel then mounts the /dev/ram0 device as the original root file system in a readable and writable manner.
6. If /dev/ram0 is designated as the real root filesystem, then the kernel skips to the last step and starts normally.
7. Execute the /linuxrc file on the initrd. linuxrc is usually a script file, which is responsible for loading the necessary drivers for the kernel to access the root file system and loading the root file system.
8. After execution of /linuxrc, the regular root file system is mounted.
9. If the regular root file system exists in the /initrd directory, then /dev/ram0 will be moved from / to /initrd. Otherwise if the /initrd directory does not exist, /dev/ram0 will be unmounted.
10. Go through the normal boot process on the regular root filesystem and execute /sbin/init.
From the above process introduction, it can be seen that the processing flow of image-initrd in Linux 2.6 kernel has no significant change compared with that in linux 2.4 kernel, while the processing flow of cpio-initrd is quite different from that of image-initrd. The difference, the process is very simple, in the subsequent source code analysis, readers can better appreciate the simplicity of processing.
4. Differences and advantages between cpio-initrd and image-initrd I
have not found a formal literature on the comparison of cpio-initrd and image-initrd. According to the author's experience and analysis of the kernel code, the following three differences are summarized, and these differences are also correct. This is the advantage of
cpio-initrd: The production method of
cpio-initrd is simpler. The production of cpio-initrd is very simple. The entire production process can be completed with two commands. #Assume
that the current directory is located in the root directory of the prepared initrd file system
bash # find . | cpio -c -o > ../initrd.img
bash# gzip ../initrd.img
The traditional initrd production process is cumbersome and requires the following six steps
#Assume the current directory is located in the prepared initrd file system
bash# dd if=/dev/zero of=../initrd.img bs=512k count=5 bash
# mkfs.ext2 -F -m0 ../initrd.img
bash# mount -t ext2 -o loop ../initrd.img /mnt
bash# cp -r * /mnt
bash# umount /mnt
bash# gzip -9 ../initrd.img
This article does not explain the meaning of the above commands in detail, because this article mainly introduces the processing of initrd by the linux kernel. Readers who do not understand the command can refer to the relevant documentation.
The kernel processing flow of cpio-initrd is more simplified.
Through the introduction of the initrd processing flow above, the processing flow of cpio-initrd is very simple. By comparison, it can be seen that the processing flow of cpio-initrd has been simplified in the following two aspects:
1.cpio-initrd No extra ramdisk is used, but its contents are entered into rootfs, which itself is also a memory-based file system. This saves the steps of mounting and unmounting the ramdisk.
2. After cpio-initrd starts the /init process, the task of the kernel is over, and the rest of the work is completely handed over to /init; for image-initrd, the kernel needs to do some finishing work after executing the /linuxrc process, and Responsible for executing /sbin/init of the real root filesystem.

The responsibilities of cpio-initrd are more important.
cpio-initrd is no longer an intermediate step of linux kernel startup like image-initrd, but as the end point of kernel startup. After the kernel gives control to the /init file of cpio-initrd, the kernel The task is over, so in the /init file, we can do more work than worrying about the connection with the subsequent processing of the kernel. Of course, the content of the /init file of the cpio-initrd of the Linux distribution has not changed substantially, but it is believed that the increase of initrd responsibilities must be a trend.

5. Source code analysis of initrd processing of linux2.6 kernel The
above briefly introduces the processing flow of initrd of Linux 2.4 kernel and 2.6 kernel. .6 The kernel initialization part of the code closely related to initrd is given a more detailed analysis. For the convenience of description, the concepts used in the code analysis are further clarified:
rootfs: a memory-based file system, which is the first file system loaded by linux during initialization. A file system, for a further introduction to it, please refer to [4].
initramfs: initramfs is not very related to the subject of this article, but initramfs is involved in the code. In order to better understand the code, here is a brief introduction. Initramfs is a technology introduced in kernel 2.5. In fact, its meaning is: attach a cpio package to the kernel image. This cpio package contains a small file system. When the kernel starts, the kernel unpacks the cpio package , and release the file system contained in it to rootfs, and part of the initialization code in the kernel will be placed in this file system and executed as a user-level process. The obvious advantage of this is to simplify the initialization code of the kernel, and make the initialization process of the kernel easier to customize. The initramfs of the Linux 2.6.12 kernel has nothing substantial yet, and the implementation of a fully functional initramfs may still require a slow process. For further understanding of initramfs, please refer to [1][2][3].
cpio-initrd: As defined above, it refers to the initrd in cpio format used by the Linux 2.6 kernel.
image-initrd: As defined above, it refers to the initrd in the traditional file image format.
realfs: The real file system that the user ultimately uses.
The initialization code of the kernel is located in the static int init(void * unused) function in init/main.c.
The init function is the entry point of all initialization codes of the kernel. The code is as follows, in which only the code related to initrd is retained.
static int init(void * unused){
​​[1]populate_rootfs();

[2]if (sys_access((const char __user *) "/init", 0) == 0)
execute_command = "/init";
else
prepare_namespace( );
[3]if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
printk(KERN_WARNING "Warning: unable to open an initial console.\n");
(void) sys_dup(0);
(void) sys_dup(0);
[4]if (execute_command)
run_init_process(execute_command);
run_init_process("/sbin/init");
run_init_process("/etc/init");

run_init_process("/bin/sh");
panic("No init found. Try passing init= option to kernel.");
}
Code[1]: The populate_rootfs function is responsible for loading initramfs and cpio-initrd, for the details of the populate_rootfs function later will speak.
Code [2]: If the /init process is contained in the root directory of rootfs, execute_command is assigned, and it will be executed at the end of the init function. Otherwise execute the prepare_namespace function, in which the initrd is loaded.
Code [3]: Set the console as standard input, and the following two sys_dup(0) will copy standard input as standard output and standard error output.
Code [4]: ​​If the init process exists in the rootfs, the subsequent processing work is handed over to the init process. In fact, the meaning of this code is that if cpio-initrd is loaded, it will be handed over to /init in cpio-initrd for processing, otherwise init in realfs will be executed. Readers may ask: If cpio-initrd is loaded, doesn't the init process in realfs have no chance to run? Indeed, if cpio-initrd is loaded, then the kernel will not be responsible for executing the init process of realfs, but hand over the execution task to the init process of cpio-initrd. Unzip the initrd file of fedora core4, you will find that the init file under the root directory is a script, and there is such a piece of code in the last line of the script:
……..
switchroot --movedev /sysroot
is the switchroot statement responsible for loading realfs, And the init process that executes realfs.

对cpio-initrd的处理
对cpio-initrd的处理位于populate_rootfs函数中。
void __init populate_rootfs(void){
[1]  char *err = unpack_to_rootfs(__initramfs_start,
__initramfs_end - __initramfs_start, 0);
[2]if (initrd_start) {
[3]err = unpack_to_rootfs((char *)initrd_start,
initrd_end - initrd_start, 1);

[4]if (!err) {
printk(" it is\n");
unpack_to_rootfs((char *)initrd_start,
initrd_end - initrd_start, 0);
free_initrd_mem(initrd_start, initrd_end);
return;
}
[5]fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 700);
if (fd >= 0) {
sys_write(fd, (char *)initrd_start,
initrd_end - initrd_start);
sys_close(fd);
free_initrd_mem(initrd_start, initrd_end);
}
}
Code [1]: Load initramfs, initramfs is located at the address __initramfs_start, which is generated by the kernel during the compilation process. The initramfs exists as a part of the kernel, not loaded by the boot loader. As mentioned earlier, initramfs does not have any substance now.
Code [2]: Determine whether initrd is loaded. Regardless of the format of initrd, it will be loaded into the address initrd_start by the boot loader.
Code [3]: Determine whether cpio-initrd is loaded. In fact, unpack_to_rootfs has two functions: one is to release the cpio package, and the other is to judge whether it is a cpio package, which is distinguished by the last parameter, 0: release 1: view.
Code [4]: ​​If it is cpio-initrd, release its contents to rootfs.
Code [5]: If it is not cpio-initrd, it is considered to be an image-initrd, and its contents are saved to /initrd.image. In the following image-initrd processing code, /initrd.image will be read.
The processing of image-initrd is in the prepare_namespace function, which includes the code for processing image-initrd. The relevant code is as follows:
void __init prepare_namespace(void){
[1]if (initrd_load())
goto out;
out:
umount_devfs("/dev");
[2]sys_mount(".", "/", NULL, MS_MOVE, NULL);
sys_chroot(".");
security_sb_post_mountroot();
mount_devfs_fs ();
}
Code[1]: Execute The initrd_load function loads the initrd. If the load is successful, the initrd_load function will set the root of realfs to the current directory.
Code [2]: Mount the current directory, that is, the root of realfs, as the root of Linux VFS. After the initrd_load function is executed, the root of the real file system is set to the current directory.
The initrd_load function is responsible for loading image-initrd, the code is as follows:
int __init initrd_load(void)
{
[1]if (mount_initrd) {
create_dev("/dev/ram", Root_RAM0, NULL);
[2]if (rd_load_image("/ initrd.image") && ROOT_DEV != Root_RAM0) {
sys_unlink("/initrd.image");
handle_initrd();
return 1;



return 0;
}
code[1]: If initrd is loaded then create a ram0 device /dev/ram.
Code [2]: /initrd.image file saves image-initrd, the rd_load_image function executes the specific loading operation and releases the file content of image-nitrd to ram0. The meaning of judging ROOT_DEV!=Root_RAM0 is that if you configure root=/dev/ram0 in grub or lilo, the real root device is actually initrd, so it is not treated as initrd, but as realfs .
The handle_initrd() function is responsible for the specific processing of initrd. The code is as follows:
static void __init handle_initrd(void){
[1]real_root_dev = new_encode_dev(ROOT_DEV);
[2]create_dev("/dev/root.old", Root_RAM0, NULL );
mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY);
[3]sys_mkdir("/old", 0700);
root_fd = sys_open("/", 0, 0);
old_fd = sys_open(" /old", 0, 0);

[4]sys_chdir("/root");
sys_mount(".", "/", NULL, MS_MOVE, NULL);
sys_chroot(".");
mount_devfs_fs ();
[5]pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);
if (pid > 0) {
while (pid != sys_wait4(-1, &i, 0, NULL))
yield();
}
/* move initrd to rootfs' /old */
sys_fchdir(old_fd);
sys_mount("/", ".", NULL, MS_MOVE, NULL);
/* switch root and cwd back to / of rootfs */
[6]sys_fchdir(root_fd);
sys_chroot(".");
sys_close(old_fd);
sys_close(root_fd);
umount_devfs("/old/dev");
[7]if (new_decode_dev(real_root_dev) == Root_RAM0) {
sys_chdir("/old");
return;
}
[8]ROOT_DEV = new_decode_dev(real_root_dev);
mount_root();
[9]printk(KERN_NOTICE "Trying to move old root to /initrd ... ");
error = sys_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL);
if (!error)
printk("okay\n");
else {
int fd = sys_open("/dev/root.old", O_RDWR, 0);
printk("failed\n");
printk(KERN_NOTICE "Unmounting old root\n");
sys_umount("/old", MNT_DETACH);
printk(KERN_NOTICE "Trying to free ramdisk memory ... ");
if (fd < 0) {
error = fd;
} else {
error = sys_ioctl(fd, BLKFLSBUF, 0);
sys_close(fd);
}
printk(!error ? "okay\n" : "failed\n");
}
The main function of the handle_initrd function is to execute the linuxrc file of initrd and set the root directory of realfs to the current directory.
Code [1]: real_root_dev, is a global variable that stores the device number of realfs.
Code [2]: Call the mount_block_root function to mount the initrd file system to /root of the VFS.
Code[3]: Extract the file descriptor of the root of rootfs and save it to root_fd. Its function is to chroot to the initrd file system, after processing the initrd, it can also return to rootfs. The returned code refers to code [7].
Code[4]: chroot into the initrd's filesystem. The previous initrd has been mounted to the /root directory of rootfs.
Code [5]: Execute the linuxrc file of initrd and wait for its end.
Code [6]: After initrd is processed, re-chroot into rootfs.
Code [7]: If real_root_dev is reset to Root_RAM0 in linuxrc, initrd is the final realfs, change the current directory to initrd, and return directly without subsequent processing.
Code [8]: After linuxrc is executed, the realfs device has been determined, and the mount_root function is called to mount the realfs to the /root directory of root_fs, and set the current directory to /root.
Code [9]: The following code is mainly to do some finishing work and release the memory disk of initrd.
At this point, the code analysis is complete.
6. concluding remarks
Through the elaboration and comparison of cpio-initrd and imag-initrd in the first half of this article and the code analysis in the second half, I believe that readers have a more comprehensive understanding of the initrd technology of the Linux 2.6 kernel. At the end of this article, two most important conclusions are given:
1. Although Linux 2.6 supports both cpio-initrd and image-initrd, cpio-initrd has greater advantages, and we should give priority to using it. initrd in cpio format.
2. Compared with image-initrd, cpio-initrd assumes more initialization responsibilities. This change can also be seen as a manifestation of the user layer of the kernel code. We have also seen in other projects such as FUSE. Attempts to extend to userland implementations. Streamlining the kernel code and porting some functions to the user layer must be a trend in the development of the Linux kernel.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=327098228&siteId=291194637