Linux内核的启动流程

内核本身由一个作了raw_binary处理的目标文件(下文称为kernel.o)以及大量的在运动时可能被动态装载的内核模块构成。


关于内核映像的构成,可以参考http://blog.csdn.net/crazycoder8848/article/details/19156503

本文仅谈一谈内核的启动流程,基于3.10.102版本的内核源码。


内核的启动流程,开始于kernel.o中的一段入口代码。这部分代码是汇编代码,不同的cpu体系架构,各有不同的实现。

对32位x86而言,入口代码位于arch/x86/kernel/head_32.S中的ENTRY(startup_32)。这片代码在做完底层的相关配置后,便jmp到了C函数i386_start_kernel继续执行。i386_start_kernel中继续做一些cpu相关的初始化,最后调用与cpu架构无关的函数start_kernel,这就殊途同归的进入到了统一的内核初始化流程中。


start_kernel中做的各种初始化,在下并没有能力详述,只能简单罗列罗列。
可以看到,start_kernel中的工作有:
命令行参数的设置setup_command_line
命令行参数的解析parse_early_param,parse_args


另外,还有一个比较特殊的地方是:

在sched_init()执行完成后,此时的start_kernel对应的执行流,已经处于任务上下文中了,对应的task_struct为init_task(内核源码中能找到这样的定义DEFINE_PER_CPU(struct task_struct *, current_task) = &init_task;)。并且,他是Linux系统的第一个task,而且是唯一一个不是通过fork产生的task。


start_kernel在最后进入了rest_init,rest_init中创建了入口为kernel_init的内核线程,但是并没有让他立刻运行,只是为了让他得到pid为1的进程编号。然后又创建了kthreadd,然后通过complete(&kthreadd_done)启动其执行,并等待其执行完毕。 接下来,init_task又蜕变成了主cpu上的idle进程,并通过schedule_preempt_disabled让出了cpu。这一让出cpu,kernel_init便得到运行了。


kernel_init中做的是比较上层的初始化。例如,通过do_initcalls调用内核中各个模块的初始化函数。

do_initcalls相关内容可以参考http://blog.csdn.net/crazycoder8848/article/details/50847013

kernel_init线程做完内核中的各种初始化工作后,最终通过调用do_execve拉起用户态的init程序,从而成为用户态的首个进程。

由于内核初始化已经完成,已经可以为用户态程序提供服务了。因此,接下来用户态的一切的一切,就看init程序及其子孙进程的怎么整了。

你可能感兴趣的:(Linux内核,驱动开发移植,Linux内核学习笔记)