一、OpenWrt啟動過程分析
轉自: http://www.eehello.com/?post=107
總結一下OpenWrt的啟動流程:1.CFE->2.linux->3./etc/preinit->4./sbin/init ->5./etc/inittab ->6./etc/init.d/rcS->7./etc/rc.d/S* ->8.
OpenWrt是一個開放的linux平台,主要用于帶wifi的無線路由上。
類似于Ubuntu、Red Hat、之類的linux發行版本,它也有一套自己的啟動流程。下面,我就以我的Linksys 的WRT54G為平台介紹一下,OpenWrt的啟動流程。
1.首先,CFE(common firmware environment)--它就是一個bootloader,類似u-boot,redboot之類,有點broadcom公司禦用之嫌--
最先啟動。它的任務隻是創造一個簡單的環境,讓系統先運作起來。除了能夠跳轉到特定位址上啟動作業系統(如Linux)外,
它還能讓你download東西到上面,比如download一個linux,然後啟動它。
另外,值得一提的是,CFE在啟動之後會有1,2秒的時間等待由tftp上傳的核心并燒寫到flash上,
這就給一些作業系統損壞但CFE還能工作的"磚頭"闆一個起死回生的機會。請注意一旦linux啟動之後,将由linux全部接管系統,
2.這時候就沒CFE什麼事情了。唯一的瓜葛是CFE傳遞給核心一個指令行的參數,這個可以在linux啟動起來之後用下面的指令檢視:
root@OpenWrt:/# cat /proc/cmdline
console=ttyS0,115200 mtdparts=spi_flash:1m(u-boot)ro,3m(kernel),-(rootfs)
之後,linux系統啟動起來了。它使用類似如下的腳本指令來解析cmdline:
for x in $(cat /proc/cmdline); do
case $x in
init=*)
init=${x#init=}
;;
root=*)
ROOT=${x#root=}
case $ROOT in
LABEL=*)
ROOT="/dev/disk/by-label/${ROOT#LABEL=}"
UUID=*)
ROOT="/dev/disk/by-uuid/${ROOT#UUID=}"
esac
rootflags=*)
ROOTFLAGS="-o ${x#rootflags=}"
cryptopts=*)
cryptopts="${x#cryptopts=}"
nfsroot=*)
NFSROOT="${x#nfsroot=}"
nfsopts=*)
NFSOPTS="-o ${x#nfsopts=}"
boot=*)
BOOT=${x#boot=}
resume=*)
RESUME=${x#resume=}
noresume)
NORESUME=y
quiet)
quiet=y
ro)
readonly=y
rw)
readonly=n
debug)
debug=y
exec >/tmp/initramfs.debug 2>&1
set -x
break=*)
break=${x#break=}
break)
break=premount
done
對于OpenWrt這個cmdline的意思就是:root=/dev/mtdblock2 檔案系統在第三個flash分區上(mtdblock0,1,2);
rootfstype=squashfs,jffs2 檔案系統類型是squashfs和jffs2(為什麼兩種類型,目前還不清楚,
不過可以确定OpenWrt使用了較為複雜的檔案系統,實作了squashfs的壓縮和jffs2的可寫 );
init=/etc/preinit 執行該初始化,noinitrd console=ttyS0,115200
沒有initrd和console口設定。
3.
init=/etc/preinit 是linux會執行的初始化,具體内容如下:
root@OpenWrt:/# cat /etc/preinit
#!/bin/sh
# Copyright (C) 2006 OpenWrt.org
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
. /etc/diag.sh
failsafe_ip() {
ifconfig $ifname 192.168.1.1 netmask 255.255.255.0 broadcast 192.168.1.2 55 up
}
failsafe() {
[ -n "$ifname" ] && grep "$ifname" /proc/net/dev >/dev/null && {
failsafe_ip
netmsg 192.168.1.255 "Entering Failsafe!"
telnetd -l /bin/login <> /dev/null 2>&1
lock /tmp/.failsafe
ash --login
mount proc /proc -t proc
mount sysfs /sys -t sysfs
size=$(awk '/MemTotal:/ {l=5242880;mt=($2*1024);print((s=mt/2)<l)&&(mt>l)?mt-l:s }' /proc/meminfo)
mount tmpfs /tmp -t tmpfs -o size=$size,nosuid,nodev,mode=1777
if grep devfs /proc/filesystems > /dev/null; then
mount devfs /dev -t devfs
M0=/dev/pty/m0
M1=/dev/pty/m1
HOTPLUG=/sbin/hotplug-call
elif [ -x /sbin/hotplug2 ]; then
mount -t tmpfs tmpfs /dev -o size=512K
mknod /dev/console c 5 1
/sbin/hotplug2 --coldplug --set-rules-file /etc/hotplug2-init.rules
/sbin/hotplug2 --no-coldplug --persistent --set-rules-file /etc/hotplug2 -init.rules &
M0=/dev/ptmx
M1=/dev/ptmx
HOTPLUG=
elif [ -x /sbin/udevd ]; then
mount -n -t tmpfs -o mode=0755 udev /dev
/sbin/udevd --daemon
/sbin/udevtrigger
/sbin/udevsettle
M0=/dev/pty/ptmx
M1=/dev/pty/ptmx
fi
mkdir -p /dev/pts /dev/shm
mount devpts /dev/pts -t devpts
# the shell really doesn't like having stdin/out closed
# that's why we use /dev/pty/m0 and m1 as replacement
# for /dev/console if there's no serial console available
dd if=/dev/console of=/dev/null bs=1 count=0 >/dev/null 2>/dev/null && {
M0=/dev/console
M1=/dev/console
exec <$M0 >$M1 2>&0
echo "- preinit -"
echo "Press CTRL-C for failsafe"
trap 'FAILSAFE=true' INT
trap 'FAILSAFE=true' USR1
[ -e /etc/preinit.arch ] && . /etc/preinit.arch
set_state preinit
echo "$HOTPLUG" > /proc/sys/kernel/hotplug
eval ${FAILSAFE:+failsafe}
lock -w /tmp/.failsafe
if [ -z "$INITRAMFS" ]; then
mount_root
[ -f /sysupgrade.tgz ] && {
echo "- config restore -"
cd /
mv sysupgrade.tgz /tmp
tar xzf /tmp/sysupgrade.tgz
rm -f /tmp/sysupgrade.tgz
sync
echo "- init -"
exec /sbin/init
可見,它主要是挂載一些系統需要的檔案系統,例如tmpfs,proc和sysfs(是否真正挂載取決于核心是否2.6的)。
并且會準備裝置節點和故障恢複(failsafe)的準備。(這裡還沒看懂 )
4.
最後,exec /sbin/init啟動檔案系統,在OpenWrt上也就是busybox的init程式。
它會自動分析/etc/inittab這個檔案,其内容解釋詳見busybox網站的cmd help。
5.
/etc/inittab的内容:
::sysinit:/etc/init.d/rcS S boot
::shutdown:/etc/init.d/rcS K stop
tts/0::askfirst:/bin/ash --login
ttyS0::askfirst:/bin/ash --login
tty1::askfirst:/bin/ash --login
6.
運作/etc/init.d/rcS:
run_scripts() {
for i in /etc/rc.d/$1*; do
[ -x $i ] && $i $2 2>&1
done | $LOGGER
LOGGER="cat"
[ -x /usr/bin/logger ] && LOGGER="logger -s -p 6 -t sysinit"
if [ "$1" = "S" ]; then
run_scripts "$1" "$2" &
else
run_scripts "$1" "$2"
7.
将執行/etc/rc.d/S*這些腳本:
root@OpenWrt:/# ls /etc/rc.d/S*
/etc/rc.d/S10boot
/etc/rc.d/S50cron
/etc/rc.d/S60led
/etc/rc.d/S20fstab
/etc/rc.d/S50dropbear
/etc/rc.d/S95done
/etc/rc.d/S39usb
/etc/rc.d/S50uhttpd
/etc/rc.d/S97watchdog
/etc/rc.d/S40network
/etc/rc.d/S50telnet
/etc/rc.d/S99sysctl
/etc/rc.d/S45firewall /
etc/rc.d/S60dnsmasq
按照數字從小到大的順序執行。
二、實作自啟動腳本
OpenWRT的啟動腳本放在 /etc/init.d 目錄下,而系統開機時自動運作/etc/rc.d目錄下的腳本。是以在rc.d目錄下、有init.d下腳本的連結檔案。
整理一下
05 defconfig //加載預設參數
10 boot //啟動
39 usb // 加載usbfs
40 network // 設定網卡參數
45 firewall // 防火牆
50 dropbear // sshd server
50 cron // .....
50 telnet // 如果沒有修改root密碼,則啟動telnet server
60 dnsmasq // DHCP 和 DNS 服務端
95 done // ...
96 led // 訓示燈
97 watchdog // ...
99 sysctl // 最後,進行必要的核心參數調整
然後,我們加入自己的腳本,實作子產品驅動的加載、應用程式的開機自啟動等。
首先在/etc/init.d裡添加需要啟動的shell腳本
例如:
vim startCamera
内容:
#!/bin/sh /etc/rc.common
# /init.d/startCamera
START=50
start()
{
./opt/ipnc/system_server &
stop()
killallsystem_server
之後還需要在rc.d目錄下做一個連結,啟動時系統會按順序啟動rc.d目錄下的腳本連結,對應執行init.d目錄下的啟動腳本。腳本的命名要符合系統規範,init.d下telnet腳本在rc.d目錄下的連結檔案名為S50telnet。是以連結檔案要在腳本名前加S+啟動順序數字,啟動順序要等系統進行完必要的初始化。是以我們命名為S95+腳本名。
指令:ln -s /etc/init.d/startCamera /etc/rc.d/S95startCamera
重新開機,即可
現在實作了應用程式的開機自啟動。
【作者】sky
【出處】http://www.cnblogs.com/sky-heaven/
【部落格園】 http://www.cnblogs.com/sky-heaven/
【知乎】 http://www.zhihu.com/people/zhang-bing-hua
【我的作品---旋轉倒立擺】 http://v.youku.com/v_show/id_XODM5NDAzNjQw.html?spm=a2hzp.8253869.0.0&from=y1.7-2
【我的作品---自平衡自動循迹車】 http://v.youku.com/v_show/id_XODM5MzYyNTIw.html?spm=a2hzp.8253869.0.0&from=y1.7-2
【大餅教你學系列】https://edu.csdn.net/course/detail/10393
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利.