Introduction to ramfs, tmpfs, rootfs, ramdisk【转】

转自:https://blog.birost.com/a?ID=00350-fda5a078-6698-4a95-b5e2-8e12d5934d3a

bootleader--- "kernel----" initrd (xz, cpio, a kind of ramfs, mainly drivers and preparation of some environments and equipment for loading rootfs ) -------- "rootfs (It is an img image file, which is also a kind of ramfs. It is an environment prepared for installing the system. Afterwards, installing the system is to install and process in the rootfs and finally get a real file system. The installation program is here)

Inscription

I analyzed this part of the content a long time ago, but at that time it was not in-depth enough. Let me know how such a thing exists and how to use it. I don't know the ins and outs. Some time ago, I encountered an initrd problem at work. There is no other way but to study it again. Fortunately, I have a little eyebrow and I just sorted it out. There are many analyses on various versions of ramdisk and initrd circulating on the Internet. My article is based on my understanding of them. I am very grateful to the selfless dedication of those seniors. Otherwise, it would be hard for us juniors to learn things. What needs to be specially stated here is that if your thoughts are quoted in the article but no references are given, please forgive my negligence. Juniors just need to stand on the shoulders of giants like you, technology will develop and society will progress.
In addition, due to my limited level, it is inevitable that there are misunderstandings and incompleteness in the article. Please correct me, thank you!
Welcome to reprint, but please keep the original information.
Contact: Li Zhiguo/lizgo lizhiguo0532@163.com 2010/10/16
Catalog
Part 1: ramfs, tmpfs, rootfs, ramdisk
1. What is ramfs
2. What is tmpfs
3. What is rootfs
4. What is ramdisk
2. Part: initrd, initramfs
1. Background of initrd appearance
2. Types and production of initrd
Part 3: Kernel initialization initrd code analysis
1. Initrd parameters passed by bootloader to kernel
2. Dynamic memory allocator slab introduction
3. Rootfs initialization
4. the processing of initrd in the kernel initialization phase
V. The initrd processing function prepare_namespace() of the old block device analyzes the
attached text
. The
first part: ramfs, tmpfs, rootfs, ramdisk
1. What is ramfs?
1. The linux caching mechanism
VFS (virtual file system) layer shields the characteristics of various real file systems and provides a unified interface to the upper layer of linux.
Usually, Linux reads and writes all files as a cache. When the system uses these files again, they can be directly read from the memory to improve the system's I/O performance. When files in the cache are modified or data is written to the cache, the system will write back the data in these caches to the corresponding file system devices (such as disks, flash, etc.) at an appropriate time, then this Afterwards, the status of these caches is marked as clean (available), which is equivalent to telling the system that there are backups on the data file system devices in these caches, and you can use them for other purposes. But the data in it will be kept until VMS (Virtual Memory System) reclaims these caches and reallocates them.
Similar to the page caching mechanism, the directory caching mechanism also greatly speeds up access to the directory.
2. ramfs
ramfs is a very simple file system, which directly uses the existing cache mechanism of the linux kernel (so its implementation code is very small, and for this reason, ramfs features cannot be shielded by the kernel configuration parameters, it is the kernel Natural attribute), using the physical memory of the system to create a memory-based file system whose size can be dynamically changed.
ramfs works at the virtual file system layer (VFS) layer and cannot be formatted. You can create more than one. By default, ramfs can use up to half of the memory. If necessary, you can also use -o maxsize = 10000 (unit is KB) To change the maximum amount of memory used.
Ramfs does not have a corresponding file system device. When files are written to ramfs, the page cache and directory cache are normally allocated like other file systems, but it is impossible to write back the files in the cache to storage like other file systems with storage devices. equipment. This means that these high-speed pages or directory caches allocated for files and directories in ramfs cannot be marked as clean (available), so the system will never release the memory occupied by ramfs. Just because you can write data in ramfs until it is full, only root (or trusted user) users can perform ramfs write operations.
In order to solve the various problems caused by the shortcomings of ramfs (no write-back device), the tmpfs file system was derived.
2. What is tmpfs?
Tmpfs is a derivative of ramfs, which adds capacity restrictions and allows
data to be written to the swap space (swap) on the basis of ramfs . Due to the addition of these two features, ordinary users can also use tmpfs.
Tmpfs is a virtual memory file system, which is different from the traditional ramdisk implemented in the form of block devices and ramfs for physical memory. tmpfs can use both physical memory and swap partitions. In the Linux kernel, virtual memory resources are composed of physical memory and swap partitions, and these resources are managed by the virtual memory subsystem in the kernel. Tmpfs deals with the virtual memory subsystem. It requests pages from the virtual memory subsystem to store files. Like other page requests in Linux, it is not known whether the page allocated to itself is in the memory or in the swap partition. That is to say, tmpfs uses virtual memory, while ramfs uses physical memory. In addition, tmpfs, like ramfs, cannot be formatted, and the size is not fixed. You can use -o size =32m or (1g) to modify it.
In addition, tmpfs can write pages that are not currently in use into the swap space. At the same time, due to the virtual memory it uses, once tmpfs is uninstalled, the data in it will be lost.
If you need to use tmpfs, you have to choose when compiling the kernel:
Virtual memory filesystem support
ramfs will only be created in physical memory, and tmpfs may be created in physical memory, or in the swap partition. For applications that want to use memory's high-speed IO to improve performance, it is best to use ramfs. For applications that just want to store temporary caches, it is best to use tmpfs to increase memory usage.
3. What is rootfs?
Rootfs is an instance of a specific ramfs (or tmpfs, if tmpfs is enabled), it always exists in the linux2.6 system. Rootfs cannot be uninstalled (instead of adding special code to maintain an empty linked list, it is better to always add rootfs nodes, so it is convenient for kernel maintenance. Rootfs is an empty instance of ramfs, which takes up very little space). Most other file systems are mounted on rootfs, and then it is ignored. It is the kernel boot initialization root file system.
4. What is ramdisk?
Ramdisk is no longer used after linux2.6, and it is still used in 2.4. A ramdisk is a technology that uses an area of memory
as a physical disk. It can also be said that a ramdisk is a block device created in a memory area to store a file system. For users, the ramdisk can be treated the same as a normal hard disk partition. The ramdisk is not suitable as a medium for storing files for a long time, and the content of the ramdisk will disappear after power failure.
In order to be able to use ramdisk, your kernel must support ramdisk, that is, when compiling the kernel, select the option RAM disk support, and CONFIG_BLK_DEV_RAM will be defined in the configuration file. At the same time, in order for the kernel to be able to load the ramdisk during the kernel loading stage and run its contents, the initial RAM disk (initrd) support option must be selected, and CONFIG_BLK_DEV_INITRD will be defined in the configuration file.
The size of the ramdisk is fixed, and the size of the file system installed on it is also fixed. When the ramdisk is in use, there is a copy of data between this fake block device and the cache (page cache and directory cache), and it also needs a file system driver to format and interpret these data. Therefore, the use of ramdisk not only wastes memory, but also increases the burden on the cpu. At the same time, it does not pollute the cache. Moreover, all its files and directories must be accessed through page and directory caches. These work ramfs must be executed, then ramdisk It can be completely unnecessary. This is one of the reasons to discard ramdisk.
Another reason to discard it is the introduction of loopback devices, and loopback devices provide a more flexible and convenient way (from files instead of large blocks of memory) to create a composite block device.
The second part: initrd, initramfs
1. The background of initrd
in the early Linux system, generally only hard disk or floppy disk is used as the storage device of the Linux root file system, so it is easy to integrate the drivers of these devices into In the kernel. But in the current embedded system, the root file system may be saved on various storage devices, including scsi, sata, u-disk, etc. Therefore, it is obviously not very convenient to compile all the driver codes of these devices into the kernel.
In the kernel module auto-loading mechanism udev, we see that udevd can be used to auto-load the kernel module, so we hope that if the driver of the storage device storing the root file system can also be auto-loaded, it will be great. But there is a contradiction here. udevd is an executable file. It is impossible to execute udevd before the root file system is mounted. However, if udevd is not started, the driver for storing the root file system device cannot be automatically loaded. It is also impossible to create the corresponding device node in the/dev directory.
In order to solve this contradiction, ramdisk-based initrd (bootloader initialized RAM disk) appeared. Initrd is a compressed small root directory. This directory contains the necessary driver modules, executable files and startup scripts during the startup phase, as well as udevd (the demon that implements the udev mechanism) mentioned above. When the system starts, the bootloader will read the initrd file into the memory, and then pass the initial address and size of the initrd file in the memory to the kernel. The kernel will decompress the initrd file during startup and initialization, then mount the decompressed initrd as the root directory, and then execute the/init script in the root directory (initrd in cpio format is/init, and initrd in image format is also called The initrd of the old block device or the initrd> of the traditional file image format is/initrc), you can run udevd in the initrd file system in this script and let it automatically load the realfs (real file system) storage device driver And create the necessary device nodes in the/dev directory. After udevd automatically loads the disk driver, you can mount the real root directory and switch to this root directory.
This is just a brief description, let's analyze it slowly later.
2. the type and production of initrd
Initrd generally currently has two formats: image format and cpio format. The image format is also called the file system image file (the initrd file of the old block device), which is mainly used in the Linux 2.4 kernel; the initramfs technology has been introduced in the Linux 2.5 kernel. Initramfs has actually overcome the shortcomings of imgae-initrd, and is essentially The initrd in cpio format is just compiled into an image file with the kernel and placed in the .init.ramfs section; the kernel to linux2.6 supports two initrd formats, image-initrd and cpio-initrd, at this time The cpio-initrd file is no longer compiled into the kernel and becomes a separate file, which is generated using the cpio tool. Detailed later.
Before introducing the production of initrd, first understand the functions of the following commands:
dd: Copy a file with a specified size block, and perform the specified conversion while copying.
1. if=file name: input the file name, the default is standard input. That is, the source file is specified. <if=input file>
Note the/dev/zero device, this device is a device that can continuously provide 0, used to initialize
2. of=file name: output file name, the default is standard output. Namely specify the destination file. <of=output file>
3.bs=bytes: Set the block size of the read-in/output as bytes.
4. Count=blocks: Only copy blocks, the block size is equal to the number of bytes specified by ibs.
There are other more detailed parameter usage reference: dd command-detailed explanation
http://blog.csdn.net/liumang_D/archive/2009/02/17/3899462.aspx
mkfs.ext2: equivalent to mke2fs, creating an ext2 file system.
mke2fs [-cFMqrSvV][-b <block size>][-f <discontinuous section size>][-i <byte>][-N ][-l <file>][-L <tag> ][-m <percentage value>][-R=<number of blocks>][device name][number of blocks]
1. -F enforces mke2fs regardless of the specified device.
2. -m<percentage value> specifies the percentage of blocks reserved for the administrator, the default is 5%.
3. The rest of the parameters can be defaulted, please refer to: mkfs.ext2 command-detailed explanation (network search)
mount: hang on the command.
mount [-t vfstype] [-o options] device dir
1.-t vfstype specifies the type of file system, Usually it is not necessary to specify. mount will automatically select the correct type.
2.-o options is mainly used to describe how to attach devices or files. Commonly used parameters are:
 loop: used to mount a file as a hard disk partition to the system
 ro: mount the device in read-only mode
 rw: mount the device in read-write mode
 iocharset: specify the character set used to access the file system
