色婷婷AⅤ一区二区三区|亚洲精品第一国产综合亚AV|久久精品官方网视频|日本28视频香蕉

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > ARM Linux啟動(dòng)過程分析

          ARM Linux啟動(dòng)過程分析

          作者: 時(shí)間:2012-08-17 來源:網(wǎng)絡(luò) 收藏

          5) 調(diào)用 內(nèi)核映像

          Bootloader完成的最后一項(xiàng)工作便是調(diào)用 內(nèi)核。如果 內(nèi)核存放在 Flash 中,并且可直接在上面運(yùn)行(這里的 Flash 指 Nor Flash),那么可直接跳轉(zhuǎn)到內(nèi)核中去執(zhí)行。但由于在 Flash 中執(zhí)行代碼會(huì)有種種限制,而且速度也遠(yuǎn)不及 RAM 快,所以一般的嵌入式系統(tǒng)都是將 Linux內(nèi)核拷貝到 RAM 中,然后跳轉(zhuǎn)到 RAM 中去執(zhí)行。不論哪種情況,在跳到 Linux 內(nèi)核執(zhí)行之前 CUP的寄存器必須滿足以下條件:r0=0,r1=處理器類型,r2=標(biāo)記列表在 RAM中的地址。

          3. Linux內(nèi)核的

          將 Linux 內(nèi)核映像拷貝到 RAM 以后,可以通過下例代碼啟動(dòng) Linux 內(nèi)核:call_linux(0, machine_type, kernel_params_base)。

          其中,machine_tpye 是 檢測(cè)出來的處理器類型, kernel_params_base 是啟動(dòng)參數(shù)在 RAM 的地址。通過這種方式將 Linux 啟動(dòng)需要的參數(shù)從 傳遞到內(nèi)核。Linux 內(nèi)核有兩種映像:一種是非壓縮內(nèi)核,叫 Image,另一種是它的壓縮版本,叫zImage。根據(jù)內(nèi)核映像的不同,Linux 內(nèi)核的啟動(dòng)在開始階段也有所不同。zImage 是 Image經(jīng)過壓縮形成的,所以它的大小比 Image 小。但為了能使用 zImage,必須在它的開頭加上解壓縮的代碼,將 zImage 解壓縮之后才能執(zhí)行,因此它的執(zhí)行速度比 Image 要慢。但考慮到嵌入式系統(tǒng)的存儲(chǔ)空容量一般比較小,采用 zImage 可以占用較少的存儲(chǔ)空間,因此犧牲一點(diǎn)性能上的代價(jià)也是值得的。所以一般的嵌入式系統(tǒng)均采用壓縮內(nèi)核的方式。

          對(duì)于 系列處理器來說,zImage 的入口程序即為 arch/arm/boot/compressed/head.S。它依次完成以下工作:開啟 MMU 和 Cache,調(diào)用 decompress_kernel()解壓內(nèi)核,最后通過調(diào)用 call_kernel()進(jìn)入非壓縮內(nèi)核 Image 的啟動(dòng)。下面將具體分析在此之后 Linux 內(nèi)核的

          3.1 Linux內(nèi)核入口

          Linux 非壓縮內(nèi)核的入口位于文件/arch/arm/kernel/head-armv.S 中的 stext 段。該段的基地址就是壓縮內(nèi)核解壓后的跳轉(zhuǎn)地址。如果系統(tǒng)中加載的內(nèi)核是非壓縮的 Image,那么bootloader將內(nèi)核從 Flash中拷貝到 RAM 后將直接跳到該地址處,從而啟動(dòng) Linux 內(nèi)核。不同體系結(jié)構(gòu)的 Linux 系統(tǒng)的入口文件是不同的,而且因?yàn)樵撐募c具體體系結(jié)構(gòu)有關(guān),所以一般均用匯編語言編寫[3]。對(duì)基于 處理的 Linux 系統(tǒng)來說,該文件就是head-armv.S。該程序通過查找處理器內(nèi)核類型和處理器類型調(diào)用相應(yīng)的初始化函數(shù),再建立頁表,最后跳轉(zhuǎn)到 start_kernel()函數(shù)開始內(nèi)核的初始化工作。

          檢測(cè)處理器內(nèi)核類型是在匯編子函數(shù)__lookup_processor_type中完成的。通過以下代碼可實(shí)現(xiàn)對(duì)它的調(diào)用:bl __lookup_processor_type。__lookup_processor_type調(diào)用結(jié)束返回原程序時(shí),會(huì)將返回結(jié)果保存到寄存器中。其中r8 保存了頁表的標(biāo)志位,r9 保存了處理器的 ID 號(hào),r10 保存了與處理器相關(guān)的 struproc_info_list 結(jié)構(gòu)地址。

          檢測(cè)處理器類型是在匯編子函數(shù) __lookup_architecture_type 中完成的。與__lookup_processor_type類似,它通過代碼:“bl __lookup_processor_type”來實(shí)現(xiàn)對(duì)它的調(diào)用。該函數(shù)返回時(shí),會(huì)將返回結(jié)構(gòu)保存在 r5、r6 和 r7 三個(gè)寄存器中。其中 r5 保存了 RAM 的起始基地址,r6 保存了 I/O基地址,r7 保存了 I/O的頁表偏移地址。當(dāng)檢測(cè)處理器內(nèi)核和處理器類型結(jié)束后,將調(diào)用__create_page_tables 子函數(shù)來建立頁表,它所要做的工作就是將 RAM 基地址開始的 4M 空間的物理地址映射到 0xC0000000 開始的虛擬地址處。對(duì)筆者的 S3C2410 開發(fā)板而言,RAM 連接到物理地址 0x30000000 處,當(dāng)調(diào)用 __create_page_tables 結(jié)束后 0x30000000 ~ 0x30400000 物理地址將映射到0xC0000000~0xC0400000 虛擬地址處。

          當(dāng)所有的初始化結(jié)束之后,使用如下代碼來跳到 C 程序的入口函數(shù) start_kernel()處,開始之后的內(nèi)核初始化工作:

          b SYMBOL_NAME(start_kernel)

          3.2 start_kernel函數(shù)

          start_kernel是所有 Linux 平臺(tái)進(jìn)入系統(tǒng)內(nèi)核初始化后的入口函數(shù),它主要完成剩余的與硬件平臺(tái)相關(guān)的初始化工作,在進(jìn)行一系列與內(nèi)核相關(guān)的初始化后,調(diào)用第一個(gè)用戶進(jìn)程-init 進(jìn)程并等待用戶進(jìn)程的執(zhí)行,這樣整個(gè) Linux 內(nèi)核便啟動(dòng)完畢。該函數(shù)所做的具體工作有[4][5]

          1) 調(diào)用 setup_arch()函數(shù)進(jìn)行與體系結(jié)構(gòu)相關(guān)的第一個(gè)初始化工作;

          對(duì)不同的體系結(jié)構(gòu)來說該函數(shù)有不同的定義。對(duì)于 平臺(tái)而言,該函數(shù)定義在arch/arm/kernel/Setup.c。它首先通過檢測(cè)出來的處理器類型進(jìn)行處理器內(nèi)核的初始化,然后通過 bootmem_init()函數(shù)根據(jù)系統(tǒng)定義的 meminfo 結(jié)構(gòu)進(jìn)行內(nèi)存結(jié)構(gòu)的初始化,最后調(diào)用paging_init()開啟 MMU,創(chuàng)建內(nèi)核頁表,映射所有的物理內(nèi)存和 IO空間。

          2) 創(chuàng)建異常向量表和初始化中斷處理函數(shù);

          3) 初始化系統(tǒng)核心進(jìn)程調(diào)度器和時(shí)鐘中斷處理機(jī)制;

          4) 初始化串口控制臺(tái)(serial-console);

          ARM-Linux 在初始化過程中一般都會(huì)初始化一個(gè)串口做為內(nèi)核的控制臺(tái),這樣內(nèi)核在中就可以通過串口輸出信息以便開發(fā)者或用戶了解系統(tǒng)的啟動(dòng)進(jìn)程。

          5) 創(chuàng)建和初始化系統(tǒng) cache,為各種內(nèi)存調(diào)用機(jī)制提供緩存,包括;動(dòng)態(tài)內(nèi)存分配,虛擬文件系統(tǒng)(VirtualFile System)及頁緩存。

          6) 初始化內(nèi)存管理,檢測(cè)內(nèi)存大小及被內(nèi)核占用的內(nèi)存情況;

          7) 初始化系統(tǒng)的進(jìn)程間通信機(jī)制(IPC);

          當(dāng)以上所有的初始化工作結(jié)束后,start_kernel()函數(shù)會(huì)調(diào)用 rest_init()函數(shù)來進(jìn)行最后的初始化,包括創(chuàng)建系統(tǒng)的第一個(gè)進(jìn)程-init 進(jìn)程來結(jié)束內(nèi)核的啟動(dòng)。Init 進(jìn)程首先進(jìn)行一系列的硬件初始化,然后通過命令行傳遞過來的參數(shù)掛載根文件系統(tǒng)。最后 init 進(jìn)程會(huì)執(zhí)行用 戶傳遞過來的“init=”啟動(dòng)參數(shù)執(zhí)行用戶指定的命令,或者執(zhí)行以下幾個(gè)進(jìn)程之一:

          execve("/sbin/init",argv_init,envp_init);

          execve("/etc/init",argv_init,envp_init);

          execve("/bin/init",argv_init,envp_init);

          execve("/bin/sh",argv_init,envp_init)。

          linux操作系統(tǒng)文章專題:linux操作系統(tǒng)詳解(linux不再難懂)


          評(píng)論


          相關(guān)推薦

          技術(shù)專區(qū)

          關(guān)閉