Linux中国 Linux中国门户站!
设为主页 设为主页
收藏本站 收藏本站
 
当前位置 :首页 ->Linux技术 ->内核研究 ->正文

Linux 2.6 内核的 Initrd 机制解析

来源: 作者: 时间:2007-04-11 点击: [收藏] [投稿]

代码[1]:populate_rootfs函数负责加载initramfs和cpio-initrd,对于populate_rootfs函数的细节后面会讲到。

代码[2]:如果rootfs的根目录下中包含/init进程,则赋予execute_command,在init函数的末尾会被执行。否则执行prepare_namespace函数,initrd是在该函数中被加载的。

代码[3]:把控制台设置为标准输入,后续的两个sys_dup(0),则复制标准输入为标准输出和标准错误输出。

代码[4]:如果rootfs中存在init进程,就把后续的处理工作交给该init进程。其实这段代码的含义是如果加载了cpio-initrd则交给cpio-initrd中的/init处理,否则会执行realfs中的init。读者可能会问:如果加载了cpio-initrd, 那么realfs中的init进程不是没有机会运行了吗?确实,如果加载了cpio-initrd,那么内核就不负责执行realfs的init进程了,而是把这个执行任务交给了cpio-initrd的init进程。解开fedora core4的initrd文件,会发现根目录的下的init文件是一个脚本,在该脚本的最后一行有这样一段代码:



………..switchroot --movedev /sysroot

就是switchroot语句负责加载realfs,以及执行realfs的init进程。

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

代码[1]:加载initramfs, initramfs位于地址__initramfs_start处,是内核在编译过程中生成的,initramfs的是作为内核的一部分而存在的,不是 boot loader加载的。前面提到了现在initramfs没有任何实质内容。

代码[2]:判断是否加载了initrd。无论哪种格式的initrd,都会被boot loader加载到地址initrd_start处。

代码[3]:判断加载的是不是cpio-initrd。实际上 unpack_to_rootfs有两个功能一个是释放cpio包,另一个就是判断是不是cpio包, 这是通过最后一个参数来区分的, 0:释放 1:查看。

代码[4]:如果是cpio-initrd则把其内容释放出来到rootfs中。

代码[5]:如果不是cpio-initrd,则认为是一个image-initrd,把其内容保存到/initrd.image中。在后面的image-initrd的处理代码中会读取/initrd.image。

对image-initrd的处理在prepare_namespace函数里,包含了对image-initrd进行处理的代码,相关代码如下:



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

代码[1]:执行initrd_load函数,把initrd载入,如果载入成功的话initrd_load函数会把realfs的根设置为当前目录。

代码[2]:把当前目录即realfs的根mount为Linux VFS的根。initrd_load函数执行完后,把真正的文件系统的根设置为当前目录。

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("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");	}	


 如果您对本文有任何疑问或者建议,请到讨论区发表您的意见: >> 论坛入口 <<



上一篇:Linux之父:内核发展已经完全超越了我   下一篇:Linux内核源代码的阅读及相关工具介绍

文章评论】 【收藏本文】 【推荐好友】 【打印本文】 【我要投稿】 【论坛讨论
更多相关文章
Power by linux-cn.com 粤ICP备05006655号