start_kernel其实也是内核的一个进程,它占用了进程号0,start_kernel的内容简写如下:
asmlinkage void __init start_kernel(void) //内核线程,0号进程idle进程 { ...... tick_init(); boot_cpu_init(); page_address_init(); //内存管理相关 kernel2.6.35.11/mm/highmem.c ...... setup_arch(&command_line); //内核解析uboot参数就在这步;内核页表 kernel2.6.35.11/arch/arm/kernel/setup.c mm_init_owner(&init_mm,&init_task); //kernel2.6.35.11/kernel/fork.c mm_init_cpumask(&init_mm); ...... page_alloc_init(); //内存页表相关 kernel2.6.35.11/mm/page_alloc.c ...... vfs_caches_init(totalram_pages); //rootfs创建,并将rootfs设置为系统根文件系统、rootfs根目录设置为系统根目录;这部是决定驱动加载关键 ...... mm_init(); //构建整个页表 kernel2.6.35.11/kernel/fork.c sched_init(); //进程管理相关 kernel2.6.35.11/kernel/sched.c ...... init_timers(); //内核定时器 kernel2.6.35.11/kernel/timer.c hrtimers_init(); //内核定时器 kernel2.6.35.11/kernel/timer.c ...... timekeeping_init(); time_init(); ...... page_cgroup_init(); //内核页表相关 kernel2.6.35.11/mm/page_cgroup.c ...... fork_init(totalram_pages); //kernel2.6.35.11/kernel/fork.c proc_caches_init(); //kernel2.6.35.11/kernel/fork.c ...... signals_init(); //信号量相关 kernel2.6.35.11/kernel/signal.c ...... rest_init(); }
接着制作根文件系统之内核挂接文件系统步骤分析继续分析,rest_init函数创建了kernel_init线程,进程号为1,kernel_init线程主要做了两件事情
1、调用prepare_namespace挂接根文件系统
2、调用init_post函数启动init进程
其中第一条已经分析过,接着分析第二条:调用init_post函数启动init进程,init_post位于init/Main.c中
748 static int noinline init_post(void) 749 { 750 free_initmem(); 751 unlock_kernel(); 752 mark_rodata_ro(); 753 system_state = SYSTEM_RUNNING; 754 numa_default_policy(); 755 756 if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)//尝试打开/dev/console设备,如果成功它就是stdin 757 printk(KERN_WARNING "Warning: unable to open an initial console.\n"); 758 759 (void) sys_dup(0);//将文件描述符0复制给文件描述符1 760 (void) sys_dup(0);//将文件描述符1复制给文件描述符2 761 762 if (ramdisk_execute_command) {//如果存在ramdisk_execute_command则执行ramdisk_execute_command不再返回 763 run_init_process(ramdisk_execute_command); 764 printk(KERN_WARNING "Failed to execute %s\n", 765 ramdisk_execute_command); 766 } 767 768 /* 769 * We try each of these until one succeeds. 770 * 771 * The Bourne shell can be used instead of init if we are 772 * trying to recover a really broken machine. 773 */ 774 if (execute_command) {//如果存在execute_command则执行ramdisk_execute_command不再返回 775 run_init_process(execute_command);//内核空间调用用户空间函数kernel_execve执行execute_command进程 776 777 printk(KERN_WARNING "Failed to execute %s. Attempting " 778 "defaults...\n", execute_command); 779 } 780 run_init_process("/sbin/init");//执行/sbin/init,不再返回 781 run_init_process("/etc/init");//执行/etc/init,不再返回 782 run_init_process("/bin/init");//执行/bin/init,不再返回 783 run_init_process("/bin/sh");//执行/bin/sh,不再返回 784 785 panic("No init found. Try passing init= option to kernel.");//到了这里,说明出错了,没有找到init程序 786 }
它的主要意思就是打开控制台设备,然后找到init进程,然后执行。其中execute_command的值在Linux移植之tag参数列表解析过程分析已经分析过,它的值就是Uboot传入的init参数的值/linuxrc,所以内核执行的第一个用户态进程就是linuxrc。这个进程是由kernel_init进程直接转换的,所以它的进程号也为1。这个就是init进程的启动过程。从上面分析可以知道根文件系统是提前建立好的,里面至少有/linuxrc程序、/dev/console文件。后面继续分析他们。