momo zone

调核人的blog

initrd 启动流程解析

1. init : 其实内核会优先查找initrd 根目录下的init ,如果没有再依次尝试sbin/init,/etc/init,/bin/init,/bin/sh/。这段代码在内核源码树 init/main.c 中:

static int __init kernel_init(void * unused)
{
	......

	if (!ramdisk_execute_command)
		ramdisk_execute_command = "/init";

	if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
		ramdisk_execute_command = NULL;
		prepare_namespace();
	}

	/*
	 * 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..
	 */

	init_post();
        return 0;
}
static noinline int init_post(void)
{
       ......
        if (ramdisk_execute_command) {
                run_init_process(ramdisk_execute_command);
                printk(KERN_WARNING "Failed to execute %s\n",
                                ramdisk_execute_command);
        }

        /*
         * 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) {
 run_init_process(execute_command);
 printk(KERN_WARNING "Failed to execute %s. Attempting "
 "defaults...\n", 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 init= option to kernel. "
              "See Linux Documentation/init.txt for guidance.");
}

init 脚本首先判断slow_boot 变量是否为空,如果是则执行fast boot 。 所谓的fast boot即init 脚本不执行boot目录下各脚本所明示的依赖关系和执行条件,以及模块挂载请求 ,而是直接 执行无特别检查机制的run-all.sh 脚本,由它再 调用boot目录下的各脚本。过程中每个boot脚本开头的验证机制将不再起作用。下面 仅介绍fast boot的流程 。

2. run-all.sh : 很简单的脚本,依次调用boot目录下的初始化脚本

3.boot/*  : 一个个介绍篇幅会很大,所以这里挑选几个重要的讲大致流程 ,以及特别注意的地方。

01-devfunctions.sh: 里面全是函数,用来处理磁盘设备文件,major,minor 等等。 还有一个就是check_for_device() 他会在83-mount.sh 被调用。检查是否真正的 根目录所在的磁盘设备文件已经就绪。

02-start.sh: 完成shell环境变量初始化,部分虚拟目录的挂载比如/dev /proc /sys 等,创建基本的设备文件。格式化传过来的命令行参数。里面有一句比较特殊 exec < /dev/console > /dev/console 2>&1 他的作用是把/dev/console 的标准输出和标准错误输出 重新 定位给他自己 。意思就是所有输出都要显示在真实终端上。这里有个很有用的参数,linuxrc=trace 将启用追踪,再配合shell=1 ,和blogd 方便进行调试。

03-dm.sh: 很简单,就两行代码,直接贴出来明白 mkdir /dev/mapper mknod /dev/mapper/control c 10 63

03-rtc.sh: 好像这个无关紧要,等待实时时钟(即cmos时钟)在/sys/class/rtc/rtc0 出现。即使最后没出现也没有关系 ?

03-storage.sh:很重要: 用来设定真正的根目录在那个设备上 很简单: 就几行

. /config/mount.sh

if [ "$root" ]; then
    rootdev="$root"
fi

if [ "$nfsroot" ]; then
    rootdev=$nfsroot
fi

[ "$resume" ] && resumedev="$resume"

04-udev.sh:轮到udev出场了,其实也就两句 :

/sbin/udevd --daemon
/sbin/udevadm trigger

另外还有一个wait_for_events() 后面很多被call 到的情况。这个函数也就一句:

/sbin/udevadm settle --timeout=$udev_timeout

含义是udev 处理事件队列的时间 最长为udev_timeout 秒(阻塞)。

05-clock.sh:调整系统时钟? bin/warpclock 不知道何用

05-kms.sh: 设定是否启用kms

06-blogd.sh: 启用日志服务。日志文件记录在/var/log/boot.msg,同时链接到了/var/log/boot.msg。不过前者在upstart之后就看不到了。

11-block.sh: 设置块设备模块参数

21-dmraid.sh: 启用fake raid

22-kpartx.sh: 根据分区表生成对应分区的设备文件。不过这个脚本是空的,kpartx 交由udev的rule ,当有新磁盘被发现时会被自动调用。/etc/udev/rules.d/70-kpartx.rules:

# Create persistent links for dmraid tables
ENV{DM_UUID}=="DMRAID-*", \
        SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}"
ENV{DM_STATE}=="ACTIVE", ENV{DM_UUID}=="DMRAID-*", \
        RUN+="/sbin/kpartx -u -p _part /dev/$kernel"

81-resume.userspace.sh: 假如之前有s2disc那么这个脚本将调用用户空间程序来将把 resume device 中的镜像导入内存。

82-resume.kernelspace.sh: 好像没有什么实际作用,只是把81-resume.userspace.sh 中的resume device的major ,minor 放入/sys/power/resume 而已。

83-mount.sh: 非常重要的脚本,真正的根目录在这里被挂载。首先检查指定的根目录所在的设备是否真的在/dev下存在。然后利用udevadm info来获得文件系统类型。再后用fsck 检查文件系统。最后用mount 把它挂载到initrd 根目录下的root目录。文件系统的驱动模块什么时候加载的?

84-remount.sh: 依次在initrd  中的/root目录下检测并执行sbin/init , etc/init, bin/init ,bin/sh 。然后格式化/root/etc/fstab ,根据其中的参数重新挂载根目录到/root

91-createfb.sh: 创建帧缓冲

91-killblogd.sh  91-killudev.sh : 杀掉blogd和udevd

91-shell.sh : 如果内核参数有加 shell=1 那么run-all.sh 执行到这里就截止了,打开一个bash 取代后面流程的执行。

92-killblogd2.sh:杀掉blogd进程。

93-boot.sh: 移动并卸载一些虚拟目录比如dev,sys 等。 然后开始执行bin/run-init。

4.bin/run-init :在boot/93-boot.sh 中被执行(内核执行的第一个基于C库的程序):

exec /bin/run-init -c ./dev/console /root $init ${kernel_cmdline[@]}
echo could not exec run-init!
die 0

它的主要作用是remount 真正的根目录,并切换到真正根目录,打开/dev/console ,执行upstart程序,即真正根目录下的/sbin/init。也可以添加内核参数init=/bin/sh 来改变$init 变量。 这里一旦exec 执行成功,后续代码将不会再执行,因为/sbin/init 永不返回 。

5. /sbin/init : upstart 的起点。挂载真正的根目录后首先要执行的二进制程序,pid=1。

Advertisements

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s

%d 博主赞过: