Linux2.6 核心的 Initrd 機制解析(4)(轉貼好文)

tonykorn97發表於2006-11-26

initrd_load函式負責載入image-initrd,程式碼如下:


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;
		}
	}
	sys_unlink("/initrd.image");
	return 0;
}

程式碼[1]:如果載入initrd則建立一個ram0裝置 /dev/ram。

程式碼[2]:/initrd.image檔案儲存的就是image-initrd,rd_load_image函式執行具體的載入操作,將image-nitrd的檔案內容釋放到ram0裡。判斷ROOT_DEV!=Root_RAM0的含義是,如果你在grub或者lilo裡配置了 root=/dev/ram0 ,則實際上真正的根裝置就是initrd了,所以就不把它作為initrd處理 ,而是作為realfs處理。

handle_initrd()函式負責對initrd進行具體的處理,程式碼如下:



	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);

	/* move initrd over / and chdir/chroot in initrd root */
[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("okayn");
	else {
		int fd = sys_open("/dev/root.old", O_RDWR, 0);
		printk("failedn");
		printk(KERN_NOTICE "Unmounting old rootn");
		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 ? "okayn" : "failedn");
	}
	

handle_initrd函式的主要功能是執行initrd的linuxrc檔案,並且將realfs的根目錄設定為當前目錄。

程式碼[1]:real_root_dev,是一個全域性變數儲存的是realfs的裝置號。

程式碼[2]:呼叫mount_block_root函式將initrd檔案系統掛載到了VFS的/root下。

程式碼[3]:提取rootfs的根的檔案描述符並將其儲存到root_fd.它的作用就是為了在chroot到initrd的檔案系統,處理完initrd之後要,還能夠返回rootfs.返回的程式碼參考程式碼[7].

程式碼[4]:chroot進入initrd的檔案系統。前面initrd已掛載到了rootfs的/root目錄。

程式碼[5]:執行initrd的linuxrc檔案,等待其結束。

程式碼[6]:initrd處理完之後,重新chroot進入rootfs.

程式碼[7]:如果real_root_dev在 linuxrc中重新設成Root_RAM0,則initrd就是最終的realfs了,改變當前目錄到initrd中,不作後續處理直接返回。

程式碼[8]:在linuxrc執行完後,realfs裝置已經確定,呼叫mount_root函式將realfs掛載到root_fs的 /root目錄下,並將當前目錄設定為/root.

程式碼[9]:後面的程式碼主要是做一些收尾的工作,將initrd的記憶體盤釋放。

到此程式碼分析完畢。

6.結束語

透過本文前半部分對cpio-initrd和imag-initrd的闡述與對比以及後半部分的程式碼分析,我相信讀者對Linux 2.6核心的initrd技術有了一個較為全面的瞭解。在本文的最後,給出兩點最重要的結論:

1. 儘管Linux2.6既支援cpio-initrd,也支援image-initrd,但是cpio-initrd有著更大的優勢,在使用中我們應該優先考慮使用cpio格式的initrd.

2. cpio-initrd相對於image-initrd承擔了更多的初始化責任,這種變化也可以看作是核心程式碼的使用者層化的一種體現,我們在其它的諸如FUSE等專案中也看到了將核心功能擴充套件到使用者層實現的嘗試。精簡核心程式碼,將部分功能移植到使用者層必然是linux核心發展的一個趨勢。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/312079/viewspace-245536/,如需轉載,請註明出處,否則將追究法律責任。

相關文章