OpenWrt是一個開放的linux平臺,主要用于帶wifi的無線路由上。類似于Ubuntu、Red Hat、之類的linux發(fā)行版本,它也有一套自己的啟動流程。本文主要介紹了openwrt啟動過程及詳細(xì)分析。
1、概述
在linux的發(fā)展過程中,linux的啟動程序也在發(fā)展,從sysv init到現(xiàn)在的upstart、systemd,通常該程序是進(jìn)程號為1的進(jìn)程,該程序在linux系統(tǒng)有著舉足輕重的地方。在openwrt中,使用了另外一種啟動程序叫做procd,本文的重點(diǎn)并不在于介紹procd,本文主要介紹并解析procd、preinit及各種腳本如何完成整個系統(tǒng)的初始化。
2、軟件環(huán)境
Linux發(fā)行版:ubuntu14.04 LTS
Openwrt版本:barrier break 14.07 r42635 (linux kernel 3.10.49)
硬件:MPR-A2模塊(rt5350)
在查看linux內(nèi)核代碼及根文件系統(tǒng)下的腳本之前,需要對openwrt進(jìn)行配置,運(yùn)行make menuconfig,在Target System中選擇Ralink RT288x/RT3xxx,Subtarget中選擇RT3x5x/RT5350based boards,Target Profile選擇HAME MPR-A2,然后make完成openwrt的編譯。打完patch的內(nèi)核代碼在build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_rt305x/目錄下,根文件系統(tǒng)目錄在build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/root-ramips/。
3、啟動分析
3.1 內(nèi)核啟動點(diǎn)
Linux的啟動入口為start_kernel()(init/main.c),該函數(shù)的最后會調(diào)用rest_init()(init/main.c),該函數(shù)創(chuàng)建兩個內(nèi)核線程init和kthreadd之后,進(jìn)入死循環(huán),即所謂的0號進(jìn)程。init線程執(zhí)行kenrel_init()(init/main.c)函數(shù),在kernel_init函數(shù)中,首先會檢查內(nèi)核的啟動參數(shù)中是否有設(shè)置init參數(shù),如果有,則會使用該參數(shù)指定的程序作為init程序,否則會按照如下代碼(打上patch后的)中所示的順序依次嘗試啟動,如果都無法啟動就會導(dǎo)致kernel panic。
if (!run_init_process(“/etc/preinit”) ||
??!run_init_process(“/sbin/init”) ||
??!run_init_process(“/etc/init”) ||
!run_init_process(“/bin/init”) ||
?。un_init_process(“/bin/sh”))
return 0;
在目前的環(huán)境下,內(nèi)核啟動參數(shù)未設(shè)置init參數(shù),所以會以/etc/preinit程序作為init程序,preinit實(shí)際為shell腳本。
3.2 相關(guān)文件
3.2.1 腳本文件
/etc/preinit
/lib/functions.sh
/lib/functions/preinit.sh
/lib/functions/system.sh
/lib/preinit/下所有腳本
3.2.2 init程序
在proc程序包中,編譯完會生成兩個可執(zhí)行程序:init和procd,均在目 錄/sbin下,這兩個程序均會使用到。
3.3 過程分析
這個初始化過程遵循如下主線:
下面我們一步一步分析這個過程。
在/etc/preinit腳本中,第一條命令如下:
?。?-z “$PREINIT” ] && exec /sbin/init
在從內(nèi)核執(zhí)行這個腳本時,PREINIT這個變量時沒有定義的,所以會直接執(zhí)行/sbin/init。/sbin/init程序主要做了一些初始化工作,如環(huán)境變量設(shè)置、文件系統(tǒng)掛載、內(nèi)核模塊加載等,之后會創(chuàng)建兩個進(jìn)程,分別執(zhí)行/etc/preinit和/sbin/procd,執(zhí)行/etc/preinit之前會設(shè)置變量PREINIT,/sbin/procd會帶-h的參數(shù),當(dāng)procd退出后會調(diào)用exec執(zhí)行/sbin/proc替換當(dāng)前init進(jìn)程(具體過程可參見procd程序包中的init和procd程序)。這就是系統(tǒng)啟動完成后,ps命令顯示的進(jìn)程號為1的進(jìn)程名最終為/sbin/procd的由來,中間是有幾次變化的。
繼續(xù)看/etc/preinit腳本,出來變量設(shè)置外,接下來是執(zhí)行了三個shell腳本:
/lib/functions.sh
/lib/functions/preinit.sh
/lib/functions/system.sh
注意“?!焙汀?”之間是有空格的,這里的點(diǎn)相當(dāng)與souce命令,但souce是bash特有的,并不在POSIX標(biāo)準(zhǔn)中,“?!?/p>
是通用的用法。使用“。”的意思是在當(dāng)前shell環(huán)境下運(yùn)行,并不會在子shell中運(yùn)行。這幾個shell腳本主要定義了shell函數(shù),
特別是preinit.sh中,定義了hook相關(guān)操作的函數(shù)。
之后會使用boot_hook_init定義五個hook結(jié)點(diǎn)如下:
boot_hook_init preinit_essential
boot_hook_init preinit_main
boot_hook_init failsafe
boot_hook_init initramfs
boot_hook_init preinit_mount_root
之后會向這些結(jié)點(diǎn)中添加hook函數(shù)。
在之后就是一個循環(huán),依次在當(dāng)前shell下執(zhí)行/lib/preinit/目錄下的腳本,
for pi_source_file in /lib/preinit/*; do
$pi_source_file
done
/lib/preinit/目錄下的腳本具體類似的格式,定義要添加到hook結(jié)點(diǎn)的函數(shù),然后通過boot_hook_add將該函數(shù)添加到對應(yīng)的hook結(jié)點(diǎn)。
最后,/etc/preinit就會執(zhí)行boot_run_hook函數(shù)執(zhí)行對應(yīng)hook結(jié)點(diǎn)上的函數(shù)。在當(dāng)前環(huán)境下只執(zhí)行了preinit_essential和
preinit_main結(jié)點(diǎn)上的函數(shù),如下:
boot_run_hook preinit_essential
boot_run_hook preinit_main
到此,/etc/preinit執(zhí)行完畢并退出。如果需要跟蹤調(diào)試這些腳本,可以 在/etc/preinit的最開始添加一條命令set -x,這樣就會打印出執(zhí)行命令的過程, 當(dāng)并不會真正執(zhí)行。
至于系統(tǒng)服務(wù)程序的啟動及初始化將全由procd完成,procd的功能不僅在于此,它還集成更多其他功能,具體可參見procd的資料。
評論