Linux Kernel ramdisk (initrd) Mechanism

Source: Internet
Author: User

Abstract: For Linux users, ramdisk is no stranger, but why does it need it? This article introduces the role of ramdisk in the kernel startup process and its internal mechanism.
Title
Initrd and initramfs processing in the kernel
Temporary root directory rootfs mounting
Initrd Decompression
Processing of vintage initrd
Processing of initrd in cpio format
Initrd instance analysis
In earlier Linux systems, only a floppy disk or hard disk was used as the linux root file system. Therefore, it is easy to integrate the drivers of these devices into the kernel. However, the root file system may be stored on a variety of storage devices, including SCSI, SATA, and USB flash disks. Therefore, it is inconvenient to compile all these device drivers into the kernel. In the introduction to the automatic loading mechanism of Linux kernel modules, we can see that udevd can be used to automatically load kernel modules. Therefore, we hope that the device driver of the root file system can also be automatically loaded. However, there is a conflict here. udevd is an executable file. It is impossible to run udevd before the root file system is mounted. However, if udevd is not started, then, the root Driver Based on the system device cannot be automatically loaded, and the corresponding device node cannot be created in the/dev directory. To solve this problem, initrd (Boot Loader initialized RAM disk) emerged ). Initrd is a compressed small root directory that contains the necessary driver modules, executable files, and startup scripts in the startup phase. Including the udevd mentioned above, when the system starts, booload will read the initrd file into the memory, and then tell the kernel the starting address of initrd. During the running process, the kernel will decompress initrd, Mount initrd to the root directory, and then execute the/initrc script in the root directory. You can run the udevd in initrd in this script, let it automatically load the device driver and create necessary device nodes in the/dev directory. After udevd automatically loads the disk driver, you can mount the real root directory and switch to the root directory.

You can use the following method to create an initrd file.

# Dd If =/dev/Zero of = initrd. img bs = 4 k count = 1024

# Mkfs. ext2-F initrd. img

# Mount-o loop initrd. img/mnt

# Cp-r miniroot/*/mnt

# Umount/mnt

# Gzip-9 initrd. img

Through the above command, we made a 4 m initrd, where miniroot is a root directory. Finally, we need to get a compressed file named initrd.img.gz.
The initrd kernel can be used to smoothly load the device driver in the startup phase. However, initrd has the following Disadvantages:

The initrd size is fixed. For example, the initrd size before compression is 4 m (4 K * 1024). Assume that your root directory (miniroot/in the above example /) the total size is only 1 m, and it still occupies 4 m space. If you specify the size of 1 m in the DD phase, you must repeat the preceding steps when you find that it is not enough.

Initrd is a virtual block device. In the above example, you use fdisk to partition the virtual block device. In the kernel, the reading and writing of Block devices also goes through the buffer management module. That is to say, when the kernel reads the file content in initrd, the buffer management layer considers that the underlying Block devices are slow, therefore, the pre-read and cache functions are enabled. In this way, the initrd itself is in the memory, and the block device buffer management layer will save part of the content. In order to avoid the above disadvantages, initramfs has emerged. Its function is similar to that of initrd. You can use the following method to create an initramfs:

# Find miniroot/| cpio-c-o> initrd. img

# Gzip initrd. IMG gets the initrd. the IMG size is variable. It depends on the total size of miniroot/in your small root directory. Because cpio is preferred to pack the root directory, this initramfs is also called cpio initrd. the starting address of the kernel is passed to the kernel. Can we combine these two files into one? The answer is yes. in Linux 2.6's kernel, you can link initrd.img.gz to a special data segment in the kernel file (ELF format). The segment name is. init. ramfs. The global variables _ initramfs_start and _ initramfs_end point to the start and end addresses of the Data Segment respectively. When the kernel is started, the data in the. init. ramfs segment is extracted and used as a temporary root file system. Although this process is complex, you only need to configure the following options in make menuconfig:
General setup --->

[*] Initial Ram filesystem and RAM disk (initramfs/initrd) Support (.. /miniroot/) initramfs source file (s) where .. /miniroot/is our small root directory. In this way, you only need a kernel image file. The kernel must handle the following situations during startup:
If the. init. ramfs data segment size is not 0 (initramfs_end-initramfs_start! = 0). This indicates that initrd is integrated into the kernel data segment. And is the initrd of cpio.

Initrd is loaded into the memory by bootloader. At this time, bootloader will pass the start address and end address to the kernel. The Global initrd_start and initrd_end in the kernel point to the initrd start address and end address respectively. Now the kernel also needs to determine whether the initrd is in the new cpio format or the old initrd.

Initrd and initramfs processing in the kernel
Temporary root directory rootfs mounting
First, during the kernel startup process, the rootfs file system will be initialized. Both rootfs and tmpfs are in-memory file systems, whose type is ramfs. Then the rootf will be mounted to the root directory. The Code is as follows:

