Openwrt学习之路-(3-Openwrt start-up process)

题图:gratisography

Openwrt start-up process

OpenWrt是一个典型的嵌入式Linux工程,主要添加了很多网络路由等功能,所以其启动过程与常见的嵌入式系统类似,不外乎Bootloader->EmbededOS->Filesystem->Application这四个过程,但内部细节它也有一套自己的方式,下面就介绍一下OpenWrt的启动流程。

OpenWrt的启动流程主要:1.Bootloader->2.linux->3./etc/preinit->4./sbin/init ->5./etc/inittab ->6./etc/init.d/rcS->7./etc/rc.d/S*

1.Bootloader


bootloader即最先启动的部分,类似u-boot、barebox、redboot。它的任务只是创造一个简单的环境,让系统先运行起来。除了能够跳转到特定地址上启动操作系统(如Linux)外,它还能让你download东西到上面,比如download一个linux,然后启动它。linux启动之后,将由linux全部接管系统。

另外,值得一提的是,bootloader在启动之后会有1,2秒的时间等待由tftp上传的内核并烧写到flash上。这就给一些操作系统损坏但bootloader还能工作的"砖头"板一个起死回生的机会。

2.Linux


这时候就没bootloader什么事情了。不过bootloader会传递给内核一个命令行的参数,这个可以在linux启动起来之后用下面的命令查看:

root@OpenWrt:/# cat /proc/cmdline
console=ttyS0,115200 mtdparts=spi_flash:1m(u-boot)ro,3m(kernel),-(rootfs)

至于linux系统的启动流程这边就不进行细说,可以参考另一篇文章“Linux start process”,这边说下Linux执行到最后要交给文件系统的部分。

Linux启动到后面会执行/init/main.c里面的kernel_init()函数,该函数最后几句就是调用文件系统的内容了,如下:

if (!try_to_run_init_process("/etc/preinit") ||
        !try_to_run_init_process("/sbin/init") ||
        !try_to_run_init_process("/etc/init") ||
        !try_to_run_init_process("/bin/init") ||
        !try_to_run_init_process("/bin/sh"))
        return 0;

可见下一步是要执行/etc/preinit脚本。

3.init=/etc/preinit


/etc/preinit的部分内容如下:

(前面十几行就是初始化一些参数,这些参数在后面的hook函数里面用到,这边将其省略不贴出)

[ -z "$PREINIT" ] && exec /sbin/init

...

. /lib/functions.sh
. /lib/functions/preinit.sh
. /lib/functions/system.sh

boot_hook_init preinit_essential
boot_hook_init preinit_main
boot_hook_init failsafe
boot_hook_init initramfs
boot_hook_init preinit_mount_root

