本篇是通用内核启动阶段,一般是C语言实现。
承接上篇,start_kernel函数板级引导阶段进入通用内核启动阶段的第一个函数,从编程语言角度理解,也是汇编进入C语言的第一个函数。
该函数定义在init/main.c文件内,它主要完成Linux启动之前的一些初始化工作,该函数内调用的子函数非常多,大多子函数十分复杂,我们先从整体理解初始化流程,后续结合每个模块再回头理解子函数初始化的意义。
1、start_kernel函数添加注释,根据注释来理解
asmlinkage __visible void __init start_kernel(void)
{
char *command_line; // 存放BootLoader的传参
char *after_dashes;
set_task_stack_end_magic(&init_task); // 设置任务堆栈结束幻数,可以检测堆栈溢出
smp_setup_processor_id(); // 如果非SMP则为空函数,是SMP则设置处理器ID
debug_objects_early_init(); // debug提前初始化
cgroup_init_early(); // control group 提前初始化
local_irq_disable(); // 关闭当前CPU中断
early_boot_irqs_disabled = true; // 系统中断关闭标志,当early init完成后,设置为false
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them.
*/
boot_cpu_init(); // CPU相关初始化,CPU位图管理
page_address_init(); // 页地址初始化,主要是初始化高端内存映射表
pr_notice("%s", linux_banner); // 打印Linux版本信息和kernel编译时间等信息
early_security_init(); // LSM 早期初始化
setup_arch(&command_line); // 和ARM架构相关,解析ATAGS或设备树,解析的参数放入command_line
setup_command_line(command_line); // 保存命令行,日后再用
setup_nr_cpu_ids(); // 获取nr_cpu_ids个数,即CPU核数量
setup_per_cpu_areas(); // 设置SMP体系每个CPU使用的内存空间,并拷贝初始化段内的数据
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
boot_cpu_hotplug_init(); // 初始化CPU热插拔
build_all_zonelists(NULL); // 设置内存管理相关的node(节点,每个CPU一个内存节点)和其中的zone(内存域,包含于节点中,如)数据结构,以完成内存管理子系统的初始化,并设置bootmem分配器
page_alloc_init(); // 设置内存页分配通知器
pr_notice("Kernel command line: %s\\n", boot_command_line);
/* parameters may set static keys */
jump_label_init();
parse_early_param(); // 解析boot_command_line的参数
after_dashes = parse_args("Booting kernel",
static_command_line, __start___param,
__stop___param - __start___param,
-1, -1, NULL, &unknown_bootoption);
if (!IS_ERR_OR_NULL(after_dashes))
parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
NULL, set_init_arg);
/*
* These use large bootmem allocations and must precede
* kmem_cache_init()
*/
setup_log_buf(0); // 使用boot mem分配一个记录启动信息的缓冲区
vfs_caches_init_early(); // 前期虚拟文件系统(vfs)的缓存初始化
sort_main_extable(); // 对内核异常表(exception_table)按照异常向量号大小进行排序,以便加速访问
trap_init(); // 对内核陷阱异常进行初始化,在ARM系统里是空函数,没有任何的初始化
mm_init(); // 内存初始化,标记可使用内存,告知系统还剩多少内存可使用
ftrace_init(); // ftrace用于内核函数的trace功能
/* trace_printk can be enabled here */
early_trace_init(); // 为trace_printk等分配buffer
/*
* Set up the scheduler prior starting any interrupts (such as the
* timer interrupt). Full topology setup happens at smp_init()
* time - but meanwhile we still have a functioning scheduler.
*/
sched_init(); // 对进程调度器的数据结构进行初始化,创建运行队列,设置当前任务的空线程,当前任务的调度策略为CFS调度器
/*
* Disable preemption - early bootup scheduling is extremely
* fragile until we cpu_idle() for the first time.
*/
preempt_disable(); // 关闭优先级调度。由于每个进程任务都有优先级,目前系统还没有完全初始化,还不能打开优先级调度
if (WARN(!irqs_disabled(),
"Interrupts were enabled *very* early, fixing it\\n"))
local_irq_disable();
radix_tree_init(); // 内核radis 树算法初始化
/*
* Set up housekeeping before setting up workqueues to allow the unbound
* workqueue to take non-housekeeping into account.
*/
housekeeping_init(); // 在设置工作队列之前设置内部管理
/*
* Allow workqueue creation and work item queueing/cancelling
* early. Work item execution depends on kthreads and starts after
* workqueue_init().
*/
workqueue_init_early(); // 工作队列早期初始化
rcu_init(); // Read Copy Update 初始化
/* Trace events are available after this */
trace_init(); // trace event的初始化
if (initcall_debug)
initcall_debug_enable();
context_tracking_init();
/* init some links before init_ISA_irqs() */
early_irq_init(); // 前期外部中断描述符初始化,主要初始化数据结构
init_IRQ(); // 调用machine_desc- >init_irq()对中断初始化
tick_init(); // 初始化内核时钟系统
rcu_init_nohz();
init_timers(); // 初始化引导CPU的时钟相关的数据结构体,和初始化时钟软中断
hrtimers_init(); // 初始化