Linux內(nèi)核自解壓過程
uboot完成系統(tǒng)引導(dǎo)以后,執(zhí)行環(huán)境變量bootm中的命令;即,將Linux內(nèi)核調(diào)入內(nèi)存中并調(diào)用do_bootm函數(shù)啟動內(nèi)核,跳轉(zhuǎn)至kernel的起始位置。如果內(nèi)核沒有被壓縮,則直接啟動;如果內(nèi)核被壓縮過,則需要進(jìn)行解壓,被壓縮過的kernel頭部有解壓程序。
壓縮過的kernel入口第一個(gè)文件源碼位置在/kernel/arch/arm/boot/compressed/head.S。它將調(diào)用decompress_kernel()函數(shù)進(jìn)行解壓,解壓完成后,打印出信息“Uncompressing Linux...done,booting the kernel”。解壓縮完成后,調(diào)用gunzip()函數(shù)(或unlz4()、或bunzip2()、或unlz())將內(nèi)核放于指定位置,開始啟動內(nèi)核。
2. Linux內(nèi)核啟動準(zhǔn)備階段
由內(nèi)核鏈接腳本/kernel/arch/arm/kernel/vmlinux.lds可知,內(nèi)核入口函數(shù)為stext(/kernel/arch/arm/kernel/head.S)。內(nèi)核解壓完成后,解壓縮代碼調(diào)用stext函數(shù)啟動內(nèi)核。
ENTRY(stext) setmodePSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode@ and irqs disabled mrcp15, 0, r9, c0, c0 @ 獲得處理器ID,并存儲在r9寄存器中 bl__lookup_processor_type @ 結(jié)果返回:描述處理器結(jié)構(gòu)體的地址 r5=procinfo ,處理器ID號 r9=cpuid movsr10, r5 @ invalid processor (r5=0)?判斷內(nèi)核是否支持該處理器 beq__error_p @ yes, error 'p' bl__lookup_machine_type @結(jié)果返回:描述機(jī)器(開發(fā)板)的結(jié)構(gòu)體地址 r5=machinfo movsr8, r5 @ invalid machine (r5=0)?判斷內(nèi)核是否支持該機(jī)器(開發(fā)板) beq__error_a @ yes, error 'a' bl__vet_atags @檢查uboot給內(nèi)核的傳參ATAGS格式是否正確 bl__create_page_tables @建立虛擬地址映射頁表 ldrr13, __switch_data @ address to jump to after
(1)關(guān)閉IRQ、FIQ中斷,進(jìn)入SVC模式。調(diào)用setmode宏實(shí)現(xiàn);
(2)校驗(yàn)處理器ID,檢驗(yàn)內(nèi)核是否支持該處理器;若不支持,則停止啟動內(nèi)核。調(diào)用__lookup_processor_type函數(shù)實(shí)現(xiàn);
(3)校驗(yàn)機(jī)器碼,檢驗(yàn)內(nèi)核是否支持該機(jī)器;若不支持,則停止啟動內(nèi)核。調(diào)用__lookup_machine_type函數(shù)實(shí)現(xiàn);
(4)檢查uboot向內(nèi)核傳參ATAGS格式是否正確,調(diào)用__vet_atars函數(shù)實(shí)現(xiàn);
(5)建立虛擬地址映射頁表。此處建立的頁表為粗頁表,在內(nèi)核啟動前期使用。Linux對內(nèi)存管理有更精細(xì)的要求,隨后會重新建立更精細(xì)的頁表。調(diào)用__create_page_tables函數(shù)實(shí)現(xiàn)。
(6)跳轉(zhuǎn)執(zhí)行__switch_data函數(shù),其中調(diào)用__mmap_switched完成最后的準(zhǔn)備工作。
1)復(fù)制數(shù)據(jù)段、清除bss段,目的是構(gòu)建C語言運(yùn)行環(huán)境; 2)保存處理器ID號、機(jī)器碼、uboot向內(nèi)核傳參地址; 3)b start_kernel跳轉(zhuǎn)至內(nèi)核初始化階段。
__switch_data: .long__mmap_switched .......................................................... __mmap_switched: adrr3, __switch_data + 4 ldmiar3!, {r4, r5, r6, r7} cmpr4, r5@ Copy data segment if needed 1:cmpner5, r6 ldrnefp, [r4], #4 strnefp, [r5], #4 bne1b movfp, #0@ Clear BSS (and zero fp) 1:cmpr6, r7 strccfp, [r6],#4 bcc1b ARM(ldmiar3, {r4, r5, r6, r7, sp}) THUMB(ldmiar3, {r4, r5, r6, r7}) THUMB(ldrsp, [r3, #16]) strr9, [r4]@ Save processor ID strr1, [r5]@ Save machine type strr2, [r6]@ Save atags pointer bicr4, r0, #CR_A@ Clear 'A' bit stmiar7, {r0, r4}@ Save control register values bstart_kernel ENDPROC(__mmap_switched)
3. Linux內(nèi)核初始化階段
此階段從start_kernel函數(shù)開始。start_kernel函數(shù)是所有Linux平臺進(jìn)入系統(tǒng)內(nèi)核初始化的入口函數(shù)。它的主要工作是完成剩余與硬件平臺相關(guān)的初始化工作,在進(jìn)行一系列與內(nèi)核相關(guān)的初始化之后,調(diào)用第一個(gè)用戶進(jìn)程init并等待其執(zhí)行。至此,整個(gè)內(nèi)核啟動完成。
3.1 start_kernel函數(shù)的主要工作
start_kernel函數(shù)主要完成內(nèi)核相關(guān)的初始化工作。具體包括以下部分:
(1)內(nèi)核架構(gòu) 、通用配置相關(guān)初始化
(2) 內(nèi)存管理相關(guān)初始化
(3)進(jìn)程管理相關(guān)初始化
(4)進(jìn)程調(diào)度相關(guān)初始化
(5)網(wǎng)絡(luò)子系統(tǒng)管理
(6)虛擬文件系統(tǒng)
(7)文件系統(tǒng)
3.2 start_kernel函數(shù)流中的關(guān)鍵函數(shù)
(1)setup_arch(&command_line)函數(shù)
內(nèi)核架構(gòu)相關(guān)的初始化函數(shù),是非常重要的一個(gè)初始化步驟。其中,包含了處理器相關(guān)參數(shù)的初始化、內(nèi)核啟動參數(shù)(tagged list)的獲取和前期處理、內(nèi)存子系統(tǒng)的早期初始化。
command_line實(shí)質(zhì)是uboot向內(nèi)核傳遞的命令行啟動參數(shù),即uboot中環(huán)境變量bootargs的值。若uboot中bootargs的值為空,command_line = default_command_line,即為內(nèi)核中的默認(rèn)命令行參數(shù),其值在.config文件中配置,對應(yīng)CONFIG_CMDLINE配置項(xiàng)。
(2)setup_command_line、parse_early_param以及parse_args函數(shù)
這些函數(shù)都是在完成命令行參數(shù)的解析、保存。譬如,cmdline = console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3;解析為一下四個(gè)參數(shù):
console=ttySAC2,115200 //指定控制臺的串口設(shè)備號,及其波特率
root=/dev/mmcblk0p2 rw //指定根文件系統(tǒng)rootfs的路徑
init=/linuxrc //指定第一個(gè)用戶進(jìn)程init的路徑
rootfstype=ext3 //指定根文件系統(tǒng)rootfs的類型
(3)sched_init函數(shù)
初始化進(jìn)程調(diào)度器,創(chuàng)建運(yùn)行隊(duì)列,設(shè)置當(dāng)前任務(wù)的空線程。
(4)rest_init函數(shù)
rest_init函數(shù)的主要工作如下:
1)調(diào)用kernel_thread函數(shù)啟動了2個(gè)內(nèi)核線程,分別是:kernel_init和kthreadd。kernel_init線程中調(diào)用prepare_namespace函數(shù)掛載根文件系統(tǒng)rootfs;然后調(diào)用init_post函數(shù),執(zhí)行根文件系統(tǒng)rootfs下的第一個(gè)用戶進(jìn)程init。用戶進(jìn)程有4個(gè)備選方案,若command_line中init的路徑錯(cuò)誤,則會執(zhí)行備用方案。第一備用:/sbin/init,第二備用:/etc/init,第三備用:/bin/init,第四備用:/bin/sh。 2)調(diào)用schedule函數(shù)開啟內(nèi)核調(diào)度系統(tǒng); 3)調(diào)用cpu_idle函數(shù),啟動空閑進(jìn)程idle,完成內(nèi)核啟動。
審核編輯:劉清
-
處理器
+關(guān)注
關(guān)注
68文章
19851瀏覽量
234127 -
SVC
+關(guān)注
關(guān)注
0文章
33瀏覽量
12411 -
LINUX內(nèi)核
+關(guān)注
關(guān)注
1文章
317瀏覽量
22292 -
FIQ
+關(guān)注
關(guān)注
0文章
9瀏覽量
2400
原文標(biāo)題:linux 內(nèi)核啟動流程分析
文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
Linux內(nèi)核自解壓過程
嵌入式Linux主要有哪幾部分呢
嵌入式linux內(nèi)核啟動流程是怎樣的
Linux內(nèi)核啟動全過程解析
linux內(nèi)核啟動內(nèi)核解壓過程分析
基于Linux 2.6內(nèi)核Makefile分析

關(guān)于Linux 2.6內(nèi)核Makefile的分析
Linux內(nèi)核移植相關(guān)代碼解析
ARM處理器上的linux內(nèi)核啟動的過程詳細(xì)資料概述

嵌入式Linux內(nèi)核移植相關(guān)代碼分析
Linux內(nèi)核GPIO操作函數(shù)的詳解分析
STM32MP157 Linux系統(tǒng)移植開發(fā)篇8:Linux內(nèi)核配置方法及編譯

評論