OpenWrt是一個開放的linux平臺,主要用于帶wifi的無線路由上。類似于Ubuntu、Red Hat、之類的linux發行版本,它也有一套自己的啟動流程。本文主要介紹了openwrt啟動過程及詳細分析。
1、概述
在linux的發展過程中,linux的啟動程序也在發展,從sysv init到現在的upstart、systemd,通常該程序是進程號為1的進程,該程序在linux系統有著舉足輕重的地方。在openwrt中,使用了另外一種啟動程序叫做procd,本文的重點并不在于介紹procd,本文主要介紹并解析procd、preinit及各種腳本如何完成整個系統的初始化。
2、軟件環境
Linux發行版:ubuntu14.04 LTS
Openwrt版本:barrier break 14.07 r42635 (linux kernel 3.10.49)
硬件:MPR-A2模塊(rt5350)
在查看linux內核代碼及根文件系統下的腳本之前,需要對openwrt進行配置,運行make menuconfig,在Target System中選擇Ralink RT288x/RT3xxx,Subtarget中選擇RT3x5x/RT5350based boards,Target 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),該函數創建兩個內核線程init和kthreadd之后,進入死循環,即所謂的0號進程。init線程執行kenrel_init()(init/main.c)函數,在kernel_init函數中,首先會檢查內核的啟動參數中是否有設置init參數,如果有,則會使用該參數指定的程序作為init程序,否則會按照如下代碼(打上patch后的)中所示的順序依次嘗試啟動,如果都無法啟動就會導致kernel panic。
if (!run_init_process(“/etc/preinit”) ||
?。un_init_process(“/sbin/init”) ||
!run_init_process(“/etc/init”) ||
?。un_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程序包中,編譯完會生成兩個可執行程序:init和procd,均在目 錄/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程序包中的init和procd程序)。這就是系統啟動完成后,ps命令顯示的進程號為1的進程名最終為/sbin/procd的由來,中間是有幾次變化的。
繼續看/etc/preinit腳本,出來變量設置外,接下來是執行了三個shell腳本:
/lib/functions.sh
/lib/functions/preinit.sh
/lib/functions/system.sh
注意“?!焙汀?”之間是有空格的,這里的點相當與souce命令,但souce是bash特有的,并不在POSIX標準中,“?!?/p>
是通用的用法。使用“。”的意思是在當前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的資料。
評論
查看更多