OpenWrt是一个开放的linux平台,主要用于带wifi的无线路由上。

类似于Ubuntu、Red Hat、之类的linux发行版本,它也有一套自己的启动流程。下面,我就以我的Linksys 的WRT54G为平台介绍一下,OpenWrt的启动流程。


首先,CFE(common firmware environment)--它就是一个bootloader,类似u-boot,redboot之类,有点broadcom公司御用之嫌--最先启动。它的任务只是创造一个简单的环境,让系统先运行起来。除了能够跳转到特定地址上启动操作系统(如Linux)外,它还能让你download东西到上面,比如download一个linux,然后启动它。另外,值得一提的是,CFE在启动之后会有1,2秒的时间等待由tftp上传的内核并烧写到flash上,这就给一些操作系统损坏但CFE还能工作的"砖头"板一个起死回生的机会。请注意一旦linux启动之后,将由linux全部接管系统,这时候就没CFE什么事情了。唯一的瓜葛是CFE传递给内核一个命令行的参数,这个可以在linux启动起来之后用下面的命令查看:


root@OpenWrt:/# cat /proc/cmdline
 root=/dev/mtdblock2 rootfstype=squashfs,jffs2 init=/etc/preinit noinitrd console=ttyS0,115200


之后,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
 ;;
 esac
 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口设定。

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.shfailsafe_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 sysfssize=$(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=1777if grep devfs /proc/filesystems > /dev/null; then
 mount devfs /dev -t devfs
 M0=/dev/pty/m0
 M1=/dev/pty/m1
 HOTPLUG=/sbin/hotplug-callelif [ -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
 HOTPLUG=
 fimkdir -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/.failsafeif [ -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
 fi

可见,它主要是挂载一些系统需要的文件系统,例如tmpfs,proc和sysfs(是否真正挂载取决于内核是否2.6的)。并且会准备设备节点和故障恢复(failsafe)的准备。(这里还没看懂 )

最后,exec /sbin/init启动文件系统,在OpenWrt上也就是busybox的init程序。它会自动分析/etc/inittab这个文件,其内容解释详见busybox网站的cmd help。

/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

运行/etc/init.d/rcS:

#!/bin/sh
 # Copyright (C) 2006 OpenWrt.orgrun_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"
 fi


将执行/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/S50httpd /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的启动流程:1.CFE->2.linux->3./etc/preinit->4./sbin/init ->5./etc/inittab ->6./etc/init.d/rcS->7./etc/rc.d/S* ->8.

OK,到这里基本有个整体的流程概念了。更多的内容待以后丰富。bye!