本篇是通用內核啟動階段,一般是C語言實現(xiàn)。
承接上篇,start_kernel函數(shù)板級引導階段進入通用內核啟動階段的第一個函數(shù),從編程語言角度理解,也是匯編進入C語言的第一個函數(shù)。
該函數(shù)定義在init/main.c文件內,它主要完成Linux啟動之前的一些初始化工作,該函數(shù)內調用的子函數(shù)非常多,大多子函數(shù)十分復雜,我們先從整體理解初始化流程,后續(xù)結合每個模塊再回頭理解子函數(shù)初始化的意義。
1、start_kernel函數(shù)添加注釋,根據(jù)注釋來理解
asmlinkage __visible void __init start_kernel(void)
{
char *command_line; // 存放BootLoader的傳參
char *after_dashes;
set_task_stack_end_magic(&init_task); // 設置任務堆棧結束幻數(shù),可以檢測堆棧溢出
smp_setup_processor_id(); // 如果非SMP則為空函數(shù),是SMP則設置處理器ID
debug_objects_early_init(); // debug提前初始化
cgroup_init_early(); // control group 提前初始化
local_irq_disable(); // 關閉當前CPU中斷
early_boot_irqs_disabled = true; // 系統(tǒng)中斷關閉標志,當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或設備樹,解析的參數(shù)放入command_line
setup_command_line(command_line); // 保存命令行,日后再用
setup_nr_cpu_ids(); // 獲取nr_cpu_ids個數(shù),即CPU核數(shù)量
setup_per_cpu_areas(); // 設置SMP體系每個CPU使用的內存空間,并拷貝初始化段內的數(shù)據(jù)
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
boot_cpu_hotplug_init(); // 初始化CPU熱插拔
build_all_zonelists(NULL); // 設置內存管理相關的node(節(jié)點,每個CPU一個內存節(jié)點)和其中的zone(內存域,包含于節(jié)點中,如)數(shù)據(jù)結構,以完成內存管理子系統(tǒng)的初始化,并設置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的參數(shù)
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分配一個記錄啟動信息的緩沖區(qū)
vfs_caches_init_early(); // 前期虛擬文件系統(tǒng)(vfs)的緩存初始化
sort_main_extable(); // 對內核異常表(exception_table)按照異常向量號大小進行排序,以便加速訪問
trap_init(); // 對內核陷阱異常進行初始化,在ARM系統(tǒng)里是空函數(shù),沒有任何的初始化
mm_init(); // 內存初始化,標記可使用內存,告知系統(tǒng)還剩多少內存可使用
ftrace_init(); // ftrace用于內核函數(shù)的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(); // 對進程調度器的數(shù)據(jù)結構進行初始化,創(chuàng)建運行隊列,設置當前任務的空線程,當前任務的調度策略為CFS調度器
/*
* Disable preemption - early bootup scheduling is extremely
* fragile until we cpu_idle() for the first time.
*/
preempt_disable(); // 關閉優(yōu)先級調度。由于每個進程任務都有優(yōu)先級,目前系統(tǒng)還沒有完全初始化,還不能打開優(yōu)先級調度
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(); // 前期外部中斷描述符初始化,主要初始化數(shù)據(jù)結構
init_IRQ(); // 調用machine_desc- >init_irq()對中斷初始化
tick_init(); // 初始化內核時鐘系統(tǒng)
rcu_init_nohz();
init_timers(); // 初始化引導CPU的時鐘相關的數(shù)據(jù)結構體,和初始化時鐘軟中斷
hrtimers_init(); // 初始化