Linux內核分析:Linux內核啓動流程分析

(注:本文參考資料:朱有鵬嵌入式課程、大神博客。本文爲我的學習記錄,若有錯誤,歡迎指正。內核版本:九鼎公司移植的2.6.35.7html

1. Linux內核自解壓過程

uboot完成系統引導之後,執行環境變量bootm中的命令;即,將Linux內核調入內存中並調用do_bootm函數啓動內核,跳轉至kernel的起始位置。若是內核沒有被壓縮,則直接啓動;若是內核被壓縮過,則須要進行解壓,被壓縮過的kernel頭部有解壓程序。linux

壓縮過的kernel入口第一個文件源碼位置在/kernel/arch/arm/boot/compressed/head.S。它將調用decompress_kernel()函數進行解壓,解壓完成後,打印出信息「Uncompressing Linux...done,booting the kernel」。解壓縮完成後,調用gunzip()函數(或unlz4()、或bunzip2()、或unlz())將內核放於指定位置,開始啓動內核。網絡

P.S.:內核格式類型詳見架構

2. Linux內核啓動準備階段

由內核連接腳本/kernel/arch/arm/kernel/vmlinux.lds可知,內核入口函數爲stext(/kernel/arch/arm/kernel/head.S)。內核解壓完成後,解壓縮代碼調用stext函數啓動內核。函數

P.S.:內核連接腳本vmlinux.lds在內核配置過程當中產生,由/kernel/arch/arm/kernel/vmlinux.lds.S文件生成。緣由是,內核連接腳本爲適應不一樣平臺,有條件編譯的需求,故由一個彙編文件來完成連接腳本的製做。post

 

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 @ 結果返回:描述處理器結構體的地址 r5=procinfo ,處理器ID號 r9=cpuid movsr10, r5 @ invalid processor (r5=0)?判斷內核是否支持該處理器 beq__error_p @ yes, error 'p' bl__lookup_machine_type @結果返回:描述機器(開發板)的結構體地址 r5=machinfo movsr8, r5 @ invalid machine (r5=0)?判斷內核是否支持該機器(開發板) beq__error_a @ yes, error 'a' bl__vet_atags @檢查uboot給內核的傳參ATAGS格式是否正確 bl__create_page_tables @創建虛擬地址映射頁表 ldrr13, __switch_data @ address to jump to after

 

(1)關閉IRQ、FIQ中斷,進入SVC模式。調用setmode宏實現;學習

(2)校驗處理器ID,檢驗內核是否支持該處理器;若不支持,則中止啓動內核。調用__lookup_processor_type函數實現;ui

(3)校驗機器碼,檢驗內核是否支持該機器;若不支持,則中止啓動內核。調用__lookup_machine_type函數實現;spa

(4)檢查uboot向內核傳參ATAGS格式是否正確,調用__vet_atars函數實現;.net

(5)創建虛擬地址映射頁表。此處創建的頁表爲粗頁表,在內核啓動前期使用。Linux對內存管理有更精細的要求,隨後會從新創建更精細的頁表。調用__create_page_tables函數實現。

(6)跳轉執行__switch_data函數,其中調用__mmap_switched完成最後的準備工做。

        1)複製數據段、清除bss段,目的是構建C語言運行環境;

        2)保存處理器ID號、機器碼、uboot向內核傳參地址;

        3)b   start_kernel跳轉至內核初始化階段。

 

__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內核初始化階段

此階段從start_kernel函數開始。start_kernel函數是全部Linux平臺進入系統內核初始化的入口函數。它的主要工做是完成剩餘與硬件平臺相關的初始化工做,在進行一系列與內核相關的初始化以後,調用第一個用戶進程init並等待其執行。至此,整個內核啓動完成。

3.1 start_kernel函數的主要工做

start_kernel函數主要完成內核相關的初始化工做。具體包括如下部分:

(1)內核架構 、通用配置相關初始化 

(2) 內存管理相關初始化

(3)進程管理相關初始化 

(4)進程調度相關初始化

(5)網絡子系統管理

(6)虛擬文件系統

(7)文件系統 

start_kernel函數詳解。

3.2 start_kernel函數流中的關鍵函數

 

(1)setup_arch(&command_line)函數

內核架構相關的初始化函數,是很是重要的一個初始化步驟。其中,包含了處理器相關參數的初始化、內核啓動參數(tagged list)的獲取和前期處理、內存子系統的早期初始化。

command_line實質是uboot向內核傳遞的命令行啓動參數,即uboot中環境變量bootargs的值。若uboot中bootargs的值爲空,command_line = default_command_line,即爲內核中的默認命令行參數,其值在.config文件中配置,對應CONFIG_CMDLINE配置項。

(2)setup_command_line、parse_early_param以及parse_args函數

這些函數都是在完成命令行參數的解析、保存。譬如,cmdline = console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3;解析爲一下四個參數:

  • console=ttySAC2,115200      //指定控制檯的串口設備號,及其波特率
  • root=/dev/mmcblk0p2 rw    //指定根文件系統rootfs的路徑
  • init=/linuxrc                           //指定第一個用戶進程init的路徑
  • rootfstype=ext3                    //指定根文件系統rootfs的類型

(3)sched_init函數

初始化進程調度器,建立運行隊列,設置當前任務的空線程。

(4)rest_init函數

rest_init函數的主要工做以下:

    1)調用kernel_thread函數啓動了2個內核線程,分別是:kernel_init和kthreadd。kernel_init線程中調用prepare_namespace函數掛載根文件系統rootfs;而後調用init_post函數,執行根文件系統rootfs下的第一個用戶進程init。用戶進程有4個備選方案,若command_line中init的路徑錯誤,則會執行備用方案。第一備用:/sbin/init,第二備用:/etc/init,第三備用:/bin/init,第四備用:/bin/sh。

    2)調用schedule函數開啓內核調度系統;

    3)調用cpu_idle函數,啓動空閒進程idle,完成內核啓動。

相關文章
相關標籤/搜索