[Start_kernel ()-> vfs_caches_init ()-> mnt_init ()]

Void _ init mnt_init (void)

{......

Init_rootfs ();

Init_mount_tree ();

} Init_rootfs () registers the rootfs file system. The Code is as follows:

Static struct file_system_type rootfs_fs_type = {

. Name = "rootfs ",

. Get_sb = rootfs_get_sb,

. Kill_sb = kill_litter_super ,};

Int _ init init_rootfs (void ){

Err = register_filesystem (& rootfs_fs_type );

...... Return err ;}

Init_mount_tree mounts rootfs to the/directory. The Code is as follows:

Static void _ init init_mount_tree (void)

{

Struct vfsmount * MNT;

Struct mnt_namespace * ns;

Mnt = do_kern_mount ("rootfs", 0, "rootfs", null );......

Set_fs_pwd (current-> FS, NS-> root, NS-> root-> mnt_root );

Set_fs_root (current-> FS, NS-> root, NS-> root-> mnt_root );}

Do_kern_mount () calls the rootfs_get_sb () function of the previously registered rootfs File System Object,

[Rootfs_get_sb ()-> ramfs_fill_super ()-> d_alloc_root ()] struct dentry * d_alloc_root (struct inode * root_inode)

{

Struct dentry * res = NULL;

If (root_inode)

{

Static const struct qstr name = {. Name = "/",. Len = 1 };

Res = d_alloc (null, & name );

If (RES ){

Res-> d_sb = root_inode-> I _sb;

Res-> d_parent = res;

D_instantiate (Res, root_inode );

}

} Return res;} from the code above, we can see that the dentry object of this rootfs is named "/", that is, the root directory.
Initrd Decompression
At the end of start_kernel (), call rest_init () and rest_init () to create a new kernel process, and execute the kernel_init () function, kernel_init () populate_rootfs () is called to detect and decompress the initrd file. This function needs to handle the above several initrd cases.

[Kernel_init ()-> populate_rootfs ()] Static int _ init populate_rootfs (void) {/* If _ initramfs_end-_ initramfs_start is not 0, it indicates that this is the cpio intrd integrated with the Kernel File. */Char * err = unpack_to_rootfs (_ initramfs_start, _ initramfs_end-_ initramfs_start, 0); If (ERR) panic (ERR ); # ifdef config_blk_dev_initrd/* If initrd_start is not 0, it indicates that this is the initrd loaded by bootloader. * You need to further judge whether it is an initrd in cpio format or an initrd in an old block device. */If (initrd_start) {# ifdef config_blk_dev_ram int FD;/* First, judge whether it is in the cpio format of initrd, that is, initramfs. */Printk (kern_info "Checking if image is initramfs...");/* The last parameter of unpack_to_rootfs () is 1, which indicates check only and will not be decompressed. */Err = unpack_to_rootfs (char *) initrd_start, initrd_end-initrd_start, 1); If (! Err) {/* if it is an initrd in cpio format, extract it to the root file system mounted above, and then release the memory occupied by initrd. */Printk ("It Is/N"); unpack_to_rootfs (char *) initrd_start, initrd_end-initrd_start, 0); free_initrd (); Return 0 ;} /* if it is executed here, it indicates that this is the initrd of the old block device format. * First, create an initrd in the root directory mounted above. image File, * write the content from initrd_start to initrd_end to/initrd. in the image, * finally releases the memory space occupied by initrd (its copy has been saved to/initrd. image in progress .). */Printk ("It isn' t (% s); looks like an initrd/N", err); FD = sys_open ("/initrd. image ", o_wronly | o_creat, 0700); If (FD> = 0) {sys_write (FD, (char *) initrd_start, initrd_end-initrd_start); sys_close (FD ); free_initrd ();}...... return 0;} rootfs_initcall (populate_rootfs); after processing by the populate_rootfs () function, if it is initrd in cpio format, then unpack_to_rootfs () the function has decompressed the directory to the root directory of the previous mount. However, if the initrd and unpack_to_rootfs () Functions of the old block device are decompressed, a virtual block device image file/initrd is obtained. image. In this case, further processing is required. Next, kernel_init () will handle this situation.

Static int _ init kernel_init (void * unused ){...... do_basic_setup ();/* during kernel startup, you can specify the final stage of startup by using the startup parameter rdinit = xxx. Which executable file in initrd needs to be run, * If this parameter is specified, ramdisk_execute_command points to the xxx string. initrd in the new cpio format runs/init by default. * Therefore, if ramdisk_execute_command is null, set it to/init. */If (! Ramdisk_execute_command) ramdisk_execute_command = "/init";/* now, try to access ramdisk_execute_command. The default value is/init. If the access fails, the file does not exist in the root directory. * Therefore, prepare_namespace () is called to further check whether it is the initrd of the old block device * (in this case, it is still a block device image file/initrd. failed to access the/init file .). */If (sys_access (const char _ User *) ramdisk_execute_command, 0 )! = 0) {ramdisk_execute_command = NULL; prepare_namespace ();} init_post (); Return 0;} processing of old-fashioned initrd
Prepare_namespace () is used to process vintage initrd.