3. Detailed parameters Reference: detailed explanation of mount command (web search)
1), image-initrd production
We can use the following method to create an old block device-based image-initrd file:
# dd if=/dev/zero of=initrd.img bs= 4k count=1024
# mkfs.ext2 -F --m 0 initrd.img
# sudo mkdir/mnt/ramdisk
# mount -o loop initrd.img/mnt/ramdisk
# cp -r/opt/filesystem/mnt/ramdisk
# umount/mnt
# gzip -9 initrd.img
Through the above command, we made a 4M initrd, where/opt/filesystem is a root directory that we made with busybox. Finally, we get a compressed file named initrd.img.gz with the largest compression ratio. For a more detailed process, refer to: ramdisk production
http://blog.csdn.net/epicyong333/archive/2008/12/24/3590619.aspx
http://blog.csdn.net/vcvbve/archive/2010/02/27/5329384.aspx
uses image-initrd to enable the kernel to successfully load various storage media drives and mount the realfs file system during the startup phase. However, image-initrd has the following disadvantages: 1. The size of image-initrd
is Fixed, for example, the size of the initrd before compression above is 4M (4k*1024). Assuming that the total size of your root directory (/opt/filesystem in the above example) is only 1M, it still takes up 4M of space. If you specify a size of 1M in the dd stage, and later find that it is not enough, you must repeat the above steps.
2. image-initrd is a virtual block device, but you can use fdisk to partition this virtual block device. In the kernel, the read and write to the block device also go through the buffer management module, that is, when the kernel reads the file content in the initrd, the buffer management layer will think that the lower block device is slower, so it will be enabled Pre-reading and cache functions. In this way, the initrd itself is in the memory, and the block device buffer management layer also saves part of the content.
2), initramfs
, in order to avoid the above shortcomings, initramfs appeared in linux2.5, its function is similar to initrd, but compiled into a file with the kernel (the initramfs is a data file in cpio format after gzip compression), the cpio The formatted file is linked into the special data segment .init.ramfs in the kernel, where the global variables __initramfs_start and __initramfs_end point to the start address and end address of this data segment respectively. When the kernel starts, it will decompress the data in the .init.ramfs section, and then use it as a temporary root file system.
To make such a kernel, we only need to configure the following options in make menuconfig: 
General setup ---> 
[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support 
(/opt/filesystem) Initramfs source file (s) 
Among them,/opt/filesystem is our small root directory. Here you can make a ready-made gzip compressed cpio file, or a directory, or even a configuration file in txt format, such as the following example:
dir/dev 755 0 0
nod/dev/console 644 0 0 c 5 1
nod/dev/loop0 644 0 0 b 7 0
dir/bin 755 1000 1000
slink/bin/sh busybox 777 0 0
file/bin/busybox initramfs/busybox 755 0 0
dir/proc 755 0 0
dir/sys 755 0 0
dir/mnt 755 0 0
file/init initramfs/init.sh 755 0 0
If you specify a directory instead of a configuration file like this, the kernel will create a configuration file from the specified directory during compilation (usr/Makefile calls scripts/gen_initramfs_list.sh), as input to the usr/gen_init_cpio.c file, and finally generate a usr/initramfs_data.cpio.gz file, which is included in the .init.ramfs section through usr/initramfs_data.S, and finally generates zImage .
3), cpio-initrd
The initrd format mentioned here is the same as the initramfs format compiled into the kernel. Both are cpio, also
called external initramfs, which exist independently and need to be loaded into the memory by the bootloader at a specific address. Then pass the address and size to the kernel, and perform corresponding processing in the kernel's initialization phase.
This initrd can be implemented using the cpio command, as follows:
# sudo find/opt/filesystem/-depth | cpio -c -o> initrd.img 
# gzip -9 initrd.img
The initrd obtained in this way is in cpio format, and the size of this file is variable, which means that
it changes according to the size of your filesystem, and will not be fixed in size like the initrd in the previous image format. Of course I think the size of the previous initramfs is also variable.
Keywords:
ramfs, tmpfs, rootfs, ramdisk
image-initrd, initramfs, cpio-initrd
Part 3: Kernel initialization initrd code analysis
arm platform, linux2.6.29
1. The parameter about initrd passed by the bootloader to the kernel
root=
This parameter tells The kernel uses that device as the root file system, usually in the form:
root=/dev/ramx
root=/dev/mtdblockx rw rootfstype = jffs2
root=31:0x rootfstype = jffs2
root=/dev/nfs nfsroot=serverip:nfs_dir
第One is necessary when using ramdisk and image-initrd. Initramfs and cpio-initdrd use rootfs that always exist in the system, so this parameter can be omitted.
The third and four are basically common, but the fourth one uses the device number and partition number to specify, for example, root=/dev/mtdblock4 is equivalent to root=31:04, which means to use the fourth partition on the nand device As the root file system, use rootfstype to specify the file system type.
The last one is to mount on the nfs file system. In addition to specifying the root type, you also need to specify a directory on the server as the root directory for mounting.
initrd=
should follow this format: initrd = addr, size, where addr represents the location of initrd in memory, and size represents the size of initrd. For example, initrd = 0xa060000,0x2c6b.
Another way to pass this parameter is to use the tag parameter to pass it. uboot uses the function setup_initrd_tag() to generate the corresponding tag parameter.
There are two points to note:
1. The kernel initialization first parses the initrd address and size parameters in the tag, and then goes to execute cmdline to parse the initrd= parameter. If both of these places are specified, then it ultimately depends on the cmdline initrd=The specified parameters, because the results of these two methods are passed by the global variables phys_initrd_start and phys_initrd_size in the kernel initialization phase (this is the physical address), and then converted into virtual addresses and stored in the global variables initrd_start and initrd_size For subsequent use.
2. The size passed by the bootloader should be the actual size of the initrd, it cannot be transmitted more or less, otherwise, an error will occur when the gunzip is decompressed.
init=
init specifies the first script that runs in the system after the kernel is started, generally init=/linuxrc (used by image-initrd) and init=/init (used by initramfs and cpio-initrd).
The ramdisk uses rdinit=/xxx to specify.
Summary: (The following conclusions are in the initrd address and size are passed through the tag parameter)
Through the following code analysis, the following summary can be made:
1. Use initramfs and cpio-initrd
does not need root=xxx parameter, but init=/init is necessary
2. Use image-initrd (remember to turn on the kernel support for ramdisk and initrd)
init=/initrc, the following is to pass different root =xxx case
a. root=/dev/ramx, using image-initrd as the real file system
b. root=/dev/mtdblockx rw rootfstype = jffs2, so image-initrd is only used as an intermediate file system, In the end, you need to rely on the kernel thread 1 to mount the real file system.
(B The root = just an example, the real system and it does not matter on what equipment, is the need in the main image-initrd file device driver in the system really present in the file system like the loading)
2. the dynamic The memory allocator slab introduces
that every dynamic memory management of Linux uses a heap-based allocation strategy.
The original allocation strategy is like this. In this method, large blocks of memory (called heap) are used to provide memory for user-defined purposes. When users need a piece of memory, they request to allocate a certain size of memory to themselves. The heap manager looks at the available memory (using a specific algorithm) and returns a block of memory. Some algorithms used in the search process are first-fit (the first memory block found in the heap that satisfies the request) and best-fit (use the most suitable memory block in the heap that satisfies the request). When the user finishes using the memory, the memory is returned to the heap. The fundamental problem of this heap-based allocation strategy is fragmentation. When memory blocks are allocated, they will be returned at different times in different orders. This will leave some holes in the heap, and it will take some time to effectively manage free memory. This algorithm usually has a higher memory efficiency (allocation of required memory), but it takes more time to manage the heap.
About before the 2.0 kernel, another management mechanism called buddy memory allocation (buddy memory allocation system) appeared, which is a faster memory allocation technology that divides memory into power-of-two partitions and uses The best-fit method to allocate memory requests. When the user releases the memory, it will check the buddy block to see if its adjacent memory block has also been released. If so, the memory blocks will be merged to minimize memory fragmentation. This algorithm is more time efficient, but due to the best-fit method, it will waste memory.
Later, after the 2.2 kernel, a brand-new dynamic memory allocator slab allocator was introduced. The concept of slab allocator was first implemented in Sun Microsystem's SunOs 5.4 operating system, which revolved around object caching. The slab layer divides different objects into so-called cache groups, where each cache stores different types of objects. There is a cache for each object type. For example, one cache is used to store the process descriptor (task_struct), and the other cache stores the inode object (struct inond). It is worth mentioning that the kmalloc interface is built on the slab layer and uses a set of general-purpose caches.
The following figure shows the high-level organizational structure of the slab structure. At the top level is cache_chain, which is a list of links cached by the slab. This is very useful for the best-fit algorithm, which can be used to find the cache that best fits the required allocation size (traverse the list). Each element of cache_chain is a reference to a kmem_cache structure (called a cache). It defines a pool of objects of a given size to be managed.
The picture is not posted. The "Linux Kernel Design and Implementation" book has
these caches and is divided into slabs. The slab is composed of one or more physically continuous pages, and each physical page is divided into a specific object. In general, a slab is only composed of one page.
Interface of slab distributor:
Create a new cache, which is usually executed when the kernel is initialized or when the kernel module is first loaded.
kmem_cache_t *kmem_cache_creat( const char *name, size_t size, 
size_t align,unsigned long flags;
  void (*ctor)(void*, struct kmem_cache *, unsigned long),
  void (*dtor)(void*, struct kmem_cache *, unsigned long));
