OpenWrt是一個(gè)開放的linux平臺(tái),主要用于帶wifi的無線路由上。類似于Ubuntu、Red Hat、之類的linux發(fā)行版本,它也有一套自己的啟動(dòng)流程。本文主要介紹了openwrt啟動(dòng)過程及詳細(xì)分析。
1、概述
在linux的發(fā)展過程中,linux的啟動(dòng)程序也在發(fā)展,從sysv init到現(xiàn)在的upstart、systemd,通常該程序是進(jìn)程號(hào)為1的進(jìn)程,該程序在linux系統(tǒng)有著舉足輕重的地方。在openwrt中,使用了另外一種啟動(dòng)程序叫做procd,本文的重點(diǎn)并不在于介紹procd,本文主要介紹并解析procd、preinit及各種腳本如何完成整個(gè)系統(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)下的腳本之前,需要對(duì)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、啟動(dòng)分析
3.1 內(nèi)核啟動(dòng)點(diǎn)
Linux的啟動(dòng)入口為start_kernel()(init/main.c),該函數(shù)的最后會(huì)調(diào)用rest_init()(init/main.c),該函數(shù)創(chuàng)建兩個(gè)內(nèi)核線程init和kthreadd之后,進(jìn)入死循環(huán),即所謂的0號(hào)進(jìn)程。init線程執(zhí)行kenrel_init()(init/main.c)函數(shù),在kernel_init函數(shù)中,首先會(huì)檢查內(nèi)核的啟動(dòng)參數(shù)中是否有設(shè)置init參數(shù),如果有,則會(huì)使用該參數(shù)指定的程序作為init程序,否則會(huì)按照如下代碼(打上patch后的)中所示的順序依次嘗試啟動(dòng),如果都無法啟動(dòng)就會(huì)導(dǎo)致kernel panic。
if (!run_init_process(“/etc/preinit”) ||
??!run_init_process(“/sbin/init”) ||
?。un_init_process(“/etc/init”) ||
?。un_init_process(“/bin/init”) ||
??!run_init_process(“/bin/sh”))
return 0;
在目前的環(huán)境下,內(nèi)核啟動(dòng)參數(shù)未設(shè)置init參數(shù),所以會(huì)以/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程序包中,編譯完會(huì)生成兩個(gè)可執(zhí)行程序:init和procd,均在目 錄/sbin下,這兩個(gè)程序均會(huì)使用到。
3.3 過程分析
這個(gè)初始化過程遵循如下主線:
下面我們一步一步分析這個(gè)過程。
在/etc/preinit腳本中,第一條命令如下:
?。?-z “$PREINIT” ] && exec /sbin/init
在從內(nèi)核執(zhí)行這個(gè)腳本時(shí),PREINIT這個(gè)變量時(shí)沒有定義的,所以會(huì)直接執(zhí)行/sbin/init。/sbin/init程序主要做了一些初始化工作,如環(huán)境變量設(shè)置、文件系統(tǒng)掛載、內(nèi)核模塊加載等,之后會(huì)創(chuàng)建兩個(gè)進(jìn)程,分別執(zhí)行/etc/preinit和/sbin/procd,執(zhí)行/etc/preinit之前會(huì)設(shè)置變量PREINIT,/sbin/procd會(huì)帶-h的參數(shù),當(dāng)procd退出后會(huì)調(diào)用exec執(zhí)行/sbin/proc替換當(dāng)前init進(jìn)程(具體過程可參見procd程序包中的init和procd程序)。這就是系統(tǒng)啟動(dòng)完成后,ps命令顯示的進(jìn)程號(hào)為1的進(jìn)程名最終為/sbin/procd的由來,中間是有幾次變化的。
繼續(xù)看/etc/preinit腳本,出來變量設(shè)置外,接下來是執(zhí)行了三個(gè)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)行,并不會(huì)在子shell中運(yùn)行。這幾個(gè)shell腳本主要定義了shell函數(shù),
特別是preinit.sh中,定義了hook相關(guān)操作的函數(shù)。
之后會(huì)使用boot_hook_init定義五個(gè)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
之后會(huì)向這些結(jié)點(diǎn)中添加hook函數(shù)。
在之后就是一個(gè)循環(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ù)添加到對(duì)應(yīng)的hook結(jié)點(diǎn)。
最后,/etc/preinit就會(huì)執(zhí)行boot_run_hook函數(shù)執(zhí)行對(duì)應(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,這樣就會(huì)打印出執(zhí)行命令的過程, 當(dāng)并不會(huì)真正執(zhí)行。
至于系統(tǒng)服務(wù)程序的啟動(dòng)及初始化將全由procd完成,procd的功能不僅在于此,它還集成更多其他功能,具體可參見procd的資料。
評(píng)論