[Funkunux] Linux_2.6.22.6 内核 start_kernel 函数分析之 rest_init

来源:互联网 发布:立邦工作 知乎 编辑:程序博客网 时间:2024/05/23 01:32

前面我们已经对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"


start_kernel函数中调用的console_init函数会对console_cmdline[i]进行处理并初始化串口,这些我在上一篇文章中已经讲解过了。

接下来我们要对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();<span style="white-space:pre"></span>//调用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();<span style="white-space:pre"></span><span style="font-family: Arial, Helvetica, sans-serif;"></span><span style="font-family: Arial, Helvetica, sans-serif;">//调用init_post();</span>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) {<span style="white-space:pre"></span>//在parse_args()函数分析中我们知道,ramdisk_execute_command = NULLrun_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,如果失败,则继续执行:

<span style="white-space:pre"></span>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分区的根文件系统擦除:



然后boot:



最后出现如下三行信息:

和我们分析的一模一样,证明我们的分析是对的。

以上就是我对start_kernel中rest_init函数的分析。

0 0
原创粉丝点击