第一课--MicroWRT 系统启动流程

来自Microduino Wikipedia
Shengkai81@gmail.com讨论 | 贡献2014年10月26日 (日) 14:00的版本
跳转至: 导航搜索

任何系统的启动都是开发人员首要关注的问题,因为只有了解了系统的启动流程和启动机制,才能 真正掌握一个系统,如果对启动的启动不熟悉的话,是不可能真正用好一个系统,openwrt 系统也不例 外,它的启动和一般的嵌入式系统启动还有所区别,本教程就分析一下openwrt 的启动流程。

内核补丁

在openwrt 的官网上面下载的源码,其中包括了一些内核补丁,这里究竟为什么要给内核做补丁呢? 因 为openwrt 为了支持更多的路由器,更多的操作和openwrt 特有的一些内核功能,linux 源码是不具备 的,这样openwrt 为了增加这些功能,就需要在linux 官网上面下载的源代码中做一些修改,在这里体 现为给linux 源码打补丁。Openwrt 源码中的linux 补丁文件放在target/linux/generic 文件下面,有对于 不同版本的linux 内核补丁文件。我们以 3.10 版本的内核为例,它的补丁文件在patches-3.10 目录下面。这里是所有的内核补丁文件,在编译openwrt 的时候,会首先把他们拷贝到内核目录下面, 然后在内核上面打上这些补丁,然后再编译内核。咱们首先分析他对于linux 启动的补丁,它的名字是 921-use_preinit_as_init.patch,我们可以看看他的内容:

--- a/init/main.c
+++ b/init/main.c
@@ -840,7 +840,8 @@ static int __ref kernel_init(void *unuse
               pr_err("Failed to execute %s.  Attempting defaults...\n",
                       execute_command);
       }
-       if (!run_init_process("/sbin/init") ||
+       if (!run_init_process("/etc/preinit") ||
+           !run_init_process("/sbin/init") ||
run_init_process("/etc/init") run_init_process("/bin/init") run_init_process("/bin/sh"))


可以看到他它修改linux 内核中默认的启动项,可以看到它首先启动“/etc/preinit”,它是个脚本, 我们就从这个脚本说起。

Preinit 介绍

Preinit 脚本在etc 目录下面,首先先看看他的内容:

#!/bin/sh
# Copyright (C) 2006 OpenWrt.org
# Copyright (C) 2010 Vertical Communications
[ -z "$PREINIT" ] && exec /sbin/init
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
pi_ifname=
pi_ip=192.168.1.1
pi_broadcast=192.168.1.255
pi_netmask=255.255.255.0
fs_failsafe_ifname=
fs_failsafe_ip=192.168.1.1
fs_failsafe_broadcast=192.168.1.255
fs_failsafe_netmask=255.255.255.0
fs_failsafe_wait_timeout=2
pi_suppress_stderr="y"
pi_init_suppress_stderr="y"
pi_init_path="/bin:/sbin:/usr/bin:/usr/sbin"
pi_init_cmd="/sbin/init"
. /lib/functions.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

这就是个bash 脚本,前半部分只是定义了一些变量,先记住他们的内容即可,有两个函数是我们需要 了解的,boot_hook_init 和boot_run_hook。他们定义在/lib/functions/preinit.sh 文件中,boot_hook_init 是初始化一个函数队列,boot_run_hook 是运行一个函数队列,还有一个这个文件没有体现,后面的文 件中会遇到,这里说明一下,boot_book_add 这个是在一个函数队列中添加一个函数。然后就是执行:

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

循环执行/lib/preinit 目录下面的脚本,这里简要分析一个,这里分析02_default_set_state,首先看看他的 内容

#!/bin/sh
define_default_set_state(){
   ./etc/diag.sh
}
boot_hook_add preinit_main define_default_set_state

可以看到它就是在preinit_main 函数队列中增加一个函数,这个函数就是简单的执行一个脚本。当运行 preinit_main 的时候,队列中的所有函数就会依次执行。其他文件可以自行分析,都比较简单。

最后在preinit 脚本中执行preinit_main。执行完这个脚本之后init 进程会根据inittab 文件执行其他的启 动项。

Inittab 介绍

inittab 为linux 初始化文件系统时init 初始化程序用到的配置文件。这个文件负责设置init 初始化程序初 始化脚本在哪里;每个运行级初始化时运行的命令; 开机、关机、重启对应的命令;各运行级登陆时所运 行的命令。 如果存在/etc/inittab 文件,Busybox init 程序解析它,然后按照它的指示创建各种子进程,否则使用默 认的配置创建子进程. /etc/inittab 文件中每个条目用来定义一个子进程,并确定它的启动方法,格式如下 <id> : <runlevels> : <action> : <process> 1、id :表示这个子进程要使用的控制台,如果省略,则使用与init 进程一样的控制台. 2、runlevels:这个字段没有意义,可以省略。在linux 有意义. 3、action:表示init 进程如何控制这个子进程,具体取值见下表. 4、process:要执行的程序,它可以是可执行程序,也可以是脚本.如果process 字段前有“-”字符,这 个程序被称为“交互的”. 【attention】action 取值

名称 执行条件 说明
sysinit 系统启动后最先执行 指定初始化脚本路径,只执行一次,init进程等待它结束才继续执行其他动作
wait 系统执行完sysinit进城后 只执行一次,init进程等待它结束才能执行其他动作
once 系统执行完wait进程后 只执行一次,init进程不等待它结束
respawn 启动完once进程后 init进程监测发现子进程退出时,重新启动它,永不结束
askfirst 启动完respawn进程后 与respawn类似,不过init进程先输出“please press Enter toactivate this console", 等用户输入回车后才启动子进程
shutdown 当系统关机时 即重启,关闭系统时执行的程序
restart 当系统重启时 init进程重启时执行的程序,通常init程序本身先重新读取,解析/etc/inittab 文件,在执行 restart程序
ctrl+alt+del 按下Ctrl+Alt+Del键时 按Ctrl+Alt+Del组合键时执行的程序

先看看/etc/inittab 中的内容:

syhsinit:/etc/init.d/rcs_S boot
shutdown:/etc/init.d/rcs_K shutdown
askconsole:/bin/ash --login

从上面的分析可以看出它在开机启动的时候执行/etc/init.d/rcS 脚本,以前是有/etc/init.d/rcS 脚本的, 现在的openwrt 已经去掉了这个脚本文件,只要有rcS S boot 这几个参数就可以,但是功能是有的 就是按顺序执行/etc/rc.d 下面的各个脚本,以S 开头代表启动的时候执行的脚本,与命令行中的S 对应, 以K 开头的代表关机的时候需要执行的脚本,与命令行中的K 对应。

总结

从上面的分析我们可以总结一下 openwrt的启动流程如下: /etc/preinit->/lib/preinit/*->/etc/inittab->/etc/rc.d/S*