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

来自Microduino Wikipedia
跳转至: 导航搜索

任何系统的启动都是开发人员首要关注的问题,因为只有了解了系统的启动流程和启动机制,才能 真正掌握一个系统,如果对启动的启动不熟悉的话,是不可能真正用好一个系统,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"))

可以看到他它修改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 字段前有“-”字符,这 个程序被称为“交互的”.

名称 执行条件 说明
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*