zoukankan      html  css  js  c++  java
  • OpenWrt启动过程分析+添加自启动脚本【转】

    一、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

    ;;

    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口设定。

       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

    HOTPLUG=

    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

    fi

    可见,它主要是挂载一些系统需要的文件系统,例如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:

    #!/bin/sh

    # Copyright (C) 2006 OpenWrt.org

    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"

    fi

        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

    重启,即可

    现在实现了应用程序的开机自启动。

  • 相关阅读:
    QSet使用及Qt自定义类型使用QHash等算法
    QQueue与QStack使用
    QHash和QMultiHash使用
    【洛谷6633】[ZJOI2020] 抽卡(多项式毒瘤题)
    【洛谷5996】[PA2014] Muzeum(模拟费用流)
    【CF1063F】String Journey(后缀自动机+线段树)
    【BZOJ3640】JC的小苹果(高斯消元)
    【洛谷6478】[NOI Online #2 提高组] 游戏(树形DP+二项式反演)
    【洛谷6730】[WC2020] 猜数游戏(数论)
    【洛谷6186】[NOI Online #1 提高组] 冒泡排序(树状数组)
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/4515759.html
Copyright © 2011-2022 走看看