Destroy a cache
int kmem_cache_destroy(kmem_cache_t *cachep);
After the cache is created, you can obtain objects from it through the following function
void *kmem_cache_alloc(kmem_cache_t *cachep,int flags);
If all slabs in the cache are If there is no free object in, then the slab layer must obtain a new page through kmem_getpages(), and the flags value is passed to __get_free_pages().
The following function is to release the object and return it to the original slab
void kmem_cache_free(kmem_cache_t *cachep,void *objp); For
more details, see: "Linux Kernel Design and Implementation" by Robrt Love
It is also worth mentioning that after linux2.6.22, the slub allocator was introduced, which retains the basic idea of ​​slab: each buffer is composed of multiple small slabs, and each slab contains a fixed number of objects. The slub allocator simplifies the management data structure of kmem_cache, slab, etc., abandons many queue concepts in the slab allocator, and is optimized for multi-processor and NUMA systems, thereby improving performance and scalability and reducing memory waste. In order to ensure that other modules of the kernel can be seamlessly migrated to the slub allocator, slub also retains all the interface API functions of the original slab allocator.
http://qgjie456.blog.163.com/blog/static/3545136720090622056716/
3. rootfs initialization
This part of the code is mainly implemented by vfs_caches_init_early() and vfs_caches_init(num_physpages) in the start_kernel() function in init/main.c , Where the num_physpages global variable is initialized in the mem_init() function, representing the total number of pages of physical memory. For
detailed code analysis, see: rootfs_initialize.c
vfs_caches_init_early():
struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node * next, **pprev;
};
Summary:
inode_hashtable is an array, each array element is a struct hlist_head structure, the structure actually only has a pointer to the struct hlist_node object first, all pointers are 4 bytes on the arm system, so this function actually Only a section of memory space is allocated for the array inode_hashtable, which is actually an array of pointers, and each element is a pointer to a struct hlist_node object. Then finally set these pointers to NULL. As for how to use it later, it has not been involved.
Similarly, the same is true for Dentry cache hash table (dentry_hashtable), except that an array of pointers is created.
http://qgjie456.blog.163.com/blog/static/3545136720081126102615685/
4. the kernel initialization phase of the initrd processing
start_kernel () function will finally call rest_init (), in this function will create two kernel threads kernel_init and kthreadd (only one kernel thread init is created in the 2.6.14 kernel, but the work is basically the same). After the kernel thread is created, the original system 0 process enters the idle state.
There are two main processing functions for initrd: populate_rootfs() and prepare_namespace(). The processing conditions are different for different formats.
One thing to note here is that the populate_rootfs() function is executed in different positions in 2.6.14 and 2.6.29. In 2.6.14 before the do_basic_setup() function is executed, but in the 2.6.29 version, it is transplanted to the do_basic_setup() function. In the higher version, populate_rootfs() is moved to do_initcalls() for execution, that is, the function is placed in the .initcallrootfs.init section when compiling.
CONFIG_BLK_DEV_RAM-This macro is defined, indicating that the kernel supports ramdisk, and RAM disk support needs to be selected in make menuconfig.
CONFIG_BLK_DEV_INITRD-This macro is defined, indicating that the kernel has the ability to load RAMDISK and run its contents during the kernel loading phase. You need to select the initial RAM disk (initrd) support item in make menuconfig.
The following are the different names of various initrds:
image-initrd: the initrd of the old block device, also called the initrd of the traditional file image format
initramfs: the initrd of the cpio format compiled with the kernel
cpio-initrd: the independent file of the cpio format initrd
populate_rootfs function analysis
static int __init populate_rootfs(void)
{
char *err = unpack_to_rootfs(__initramfs_start,
__initramfs_end-__ initramfs_start , 0);
/***
The initramfs compiled into the kernel is located in the segment .init.ramfs, and the global variables __initramfs_start and __initramfs_end are used to point to the memory start address and end address (virtual address) of this data segment respectively.
So here is if __initramfs_end-__initramfs_start is equal to 0, then The unpack_to_rootfs function will not do anything and exit directly. The system thinks that the initrd is not an initramfs file, so it needs to be processed later.
***/
if (err)
panic(err);
//Determine whether the initrd is loaded, bootlaoder will load the initrd to the memory address initrd_start,//I have already mentioned how to pass these parameters to the kernel
if (initrd_start) {
#ifdef CONFIG_BLK_DEV_RAM
//CONFIG_BLK_DEV_RAM and CONFIG_BLK_DEV_RAM are often defined at the same time
int fd;
printk(KERN_INFO "checking if image is initramfs...");
err = unpack_to_rootfs((char *)initrd_start,
initrd_end-initrd_start, 1);
/***
judge Is cpio-initrd loaded? In fact, unpack_to_rootfs has two functions. One is to release the cpio package, and the other is to determine whether it is a cpio package. This is distinguished by the last parameter, 0: release 1: view.
***/
if (!err) {
//If it is cpio-initrd, use the function unpack_to_rootfs to release its content
//to rootfs
printk(" it is\n");
unpack_to_rootfs((char *)initrd_start,
initrd_end-initrd_start, 0) ;
free_initrd();//Release the memory space where cpio-initrd was originally stored
return 0;
}
/***
If the execution reaches this point, it means that this is the initrd of the old block device format. Then first create an initrd.image file on the previously mounted root directory rootfs, and then write the contents of initrd_start to initrd_end to/initrd.image, and finally release the memory space occupied by initrd (its copy has been saved to/initrd .image is in.).
***/
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();


//If the macro that supports image-initrd is not defined, then directly use the following code to process cpio-initrd
//In fact, it is the same as processing cpio-initrd above
printk(KERN_INFO "Unpacking initramfs...");
err = unpack_to_rootfs((char *)initrd_start,
initrd_end-initrd_start, 0);
if (err)
panic(err);
printk(" done\n");
free_initrd();
#endif
}
return 0;
}
rootfs_initcall(populate_rootfs);
above After the populate_rootfs() of populate_rootfs() is executed, if it is initramfs and cpio-initrd, they have been released to the previously initialized rootfs, then the init file will definitely appear in the root directory. If it is image-initrd, only one initrd.image file will appear in the root directory of rootfs.
So the processing that comes down is even more different.
static int __init kernel_init(void * unused)
{
…
if (!ramdisk_execute_command)
ramdisk_execute_command = "/init";
//When rdinit=xxx exists in cmdline, ramdisk_execute_command is not NULL
if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
prepare_namespace();
}
/***
Try to access here ramdisk_execute_command, the default is/init, if the access fails, the file does not exist on the root directory. So call prepare_namespace() 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.image, so access to the/init file fails.).
If initramfs or cpio-initrd will direct execution init_post () function, then execute/init file later
***/
init_post ();
return 0;
}
five, the initrd old block prepare_namespace device handler () Analysis
cmdline The parameters passed need to include: 
root=/dev/mtdblockx rw or {root=/dev/ramx} init=/initrc
Remember that the device specified by root= is the final real file system. Specify it when processing image-initrd, then the system will think that your initrd is my real file system, and will skip many steps without executing it.
void __init prepare_namespace(void)
{
int is_floppy;
if (root_delay) {
printk(KERN_INFO "Waiting %dsec before mounting root device...\n",
root_delay);
ssleep(root_delay);
}
wait_for_device_probe();
md_run_setup();
if (saved_root_name[0]) {
//If cmdline has passed root=xxx, then its device number will be saved here
root_device_name = saved_root_name;
if (! strncmp (root_device_name, "mtd", 3) ||
!strncmp( root_device_name, "ubi", 3)) {
mount_block_root(root_device_name, root_mountflags);
goto out;
}
ROOT_DEV = name_to_dev_t(root_device_name);
/***
Save the device number of the device that stores the real file system in ROOT_DEV. If cmdline passes/dev/ramx, ROOT_DEV = Root_RAM0, if not, then what is what, and later will do different things based on this
***/
if (strncmp(root_device_name, "/dev/", 5) == 0)
root_device_name += 5;
}
/*** The
saved_root_name global array saves the value of root= passed in from the command line, such as You pass root=/dev/ramx or root=/dev/mtdblockx rw, then the above if condition is true, then the device number ROOT_DEV of the device is obtained through the name_to_dev_t(root_device_name) function. Otherwise, the section is skipped and not executed.
***/
if (initrd_load())/*** See the note below for details***/
/***
Load the initrd of the old block device, there are also two cases, one is to use the initrd as the real file system The other is to return 1 as a transitional file system, and return 0 at this time.
***/
goto out;
/***
If you want to use the initrd of the old block device as the real file system, you need to pass in the following parameters in cmdline: root=/dev/ramx init=/initrc, and compiled in the kernel When you need to open support ramdisk and initrd
***/
/* wait for any asynchronous scanning to complete */
if ((ROOT_DEV == 0) && root_wait) {
printk(KERN_INFO "Waiting for root device %s...\n",
saved_root_name);
while (driver_probe_done() != 0 ||
(ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)
msleep(100);
async_synchronize_full();
}/* If root= is passed normally, the if will not be established */
is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
/***
If cmdline passes the correct root =/dev/ramx, directly create a Root_RAM0 type/dev/root node in mount_root() (image-initrd has been imported into Root_RAM0ramdisk in initrd_load()), so mount_root() can be used directly.
***/
if (is_floppy && rd_doload && rd_load_disk(0))
ROOT_DEV = Root_RAM0;
mount_root();//Mount the real file system on the ROOT_DEV device to the rootfs/root directory
out:
//Note: the above process All have mounted the real file system to/root (here/is still rootfs),
//And the current directory is/root
sys_mount(".", "/", NULL, MS_MOVE, NULL);
//mount the current directory as the root directory, overwrite the original rootfs
sys_chroot(".");
//switch current The directory is the location of the root directory referenced by the program execution. At this point, the real file system
//is mounted
}
++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++
int __init initrd_load(void)
{
if (mount_initrd) {
create_dev("/dev/ram", Root_RAM0) ;
//Create a/dev/ram device node of Root_RAM0 type. In fact, the Root_RAM0 device is
//a ramdisk
if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) {
//rd_load_image() initrd the file .image is loaded into/dev/ram0 to go
sys_unlink("/initrd.image");//delete the file initrd.image
handle_initrd();//handle_initrd() function is responsible for specific processing of initrd
return 1;/* image-initrd as an intermediate transition file system*/
}
The/***
/initrd.image file saves the image-initrd. The rd_load_image function performs a specific loading operation and
releases the contents of the image-nitrd file to the ramdisk with the device type Root_RAM0 (node ​​name is ram). Determine the meaning of ROOT_DEV!=Root_RAM0 (ROOT_DEV cannot be =0, otherwise there will be an error when mounting). As mentioned earlier, if you configure root=/dev/ramx in the bootloader, the real root device is actually this Initrd, so it is not treated as an initrd, but as a real file system.
***/
}
sys_unlink("/initrd.image");//Delete file initrd.image
return 0;/* image-initrd as the real file system*/
}
++++++++++++ +++++++++++++++++++++++++++++++++++++++++++
static void __init handle_initrd(void)
{
int error;
int pid;
real_root_dev = new_encode_dev(ROOT_DEV);
//The real_root_dev global variable saves the device number of the device storing realfs
create_dev("/dev/root.old", Root_RAM0);
//Create a Root_RAM0 type The/dev/root.old device node is actually a ramdisk
//Access to root.old is the same as the ram node created in the upper function, corresponding to the same device, with the same content
/* mount initrd on rootfs's/root */
mount_block_root("/dev/root.old", root_mountflags & ~ MS_RDONLY);
//Mount the initrd file system in/dev/root.old to the/root directory of rootfs
sys_mkdir("/old", 0700);/* Create an old directory under the root directory of rootfs*/
root_fd = sys_open("/", 0, 0);
//Save the descriptor of the original root directory in this way
old_fd = sys_open("/old", 0, 0);
//Save the description of/old in this way Fu
/* Move over the initrd/and the chdir/* the root CHROOT in the initrd/
sys_chdir ( "/the root");
the sys_mount ( ".", "/", NULL, MS_MOVE, NULL);
sys_chroot ( ".");
//Enter the/root directory, mount the current directory as the root directory, and then switch the current directory to
the root directory location referenced by the program execution
*
* 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;
pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);
//start a process to execute/linuxrc, and Wait for it to finish. You can see the meaning of executing sys_chroot(".") before
if (pid> 0)
while (pid != sys_wait4(-1, NULL, 0, NULL))
yield();
current->flags &= ~PF_FREEZER_SKIP;
/* move initrd to rootfs'/old */
sys_fchdir(old_fd);
sys_mount("/", ".", NULL, MS_MOVE, NULL);
//Enter the directory described by old_fd, and then mount the current root directory to the original root In the old directory under the directory
/* switch root and cwd back to/of rootfs */
sys_fchdir(root_fd);
sys_chroot(".");
//Enter the real root directory, and then switch the current directory as the root referenced by program execution Directory location, which is the root
//directory restoration,

sys_close(root_fd);
if (new_decode_dev(real_root_dev) == Root_RAM0) {
sys_chdir("/old");
return;
}
//Take the device number of the device where the real file system is located
ROOT_DEV = new_decode_dev(real_root_dev);
mount_root();
//Mount the real file system in the/root directory
//Note that mount_root() actually calls the function mount_block_root() to implement mount, and then
//chdir(/root), otherwise there may be a possibility to return to the upper function later Can’t understand
printk(KERN_NOTICE "Trying to move old root to/initrd ... ");
error = sys_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL);
//transplant/old/root/initrd
if (!error)
printk("okay\n");
else {
int fd = sys_open("/dev/root.old", O_RDWR, 0);
if (error == -ENOENT)
printk ("/initrd does not exist.Ignored.\n");
else
printk("failed\n");
printk(KERN_NOTICE "Unmounting old root\n");
sys_umount("/old", MNT_DETACH);
//Unmount the/old directory if the move is not successful
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");
}
}
+++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++
At this point, the real file system is mounted through image-initrd.
Attachment: 
rootfs_initialize.c - code analysis and annotation of the
rootfs initialization part. The rootfs initialization call level.txt
Reference website:
http://wiki.debian.org/ramfs
http://linux.net527.cn/Linuxwendang/xitongguanliyuan/1926.html
http://blog.csdn.net/knock/archive/2010/02/02/5280255.aspx
http://www.examda.com/linux/fudao/20090506/101701272.html
http://my.donews. com/tangfl/2007/12/17/linux_tmpfs_ramfs/
kouu's home page recovery
http://www.ibm.com/developerworks/cn

 

posted @ 2022-03-24 21:43  Sky&Zhang  阅读(239)  评论(0编辑  收藏  举报