1. 程式人生 > >Openwrt啟動流程及啟動指令碼分析

Openwrt啟動流程及啟動指令碼分析

1 概述

        在linux的發展過程中,linux的啟動程式也在發展,從sysv init到現在的upstartsystemd

通常該程式是程序號為1的程序,該程式在linux系統有著舉足輕重的地方。在openwrt中,使

用了另外一種啟動程式叫做procd,本文的重點並不在於介紹procd,本文主要介紹並解析

procdpreinit及各種指令碼如何完成整個系統的初始化。

2 軟體環境

        Linux發行版:ubuntu14.04 LTS

        Openwrt版本:barrier break 14.07 r42635 linux kernel 3.10.49

        硬體:MPR-A2模組(rt5350

        在檢視linux核心程式碼及根檔案系統下的指令碼之前,需要對openwrt進行配置,執行

make menuconfigTarget System中選擇Ralink RT288x/RT3xxxSubtarget中選擇RT3x5x/RT5350 

based boardsTarget Profile選擇HAME MPR-A2,然後make完成openwrt的編譯。打完patch的核心

程式碼在build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_rt305x/

目錄下,根檔案系統目

錄在build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/root-ramips/

3 啟動分析

3.1 核心啟動點

        Linux的啟動入口為start_kernel()init/main.c),該函式的最後會呼叫rest_init()init/main.c),

該函式建立兩個核心執行緒initkthreadd之後,進入死迴圈,即所謂的0號程序。init執行緒執行

kenrel_init()init/main.c)函式,在kernel_init函式中,首先會檢查核心的啟動引數中是否有設定init

引數,如果有,則會使用該引數指定的程式作為

init程式,否則會按照如下程式碼(打上patch後的)

中所示的順序依次嘗試啟動,如果都無法啟動就會導致kernel panic

                if (!run_init_process("/etc/preinit") ||

                        !run_init_process("/sbin/init") ||

                        !run_init_process("/etc/init") ||

                        !run_init_process("/bin/init") ||

                        !run_init_process("/bin/sh"))

                return 0;

        在目前的環境下,核心啟動引數未設定init引數,所以會以/etc/preinit程式作為init程式,preinit實際為shell指令碼。

3.2 相關檔案

3.2.1 指令碼檔案

        /etc/preinit

        /lib/functions.sh

        /lib/functions/preinit.sh

        /lib/functions/system.sh

        /lib/preinit/下所有指令碼

3.2.2 init程式

        在proc程式包中,編譯完會生成兩個可執行程式:initprocd,均在目 錄/sbin下,這兩個程式均會使用到。

3.3 過程分析

        這個初始化過程遵循如下主線:

 

        下面我們一步一步分析這個過程。

        在/etc/preinit指令碼中,第一條命令如下:

                [ -z "$PREINIT" ] && exec /sbin/init

        在從核心執行這個指令碼時,PREINIT這個變數時沒有定義的,所以會直接執行/sbin/init/sbin/init程式主要做

了一些初始化工作,如環境變數設定、檔案系統掛載、核心模組載入等,之後會建立兩個程序,分別執行/etc/preinit

/sbin/procd,執行/etc/preinit之前會設定變數PREINIT/sbin/procd會帶-h的引數,當procd退出後會呼叫exec執行

/sbin/proc替換當前init程序(具體過程可參見procd程式包中的initprocd程式)。這就是系統啟動完成後,ps命令顯示

的程序號為1的程序名最終為/sbin/procd的由來,中間是有幾次變化的。

        繼續看/etc/preinit指令碼,出來變數設定外,接下來是執行了三個shell指令碼:

                . /lib/functions.sh

                . /lib/functions/preinit.sh

                . /lib/functions/system.sh

        注意“.”和“/”之間是有空格的,這裡的點相當與souce命令,但soucebash特有的,並不在POSIX標準中,“.

是通用的用法。使用“.”的意思是在當前shell環境下執行,並不會在子shell中執行。這幾個shell指令碼主要定義了shell函式,

特別是preinit.sh中,定義了hook相關操作的函式。

        之後會使用boot_hook_init定義五個hook結點如下:

                boot_hook_init preinit_essential

                boot_hook_init preinit_main

                boot_hook_init failsafe

                boot_hook_init initramfs

                boot_hook_init preinit_mount_root

        之後會向這些結點中新增hook函式。

        在之後就是一個迴圈,依次在當前shell下執行/lib/preinit/目錄下的指令碼,

                for pi_source_file in /lib/preinit/*; do

                . $pi_source_file

                done

        /lib/preinit/目錄下的指令碼具體類似的格式,定義要新增到hook結點的函式,然後通過boot_hook_add將該函式新增到

對應的hook結點。

        最後,/etc/preinit就會執行boot_run_hook函式執行對應hook結點上的函式。在當前環境下只執行了preinit_essential

preinit_main結點上的函式,如下:

                boot_run_hook preinit_essential

                boot_run_hook preinit_main

        到此,/etc/preinit執行完畢並退出。如果需要跟蹤除錯這些指令碼,可以 在/etc/preinit的最開始新增一條命令set -x,這

樣就會打印出執行命令的過程, 當並不會真正執行。

        至於系統服務程式的啟動及初始化將全由procd完成,procd的功能不僅在於此,它還整合更多其他功能,具體可參見

procd的資料。

4 參考資料

        2.Openwrt barrier break 14.07原始碼