嵌入式軟件開發之------淺析linux根文件系統掛載(九)

Linux代碼版本:linux4.4

導讀:前些天拿到供應商的一塊arm64開發板,需要對其新CPU進行測試評估。需要將公司自己的系統移植上去測試一些參數。在掛載公司的cpio包的時候,出現解壓失敗。之前對於根文件系統的掛載都是一些零零散散的學習,按部就班也沒出過啥問題,所以突然遇到問題不知道從哪下手,也剛好趁此機會系統的整理一次。最好的老師還是源代碼,只有對源代碼熟悉,才能知道該如何定位。由於公司用的是嵌入式設備,使用的是基於ram的cpio格式的文件系統,所以重點分析initrd和initramfs兩種跟文件系統的掛載過程,順便也帶上其他類型的根文件系統掛載。

一、基本概念小結

文件系統:文件系統是操作系統用於明確存儲設備(常見的是磁盤,也有基於NAND Flash的固態硬盤)或分區上的文件的方法和數據結構;即在存儲設備上組織文件的方法(引自百度百科)。這句話已經解釋的很清楚了,在存儲設備上組織文件的方法。你在電腦硬盤上看到的文件夾、文件、目錄層次都是文件系統的功勞,就是一種特定的組織文件的方法,然後提供一組標準的接口讓開發者可輕易讀寫,增刪和改變權限。假如沒有文件系統,對於一個有1000個block的存儲器,最開始開發者可以規劃,A文件放在前100個block,其他文件依次排序,如果存儲器換臺計算機換個開發者呢,鬼知道你是如何劃分的,也就是說毫無通用性可言,對於任意刪除一個文件後產生的磁盤碎片,更是無法利用。這個時候你可能會想,乾脆在存儲器的頭部留一定空間,裏面寫好有哪些文件,分別放在哪個位置。對於磁盤碎片,乾脆用像內存管理一樣,用鏈表串起來,這樣就可以再利用部分零碎空間。這個時候你又發現,有些文件不能隨便往裏寫,還需要加上一些標誌,標明哪些可寫哪些只讀。然後呢,發現爲了管理好文件,面臨的問題原來越多,最後你花了大量時間終於解決了所有問題,自己完成了一個文件系統。windows上我們最常見的兩種文件系統NTFS和FAT,Linux常用的Ext4。甚至對於不同的存儲介質,也有不同的文件系統與其對應,以更好的利用硬件特性達到最好的性能。

linux 根文件系統:從“根”這個字上就可以看出,這是linux系統最根本,最底層的文件系統,其他文件系統只能在跟文件系統之下掛載,在Linux下的目錄就是 "/",“cd /” 就是切換倒了根文件系統下,ls 就就可以看到所有的文件夾和文件都在 "/" 下面,沒有比“根”更靠前的了。就像一棵大樹,所有的樹幹、枝葉和花果都建立在樹根的基礎上。

initramfs/initrd:全稱是 initial RAM filesystem and RAM disk,百度這兩個概念的話,其實是兩種機制,多看幾篇博客的話甚至越看越迷糊。在寫這兩個概念的時候,自己內心也是比較糾結的,也許是理解有誤,暫時先將現在的理解記錄下來,將來發現現在理解的是錯誤再糾正。在linux代碼下make menuconfig-->general setup-->initial RAM filesystem and RAM disk,即是支持這兩種方式的。在我們PC上面裝linux系統,是比較靈活的,根文件系統可能在硬盤上,也可能在USB設備上,也可能在光驅設備上,在Linux掛載跟文件系統時,往往沒有這個設備驅動,這時就無法掛載根文件系統,啓動自然就會失敗。俗話說,沒有加個中間層解決不了的問題,initrd機制就是這加的這個中間層。先做一個臨時的根文件系統,啓動過程中先掛載這個臨時的跟文件系統,然後執行裏面的一些腳本,加載驅動等等工作(具體沒研究都做哪些工作),然後再掛載真正的根文件系統。在嵌入式設備上,通常都是作爲真正的根文件系統使用,畢竟嵌入式設備是定製的,不需要考慮太多通用的情況。

 initrd支持兩種格式,image-initrd和cpio-initrd。image-initrd是將一塊內存當作硬盤,然後在上面載入文件系統。cpio-initrd和下面的initramfs一塊說。

