Linux內(nèi)核啟動流程我們分上下兩篇來理解:
上篇是板級引導(dǎo)階段,一般是用匯編實現(xiàn)。
下篇是通用內(nèi)核啟動階段,一般是C語言實現(xiàn)。
本文先講解上篇,大家看到匯編不用擔(dān)心看不懂,在內(nèi)核啟動階段,沒有特別復(fù)雜的流程,都是順序執(zhí)行,只需一句一句閱讀代碼即可。
如何理解板級引導(dǎo)階段?Linux內(nèi)核支持不同的芯片架構(gòu),比如我們熟知的ARM架構(gòu)、Intel的X86架構(gòu)、MPIS架構(gòu)、RISC-V架構(gòu)等等,可以在Linux內(nèi)核源碼的arch目錄看到不同的芯片架構(gòu)源碼放在對應(yīng)的文件夾內(nèi)。每種芯片架構(gòu)的目錄下都包含boot、configs、kernel、lib、Kconfig等文件或目錄。
我們以ARM為例,源碼放在arch/arm目錄下,如下圖所示。
1、文件入口
ARM架構(gòu)的Linux內(nèi)核的鏈接腳本文件放在arch/arm/kernel/vmlinux.lds,鏈接腳本定義了整個內(nèi)核編譯之后的鏈接過程,決定了一個可執(zhí)行程序文件的入口和各個section的存儲位置。Line 21的OUTPUT_ARCH(arm)指示了該鏈接腳本針對的是ARM芯片架構(gòu),Line 22的ENTRY(stext)指明了內(nèi)核入口為stext。因此,我們分析Linux內(nèi)核啟動流程,就從stext入口分析。
2、函數(shù)入口
ENTRY(stext)是Linux內(nèi)核的入口函數(shù),該函數(shù)定義在arch/arm/kernel/head.S文件。
根據(jù)代碼注釋,stext是Kernel startup entry point,一般地,它從解壓代碼中獲取,這里解壓代碼是指按照Linux內(nèi)核壓縮格式提取內(nèi)核可執(zhí)行文件,它的啟動要求如下:
- 關(guān)閉MMU
- 關(guān)閉D-cache
- 不關(guān)心I-cache
- R0=0
- R1=機(jī)器ID
- R2=atags或設(shè)備樹文件地址
3、調(diào)用safe_svcmode_maskall函數(shù),確保安全進(jìn)入SVC模式并屏蔽所有中斷
4、獲取處理器ID并找到對應(yīng)的procinfo
主要實現(xiàn)函數(shù)是__lookup_processor_type。
Linux 內(nèi)核將每種處理器都抽象為一個struct proc_info_list,因此可以通過process id 來找到對應(yīng)的 proc_info 結(jié)構(gòu),__lookup_processor_type 函數(shù)在__lookup_processor_type_data中找到對應(yīng)處理器的 proc info,將其保存到 R5 寄存器中。
5、調(diào)用函數(shù)__vet_atags 驗證 atags 或設(shè)備樹(dtb)的合法性
6、調(diào)用__create_page_tables創(chuàng)建頁表
7、準(zhǔn)備啟動start_kernel
start_kernel函數(shù)是在__mmap_switched函數(shù)內(nèi)調(diào)用的。
上圖的line 146指示將__mmap_switched的地址賦給R13,在line 158的__enable_mmu函數(shù)內(nèi)的__turn_mmu_on函數(shù)內(nèi)執(zhí)行R13,成功進(jìn)入start_kernel.
__INIT
__mmap_switched:
mov r7, r1
mov r8, r2
mov r10, r0
adr r4, __mmap_switched_data
mov fp, #0
#if defined(CONFIG_XIP_DEFLATED_DATA)
ARM( ldr sp, [r4], #4 )
THUMB( ldr sp, [r4] )
THUMB( add r4, #4 )
bl __inflate_kernel_data @ decompress .data to RAM
teq r0, #0
bne __error
#elif defined(CONFIG_XIP_KERNEL)
ARM( ldmia r4!, {r0, r1, r2, sp} )
THUMB( ldmia r4!, {r0, r1, r2, r3} )
THUMB( mov sp, r3 )
sub r2, r2, r1
bl memcpy @ copy .data to RAM
#endif
ARM( ldmia r4!, {r0, r1, sp} )
THUMB( ldmia r4!, {r0, r1, r3} )
THUMB( mov sp, r3 )
sub r2, r1, r0
mov r1, #0
bl memset @ clear .bss
ldmia r4, {r0, r1, r2, r3}
str r9, [r0] @ Save processor ID
str r7, [r1] @ Save machine type
str r8, [r2] @ Save atags pointer
cmp r3, #0
strne r10, [r3] @ Save control register values
mov lr, #0
b start_kernel
ENDPROC(__mmap_switched)