for pi_source_file in /lib/preinit/*; do
    . $pi_source_file
done

boot_run_hook preinit_essential

pi_mount_skip_next=false
pi_jffs2_mount_success=false
pi_failsafe_net_message=false

boot_run_hook preinit_main

对于不同版本的openwrt会发现其/etc/preinit脚本会有些不一样,openwrt15.05的/etc/preinit最上面会多了一句[ -z "$PREINIT" ] && exec /sbin/init,后面再说为什么,先往下分析流程。

. /lib/functions.sh
. /lib/functions/preinit.sh
. /lib/functions/system.sh

三个. /将其他三个脚本包含进去,因为下面的boot_hook_init函数就位于/lib/functions/preinit.sh中,内容如下:

boot_hook_init() {
    local hook="${1}_hook"
    export -n "PI_STACK_LIST=${PI_STACK_LIST:+$PI_STACK_LIST }$hook"
    export -n "$hook="
}

使用boot_hook_init函数对5个hook链进行初始化。

对hook初始化后,执行下面语句

for pi_source_file in /lib/preinit/*; do
    . $pi_source_file
done

依次执行/lib/preinit/目录下的每个脚本,查看/lib/preinit/目录

/lib/preinit$ ls
02_default_set_state         10_indicate_preinit          70_initramfs_test
03_preinit_do_ramips.sh      10_sysinfo                   80_mount_root
04_handle_checksumming       30_failsafe_wait             99_10_failsafe_login
07_set_preinit_iface_ramips  40_run_failsafe_hook         99_10_run_init
10_indicate_failsafe         50_indicate_regular_preinit

里面的每个脚本都是调用boot_hook_add函数将链名加入对应的hook链中。

/lib/preinit/目录下的最后一个脚本是99_10_run_init,打开该脚本会发现新旧版本的openwrt里面会有些不同,如下:

新:
run_init() {
    preinit_ip_deconfig
}

boot_hook_add preinit_main run_init
旧:
run_init() {
    preinit_echo "- init -"
    preinit_ip_deconfig
    if [ "$pi_init_suppress_stderr" = "y" ]; then
    exec env - PATH=$pi_init_path $pi_init_env $pi_init_cmd 2>&0
    else
    exec env - PATH=$pi_init_path $pi_init_env $pi_init_cmd
    fi
}

boot_hook_add preinit_main run_init

可以发现旧的比新的openwrt多了一句执行语句,这就与上面旧的比新的openwrt少了一句[ -z "$PREINIT" ] && exec /sbin/init相呼应,只不过是执行/sbin/init的顺序前后改变罢了。

当hook链准备好后,就调用boot_run_hook,进行运行。

boot_run_hook preinit_essential
boot_run_hook preinit_main

所以整个/etc/preinit的流程如下:

(其实/etc/preinit到底是要干嘛我也没理解,只能大概理解其执行流程)

4./sbin/init


/sbin/init也就是busybox的init进程,该进程对应的代码在busybox根目录下的init/init.c中,这边将其程序流程如下:

init进程会自动分析/etc/inittab这个文件,根据配置文件决定启动那些程序,所以该init进程是后续所有进行的发起者,接下来就查看inittab配置文件。

5./etc/inittab


inittab配置文件中的每一条都被看做是一个初始化活动,busybox中定义了8种初始化活动供inittab文件使用,对于inittab更具体的用法格式等,将在另一篇“Linux profile and inittab file”进行讲解,这边主要讲Openwrt的启动过程。

查看/etc/inittab的内容:

::sysinit:/etc/init.d/rcS S boot
::shutdown:/etc/init.d/rcS K shutdown
ttyS0::askfirst:/bin/login
tty1::askfirst:/bin/ash --login

这边可以看到第一句sysinit后面接的是/etc/init.d/rcS脚本,所以下一步将执行rcS脚本。

6./etc/init.d/rcS


查看/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

最主要的一句就是run_scripts "$1" "$2",运行/etc/rc.d/目录下所有以S开头的脚本。

7./etc/rc.d/S*


可以看到/etc/rc.d/目录下好多S开头的脚本。

/etc/rc.d$ ls
K50dropbear  K98boot        S10system  S19firewall  S50dropbear  S95done
K85odhcpd    K99umount      S11sysctl  S20network   S50telnet    S96led
K89log       S00sysfixtime  S12log     S35odhcpd    S50uhttpd    S98sysntpd
K90network   S10boot        S12rpcd    S50cron      S60dnsmasq

那这些脚本是哪边来的呢,细心的你可能会看到/etc/init.d/目录下的文件名称怎么都跟这个一样呢,就差前面几个字符。

/etc/init.d$ ls
boot  dnsmasq  dropbear  led  network  rpcd    sysfixtime  system  uhttpd
cron  done     firewall  log  odhcpd   sysctl  sysntpd     telnet  umount

随便打开一写脚本看下,如system里面有START=10,telnet里面有START=50,与etc/rc.d/目录下的前缀一样。

/etc/init.d/目录下的脚本第一句话都是#!/bin/sh /etc/rc.common,即为运行 /etc/rc.common x.sh parameters,所以是通过/etc/rc.common脚本,将init.d的脚本链接到/etc/rc.d目录下,并且根据这些脚本中的START和STOP的关键字,添加K${STOP}和S${START}的前缀。

enable() {
    name="$(basename "${initscript}")"
    disable
    [ -n "$START" -o -n "$STOP" ] || {
        echo "/etc/init.d/$name does not have a START or STOP value"
        return 1
    }
    [ "$START" ] && ln -s "../init.d/$name" "$IPKG_INSTROOT/etc/rc.d/S${START}${name##S[0-9][0-9]}"
    [ "$STOP"  ] && ln -s "../init.d/$name" "$IPKG_INSTROOT/etc/rc.d/K${STOP}${name##K[0-9][0-9]}"
}

这样根据/etc/rc.d/S*的顺序就行运行开机脚本,整个系统启动完成,如果我们想自己加入一些启动脚本也要按/etc/init.d/目录下脚本的规则进行编写,放在/etc/init.d/下,这个比较简单这边就不在讲解。

Openwrt start process的分析就到这边,有感悟时会持续会更新。

注:以上内容都是本人在学习过程积累的一些心得,难免会有参考到其他文章的一些知识,如有侵权,请及时通知我,我将及时删除或标注内容出处,如有错误之处也请指出,进行探讨学习。文章只是起一个引导作用,详细的数据解析内容还请查看Openwrt相关教程,感谢您的查阅。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容