initramfs是一個基於ram的文件系統,只支持cpio格式。image-initrd 是將一塊內存當作硬盤,然後需要寫入文件系統,掛載到linux的時候還需要創建cache、filelist等等,浪費內存,並且大小固定,不靈活。initramfs則直接是基於RAM的文件系統,直接解壓就行,各種cache、filelist已經在cpio包裏的了。initramfs和cpio-initrd同樣都是cpio格式,initramfs是填上根文件路徑編譯到內核內部的,跟內核鏈接到一起的,鏈接後在__initramfs_start,__initramfs_size,代碼裏找不到這兩個變量,因爲是在vmlinux.lds鏈接腳本里定義的,通過調用scripts/gen_initramfs_list.sh 生成cpio包。這樣每次修改根文件系統裏的文件都需要重新編譯內核。

如果選擇支持initramfs並且沒有填寫initramfs source file(s) 選項,會執行gen_initramfs_list.sh中的default_initramfs生成一個默認的cpio包。

而cpio-initrd則是單獨製作cpio包,通過設備樹dts裏的chosen節點的bootargs中的linux,initrd-start和linux,initrd-end指定其地址範圍。也就是說需要boot loader拷貝到內存裏,然後通過參數傳遞給內核,這樣根文件系統的修改不用每次編譯內核了。

二、根文件系統掛載過程

1. 下面開始分析代碼,只挑出掛載根文件系統相關的部分。linux內核啓動的第一階段主要是解壓內核,初始化環境等等,真正的啓動是從start_kernel開始的,下面只列出掛載跟文件系統的部分

setup_machine_fdt(__fdt_pointer);
{
    /*轉換成虛擬地址,並做合法性檢查,如地址對齊等*/
	void *dt_virt = fixmap_remap_fdt(dt_phys);

	if (!dt_virt || !
        early_init_dt_scan(dt_virt)) {
        {
        	bool status;
            /*device tree合法性檢查,並計算crc32*/
        	status = early_init_dt_verify(params);
        	if (!status)
        		return false;
            /*掃描節點*/
        	early_init_dt_scan_nodes();
            {
            	/* Retrieve various information from the /chosen node */
                /*檢索chosen節點,原來要傳遞給內核的參數
                現在變成device tree了 ,可數據還是要傳啊,就放在了chosen節點*/
            	of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
                                {
                                	int l;
                                	const char *p;

                                	pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname);

                                	if (depth != 1 || !data ||
                                	    (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))
                                		return 0;

                                	early_init_dt_check_for_initrd(node);
                                    {
                                    	u64 start, end;
                                    	int len;
                                    	const __be32 *prop;

                                    	pr_debug("Looking for initrd properties... ");
                                        /*查找initrd的 start地址*/
                                    	prop = of_get_flat_dt_prop(node, "linux,initrd-start", &len);
                                    	if (!prop)
                                    		return;
                                    	start = of_read_number(prop, len/4);
                                        /*查找initrd的 end 地址*/
                                    	prop = of_get_flat_dt_prop(node, "linux,initrd-end", &len);
                                    	if (!prop)
                                    		return;
                                    	end = of_read_number(prop, len/4);
                                        /*從device tree中解析出initrd的start和end地址後,轉換爲虛擬地址分別
                                        賦值給相應的變量,後面會用到*/
                                    	initrd_start = (unsigned long)__va(start);
                                    	initrd_end = (unsigned long)__va(end);
                                    	initrd_below_start_ok = 1;

                                    	pr_debug("initrd_start=0x%llx  initrd_end=0x%llx\n",
                                    		 (unsigned long long)start, (unsigned long long)end);
                                    }

                                	/* Retrieve command line */
                                    /*查找bootargs並賦值給 boot_command_line ,後面還要從bootargs解析傳遞的init進程*/
                                	p = of_get_flat_dt_prop(node, "bootargs", &l);
                                	if (p != NULL && l > 0)
                                		strlcpy(data, p, min((int)l, COMMAND_LINE_SIZE));

                                	/*
                                	 * CONFIG_CMDLINE is meant to be a default in case nothing else
                                	 * managed to set the command line, unless CONFIG_CMDLINE_FORCE
                                	 * is set in which case we override whatever was found earlier.
                                	 */
                                    #ifdef CONFIG_CMDLINE
                                    #ifndef CONFIG_CMDLINE_FORCE
                                	if (!((char *)data)[0])
                                    #endif
                                		strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
                                    #endif /* CONFIG_CMDLINE */

                                	pr_debug("Command line is: %s\n", (char*)data);

                                	/* break now */
                                	return 1;
                                }

            	/* Initialize {size,address}-cells info */
            	of_scan_flat_dt(early_init_dt_scan_root, NULL);

            	/* Setup memory, calling early_init_dt_add_memory_arch */
            	of_scan_flat_dt(early_init_dt_scan_memory, NULL);
            }
        	return true;
        }
		pr_crit("\n"
			"Error: invalid device tree blob at physical address %pa (virtual address 0x%p)\n"
			"The dtb must be 8-byte aligned and must not exceed 2 MB in size\n"
			"\nPlease check your bootloader.",
			&dt_phys, dt_virt);

		while (true)
			cpu_relax();
	}

	dump_stack_set_arch_desc("%s (DT)", of_flat_dt_get_machine_name());
}

