前面我们已经对parse_args函数进行了分析,得到三个参数:
saved_root_name="/dev/mtdblock3";
console_cmdline[0].name= "ttySAC";
console_cmdline[0].options= 0;
console_cmdline[0].idx= 0;
execute_command = "/linuxrc"
接下来我们要对rest_init()函数进行分析:
static void noinline __init_refok rest_init(void)
__releases(kernel_lock)
{
int pid;
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);//创建进程,调用kernel_init函数
numa_default_policy();
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
kthreadd_task = find_task_by_pid(pid);
unlock_kernel();
/*
* The boot idle thread must execute schedule()
* at least one to get things moving:
*/
preempt_enable_no_resched();
schedule();
preempt_disable();
/* Call into cpu_idle with preempt disabled */
cpu_idle();
}
显然,rest_init()中调用了kernel_init函数:
static int __init kernel_init(void * unused)
{
lock_kernel();
/*
* init can run on any cpu.
*/
set_cpus_allowed(current, CPU_MASK_ALL);
/*
* Tell the world that we're going to be the grim
* reaper of innocent orphaned children.
*
* We don't want people to have to make incorrect
* assumptions about where in the task array this
* can be found.
*/
init_pid_ns.child_reaper = current;
__set_special_pids(1, 1);
cad_pid = task_pid(current);
smp_prepare_cpus(max_cpus);
do_pre_smp_initcalls();
smp_init();
sched_init_smp();
cpuset_init_smp();
do_basic_setup();
/*
* check if there is an early userspace init. If yes, let it do all
* the work
*/
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(); //调用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(); //调用init_post();
return 0;
}
可以看到,kernel_init函数中调用了prepare_namespace()函数和init_post()函数,我们先看prepare_namespace函数:
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 the known devices to complete their probing */
while (driver_probe_done() != 0)
msleep(100);
md_run_setup();
if (saved_root_name[0]) {
root_device_name = saved_root_name;
if (!strncmp(root_device_name, "mtd", 3)) {
mount_block_root(root_device_name, root_mountflags);
goto out;
}
ROOT_DEV = name_to_dev_t(root_device_name);
if (strncmp(root_device_name, "/dev/", 5) == 0)
root_device_name += 5;
}
is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
if (initrd_load())
goto out;
if (is_floppy && rd_doload && rd_load_disk(0))
ROOT_DEV = Root_RAM0;
mount_root();
out:
sys_mount(".", "/", NULL, MS_MOVE, NULL);
sys_chroot(".");
security_sb_post_mountroot();
}
我们可以看到,prepare_namespace中令root_device_name = saved_root_name = "/dev/mtdblock3" ,ROOT_DEV = name_to_dev_t(root_device_name), 然后令root_device_name += 5 = "mtdblock3" ,最后调用mount_root()函数,对根文件系统进行挂载。
接下来我们看init_post函数:
static int noinline init_post(void)
{
free_initmem();
unlock_kernel();
mark_rodata_ro();
system_state = SYSTEM_RUNNING;
numa_default_policy();
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
printk(KERN_WARNING "Warning: unable to open an initial console.\n");
(void) sys_dup(0);
(void) sys_dup(0);
if (ramdisk_execute_command) { //在parse_args()函数分析中我们知道,ramdisk_execute_command = NULL
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.");
}
显然,init_post()调用了run_init_process(execute_command),进入根文件系统对应的目录调用第一个程序init,如果失败,则继续执行:
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
若全部失败,则发出 panic 信息。
如果没有烧写根文件系统,内核找不到对应的init程序,则会出现上述panic信息:
Warning: unable to open an initial console.
Failed to execute /linuxrc. Attempting defaults...
Kernel panic - not syncing: No init found. Try passing init= option to kernel.
我们来实验一下,我将自己开发板上root分区的根文件系统擦除:
最后出现如下三行信息:
和我们分析的一模一样,证明我们的分析是对的。
以上就是我对start_kernel中rest_init函数的分析。