[Kernel_init ()-> prepare_namespace ()-> initrd_load ()] int _ init initrd_load (void) {If (mount_initrd) {create_dev ("/dev/Ram ", root_ram0); If (rd_load_image ("/initrd. image ") & root_dev! = Root_ram0) {sys_unlink ("/initrd. image "); handle_initrd (); return 1 ;}} sys_unlink ("/initrd. image "); Return 0;} initrd_load () perform the following steps:

Call create_dev () to create the device file node/dev/Ram, which is actually a ramfs file system.

Call rd_load_image () to load/initrd. Image to/dev/Ram.

Call handle_initrd () to mount the block Device File/dev/Ram to/root.

The handle_initrd () code is as follows:

[Kernel_init ()-> prepare_namespace ()-> initrd_load ()-> handle_initrd ()] Static void _ init handle_initrd (void ){...... real_root_dev = new_encode_dev (root_dev);/* Create/dev/root. old device file. */Create_dev ("/dev/root. Old", root_ram0);/* Mount/dev/root. Old to the/root directory. * // * Mount initrd on rootfs '/root */mount_block_root ("/dev/root. Old", root_mountflags &~ Ms_rdonly); sys_mkdir ("/old", 0700); root_fd = sys_open ("/", 0, 0); old_fd = sys_open ("/old", 0, 0 ); /* Move initrd over/and chdir/chroot in initrd root */sys_chdir ("/root"); sys_mount (". ","/", null, ms_move, null);/* chroot to the/root directory. All right, now the/root directory is the current root directory. */Sys_chroot (". ");/** In case that a resume from disk is carried out by linuxrc or one of * its children, we need to tell the freezer not to wait for us. */current-> flags | = pf_freezer_skip;/* Create a thread and run/linuxrc. This is the default file executed by the old initrd. */PID = kernel_thread (do_linuxrc, "/linuxrc", sigchld); ......} processing of initrd in cpio format
No additional processing is required for initrd in the new cpio format, so kernel_init () continues to run:

[Kernel_init ()-> init_post ()] Static int noinline init_post (void ){...... /* Open the console. Note that if the root directory in cpio format does not contain the/dev/console file, * the device file will be created in the unpack_to_rootfs () function. */If (sys_open (const char _ User *) "/dev/console", o_rdwr, 0) <0) printk (kern_warning "Warning: unable to open an initial console. /n ");/* Currently, all standard input, standard output, and standard errors are/dev/console. */(Void) sys_dup (0); (void) sys_dup (0);/* run the command specified by ramdisk_execute_command. The default value is/init. */If (ramdisk_execute_command) {run_init_process (ramdisk_execute_command); printk (kern_warning "failed to execute % s/n", ramdisk_execute_command );}...... run_init_process ("/sbin/init"); run_init_process ("/etc/init"); run_init_process ("/bin/init"); run_init_process ("/bin/sh "); panic ("No init found. try passing in It = option to kernel. ");} After run_init_process () is called to execute/init, this function will not be returned. In general releases of Linux, the/init script in initrd will start udevd, load the necessary device drivers, mount the real root file system, and finally execute the initrd on the real root file system. In this way, the startup process will be smoothly handed over.

Initrd of Block devices is not only inconvenient to use, but also complicated to process in the kernel. Therefore, initrd of cpio will definitely replace it. We recommend using initrd in cpio format.

Initrd instance analysis
If you are using Ubuntu, you can run the following command to check its content in initrd.

# Mkdir/tmp/initrd # cp/boot/initrd. IMG-xxx/tmp/initrd/initrd.img.gz # cd/tmp/initrd # gunzip initrd.img.gz # Cat initrd. IMG | cpio-ivmd now, let's see what the init script of this root directory has done.

# Cat init #! /Bin/sh # Ubuntu users must be familiar with this message. Echo "loading, please wait... "......exe C run-init $ {rootmnt} $ {init}" $ @ "<$ {rootmnt}/dev/console >$ {rootmnt}/dev/console 2> & 1 the init script finally runs run-init in initrd to switch to the real root file system.
You can modify this script, add relevant printing information, and then use the method described at the beginning of this article to re-create a cpio initrd, and then use this initrd to start the kernel, let's take a look at the test results.

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.