上面的分析setup_machine_fdt中得到兩個重點:

a. 從chosen節點中解析到 initrd_start 和 initrd_end ,後面解壓initrd的時候會用到。

b. 從chosen節點中解析到 bootargs ,後面還會解析 bootargs 參數。

下面就是關注解析bootargs中的 三個參數,看着是不是很熟悉,就是bootloader傳遞給kernel的參數,解析後放在一個全局變量裏。

static int __init init_setup(char *str)
{
	unsigned int i;

	execute_command = str;
	/*
	 * In case LILO is going to boot us with default command line,
	 * it prepends "auto" before the whole cmdline which makes
	 * the shell think it should execute a script with such name.
	 * So we ignore all arguments entered _before_ init=... [MJ]
	 */
	for (i = 1; i < MAX_INIT_ARGS; i++)
		argv_init[i] = NULL;
	return 1;
}
__setup("init=", init_setup);

static int __init rdinit_setup(char *str)
{
	unsigned int i;

	ramdisk_execute_command = str;
	/* See "auto" comment in init_setup */
	for (i = 1; i < MAX_INIT_ARGS; i++)
		argv_init[i] = NULL;
	return 1;
}
__setup("rdinit=", rdinit_setup);

static int __init root_dev_setup(char *line)
{
	strlcpy(saved_root_name, line, sizeof(saved_root_name));
	return 1;
}

__setup("root=", root_dev_setup);

linux源代碼裏面大量類似於__setup這類宏定義的寫法,將一類函數放到一個具體的代碼段裏面,然後再執行該代碼段裏函數。上面的函數通過  start_kernel-->parse_args-->unknown_bootoption-->obsolete_checksetup 調用到。

2. 上面的分析主要是對傳遞給kernel參數的解析,下面就要開始根文件系統掛載相關的分析,上面列出來的參數會被用到。

start_kernel-->vfs_caches_init-->mnt_init

void __init mnt_init(void)
{
	unsigned u;
	int err;

	.....
	/*初始化sysfs文件系統,最終會掛載到/sys/目錄下*/
	err = sysfs_init();
	if (err)
		printk(KERN_WARNING "%s: sysfs_init error: %d\n",
			__func__, err);
	/*在/sys/下創建fs目錄*/
	fs_kobj = kobject_create_and_add("fs", NULL);
	if (!fs_kobj)
		printk(KERN_WARNING "%s: kobj create error\n", __func__);
	init_rootfs();
    {
        /*註冊rootfs類型的文件系統,注意rootfs是一種文件系統的名字*/
        int err = register_filesystem(&rootfs_fs_type);
    
        if (err)
            return err;
        /*註冊ramfs/tmpfs文件系統類型,都是基於ram的文件系統,tmpfs是ramfs的一個變種,
        rootfs也是 ramfs/tmpfs的一個特殊例子*/
        if (IS_ENABLED(CONFIG_TMPFS) && !saved_root_name[0] &&
            (!root_fs_names || strstr(root_fs_names, "tmpfs"))) {
            err = shmem_init();
            is_tmpfs = true;
        } else {
            err = init_ramfs_fs();
        }
    
        if (err)
            unregister_filesystem(&rootfs_fs_type);
    
        return err;
    }
	init_mount_tree();
    {
    	struct vfsmount *mnt;
    	struct mnt_namespace *ns;
    	struct path root;
    	struct file_system_type *type;
        /*查找rootfs的類型定義結構體*/
    	type = get_fs_type("rootfs");
    	if (!type)
    		panic("Can't find rootfs type");
        /*將roots掛載到vfs中,這個第一個掛載到vfs的文件系統,
        所以也是根文件系統,其他文件系統只能掛載到rootfs下面*/
    	mnt = vfs_kern_mount(type, 0, "rootfs", NULL);
    	put_filesystem(type);
    	if (IS_ERR(mnt))
    		panic("Can't create rootfs");
        /*創建namespace即命名空間*/
    	ns = create_mnt_ns(mnt);
    	if (IS_ERR(ns))
    		panic("Can't allocate initial namespace");
        
    	init_task.nsproxy->mnt_ns = ns;
    	get_mnt_ns(ns);
        /*初始化跟文件系統的路徑*/
    	root.mnt = mnt;
    	root.dentry = mnt->mnt_root;
    	mnt->mnt_flags |= MNT_LOCKED;
        /*將當前進程工作路徑和根文件系統的路徑設置爲剛掛載的rootfs*/
    	set_fs_pwd(current->fs, &root);
    	set_fs_root(current->fs, &root);
	}
}

從上面的代碼可以看出,rootfs是作爲根文件系統掛載的,並將進程的當前路徑和根路徑 切換到rootfs,後面其他文件系統只能掛載到rootfs下面。

3. 通過上面的代碼,“/”已經有了,可是什麼裏面什麼東西都沒有啊,掛載“/”纔是第一步,下面繼續分析

start_kernel-->rest_init-->kernel_init-->kernel_init_freeable-->do_basic_setup-->do_initcalls-->populate_rootfs

static int __init populate_rootfs(void)
{
    /*解壓initramfs到rootfs,就是鏈接到內核的cpio,解壓失敗則嘗試initd方式*/
	char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size);
	if (err)
		panic("%s", err); /* Failed to decompress INTERNAL initramfs */
	if (initrd_start) {
#ifdef CONFIG_BLK_DEV_RAM
		int fd;
        /*從下面的printk可以看出,cpio-initrd和initramfs是一回事,只是傳遞方式不一樣,
        initramfs是鏈接到內核,而cpio-initrd是通過uboot傳遞*/
		printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n");
		err = unpack_to_rootfs((char *)initrd_start,
			initrd_end - initrd_start);
		if (!err) {
			free_initrd();
			goto done;
		} else {
			clean_rootfs();
			unpack_to_rootfs(__initramfs_start, __initramfs_size);
		}
        /*如果不是initramfs的格式,則按照image-initrd的方式處理,對image-initrd完全沒有了解,
        嵌入式設備上也沒見過,所以也不分析*/
		printk(KERN_INFO "rootfs image is not initramfs (%s)"
				"; looks like an initrd\n", err);
		fd = sys_open("/initrd.image",
			      O_WRONLY|O_CREAT, 0700);
		if (fd >= 0) {
			ssize_t written = xwrite(fd, (char *)initrd_start,
						initrd_end - initrd_start);

			if (written != initrd_end - initrd_start)
				pr_err("/initrd.image: incomplete write (%zd != %ld)\n",
				       written, initrd_end - initrd_start);

			sys_close(fd);
			free_initrd();
		}
	done:
#else
		printk(KERN_INFO "Unpacking initramfs...\n");
		err = unpack_to_rootfs((char *)initrd_start,
			initrd_end - initrd_start);
		if (err)
			printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err);
		free_initrd();
#endif
		/*
		 * Try loading default modules from initramfs.  This gives
		 * us a chance to load before device_initcalls.
		 */
		 /*加載默認的模塊,也就是bootargs中的"elevator="部分*/
		load_default_modules();
	}
	return 0;
}

從上上面的代碼可以看出,cpio-initrd和initramfs其實是一回事,只是傳遞給內核的方式不同,initramfs是通過鏈接到內核方式,而cpio-initrd是通過Uboot的bootargs傳遞給內核,解壓函數都是 unpack_to_rootfs 。可直接解壓到rootfs,猜測(未具體研究)cpio包本身就是基於rootfs類型,就像將基於ext4的文件目錄製作成imge格式燒寫到磁盤上。同時也發現,解壓成功後,initramfs_start和initramfs_end 之間的空間並沒有被釋放,而initrd_start和init_end之間的地址空間會被釋放(如果配置了retain_initrd 就不會被釋放了)。

4. 上面解壓完 initrd/initramfs 後就該執行剩餘的部分,調用init進程

start_kernel-->rest_init-->kernel_init-->kernel_init_freeable

static noinline void __init kernel_init_freeable(void)
{
	......

	do_basic_setup();
    
	/* Open the /dev/console on the rootfs, this should never fail */
    /*打開/dev/console ,還不能失敗,還記得上面 gen_initramfs_list.sh 裏面的default_initramfs,會創建該文件,
    如果沒有配置initramfs,真正的根文件系統還沒掛載怎麼辦,系統會調用 default_rootfs 函數,創建該文件*/
	if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
		pr_err("Warning: unable to open an initial console.\n");

	(void) sys_dup(0);
	(void) sys_dup(0);
	/*
	 * check if there is an early userspace init.  If yes, let it do all
	 * the work
	 */
    /*如果bootargs沒有指定rdinit=,默認啓動進程爲/init*/
	if (!ramdisk_execute_command)
		ramdisk_execute_command = "/init";

    /*判斷啓動文件是否存在,如果使用initrd/initramfs最爲最終根文件系統,肯定存在啓動進程的,如果不存在
    則會繼續執行,下面也進行分析*/
	if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
		ramdisk_execute_command = NULL;
		prepare_namespace();
        {
        	int is_floppy;
            /*如過配置的延時,則等待*/
        	if (root_delay) {
        		printk(KERN_INFO "Waiting %d sec before mounting root device...\n",
        		       root_delay);
        		ssleep(root_delay);
        	}

        	/*
        	 * wait for the known devices to complete their probing
        	 *
        	 * Note: this is a potential source of long boot delays.
        	 * For example, it is not atypical to wait 5 seconds here
        	 * for the touchpad of a laptop to initialize.
        	 */
        	 /*等待設備probe過程完成*/
        	wait_for_device_probe();

        	md_run_setup();
            /*判斷指定的root=前3個自己是否是mtd或ubi,如果是則掛載到根文件系統*/
        	if (saved_root_name[0]) {
        		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;
        		}
                /*比較前5個字節是否爲"/dev/",嵌入式設備也常將flash上的 文件系統掛載爲根文件系統,
                    通常形如"root=/dev/mtdblock2、"root=/dev/mmcblk0p2"*/
        		ROOT_DEV = name_to_dev_t(root_device_name);
        		if (strncmp(root_device_name, "/dev/", 5) == 0)
        			root_device_name += 5;
        	}
            /*image-initrd的 情況,不做分析*/
        	if (initrd_load())
        		goto out;

        	/* wait for any asynchronous scanning to complete */
            /*等待異步的scan完成*/
        	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();
        	}
            /*指定的根文件系統在軟盤的情況,不分析*/
        	is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;

        	if (is_floppy && rd_doload && rd_load_disk(0))
        		ROOT_DEV = Root_RAM0;

        	mount_root();
            {
            /*網絡文件系統nfs的情況*/
#ifdef CONFIG_ROOT_NFS
        	if (ROOT_DEV == Root_NFS) {
        		if (mount_nfs_root())
        			return;

        		printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");
        		ROOT_DEV = Root_FD0;
        	}
#endif
#ifdef CONFIG_BLK_DEV_FD
        	if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {
        		/* rd_doload is 2 for a dual initrd/ramload setup */
        		if (rd_doload==2) {
        			if (rd_load_disk(1)) {
        				ROOT_DEV = Root_RAM1;
        				root_device_name = NULL;
        			}
        		} else
        			change_floppy("root floppy");
        	}
#endif
    /*跟文件 系統在塊設備(flash、mmc等)的情況*/
#ifdef CONFIG_BLOCK
        	{
        	    /*創建"/dev/root"設備,上面有 ROOT_DEV 賦值*/
        		int err = create_dev("/dev/root", ROOT_DEV);
                /*將"/dev/root"掛載到/root目錄*/
        		if (err < 0)
        			pr_emerg("Failed to create /dev/root: %d\n", err);
        		mount_block_root("/dev/root", root_mountflags);
                {
                	struct page *page = alloc_page(GFP_KERNEL |
                					__GFP_NOTRACK_FALSE_POSITIVE);
                	char *fs_names = page_address(page);
                	char *p;
                    #ifdef CONFIG_BLOCK
                	char b[BDEVNAME_SIZE];
                    #else
                	const char *b = name;
                    #endif
					
                	get_fs_names(fs_names);
                retry:
                	for (p = fs_names; *p; p += strlen(p)+1) {
                		int err = do_mount_root(name, p, flags, root_mount_data);
                                  {
                                    	struct super_block *s;
                                        /*將/dev/root/掛載到/root目錄*/
                                    	int err = sys_mount(name, "/root", fs, flags, data);
                                    	if (err)
                                    		return err;
                                        /*將當前目錄切換到/root*/
                                    	sys_chdir("/root");
                                    	s = current->fs->pwd.dentry->d_sb;
                                    	ROOT_DEV = s->s_dev;
                                    	printk(KERN_INFO
                                    	       "VFS: Mounted root (%s filesystem)%s on device %u:%u.\n",
                                    	       s->s_type->name,
                                    	       s->s_flags & MS_RDONLY ?  " readonly" : "",
                                    	       MAJOR(ROOT_DEV), MINOR(ROOT_DEV));
                                    	return 0;
                                    }
                		switch (err) {
                			case 0:
                				goto out;
                			case -EACCES:
                			case -EINVAL:
                				continue;
                		}
                	        /*
                		......
                }
        	}
#endif
        }
        /*掛載devtmpfs到/dev/目錄*/    
        out:
        	devtmpfs_mount("dev");
            /*將當前目錄(上面設置的/root)掛載到 / 目錄 */
        	sys_mount(".", "/", NULL, MS_MOVE, NULL);
            /*將當前進程的根目錄切換成當前目錄*/
        	sys_chroot(".");
        }
        
	}

	/*
	 * Ok, we have completed the initial bootup, and
	 * we're essentially up and running. Get rid of the
	 * initmem segments and start the user-mode stuff..
	 *
	 * rootfs is available now, try loading the public keys
	 * and default modules
	 */

	integrity_load_keys();
	load_default_modules();
}

總結上面的流程,在 initrd/intramfs 解壓後,準備啓動init進程,如果init進程不存在,就認爲真正的根文件系統還未掛載,繼續嘗試掛載根文件系統,對於嵌入式常用的"root=/dev/mtdblock2、"root=/dev/mmcblk0p2",先創建/dev/root,然後掛載到/root目錄,再然後掛載/目錄下。這下,不管用的是initrd/initramfs還是nfs或者flash設備上文件 系統,都已經完成了掛載,下一步就是執行init進程了

static int __ref kernel_init(void *unused)
{
	int ret;

	kernel_init_freeable();
	/* need to finish all async __init code before freeing the memory */
	async_synchronize_full();
	free_initmem();
	mark_rodata_ro();
	system_state = SYSTEM_RUNNING;
	numa_default_policy();

	flush_delayed_fput();
	if (ramdisk_execute_command) {
		ret = run_init_process(ramdisk_execute_command);
		if (!ret)
			return 0;
		pr_err("Failed to execute %s (error %d)\n",
		       ramdisk_execute_command, ret);
	}

	/*
	 * We try each of these until one succeeds.
	 *
	 * The Bourne shell can be used instead of init if we are
	 * trying to recover a really broken machine.
	 */
	if (execute_command) {
		ret = run_init_process(execute_command);
		if (!ret)
			return 0;
		panic("Requested init %s failed (error %d).",
		      execute_command, ret);
	}
	if (!try_to_run_init_process("/sbin/init") ||
	    !try_to_run_init_process("/etc/init") ||
	    !try_to_run_init_process("/bin/init") ||
	    !try_to_run_init_process("/bin/sh"))
		return 0;

	panic("No working init found.  Try passing init= option to kernel. "
	      "See Linux Documentation/init.txt for guidance.");
}

上面的代碼很明顯,先執行rdinit=xx,如果不存在,則按照/sbin/init、/etc/init、/bin/init和/bin/sh的過程執行,都不存在那肯定是啓動不了。

三、總結

linux對根文件系統的掛載,還是比較靈活的,考慮了各種應用場景,對於要求性能的設備,可以選擇initrd/initramfs,畢竟ram的讀寫性能不是其他存儲介質能比的;調試過程中也可以使用nfs來提高效率;也可以將initrd/initramfs作爲臨時的根文件系統做一些必要的動作後再掛載最終的根文件系統。

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章