了解Openwrt系统的启动流程
1.1:系统介绍
任何系统的启动都是开发人员首要关注的问题,因为只有了解了系统的启动流程和启动机制,才能真正掌握一个系统,如果对系统的启动不熟悉的话,是不可能用好一个系统的。Openwrt系统也不例外,他的启动和一般的嵌入式系统的启动还有所区别,下面记录一下Openwrt的启动流程。
1.2:内核补丁
在openwrt的官网上面下载的源码,其中包括了一些内核补丁 ,这里究竟为什么要给内核做补丁呢?因为openwrt为了支持更多的路由器,更多的操作和openwrt特有的一些内核功能,linux源码是不具备的,这样openwrt为了增加这些功能,就需要在linux官网下载的源代码中做一些修改,在这里体现为给linux打补丁。
openwrt源码中的linux补丁文件放在$<openwrt_dir>/target/linux/generic/文件下面,有对于不同版本的linux内核补丁。根据采用不同的内核,使用不同的补丁。这些不同的内核补丁文件,在编译openwrt的时候,会首先把他们拷贝到内核目录下面,然后在内核上面打上这些补丁,然后在编译内核。首先分析他对于linux启动的补丁,他的名字是:921-use_preinit_as_init.patch(应该与平台有关),下面是一个实例:
--- a/init/main.c
+++ b/int/main.c
@@ -844,7 +844,8 @@ static int _ref kernel_init(void *unuse
pr_err("Failed to execute %s. Attempting default...\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/preinit")||
!run_init_process("/bin/sh"))
可以看到他修改linux中默认的启动项,可以看到它首先启动“/etc/preinit”.他是一个脚本,咱们就从脚本说起.
!/bin/sh
# Copyright (C) 2006 OpenWrt.org
# Copyright (C) 2010 Vertical Communications
[ -z "$PREINIT" ] && exec /sbin/init
export PATH=/usr/sbin:/usr/bin:/sbin:/bin
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="/usr/sbin:/usr/bin:/sbin:/bin"
pi_init_cmd="/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
对于这个bash脚本,前半部分定义了一些变量,先记住他们即可,但是有两个函数需要了解,即:boot_hook_init 和 boot_run_hook 他们的定义在/lib/functions/preinit.sh文件当中,boot_hook_init是初始化一个函数队列,boot_run_hook是运行一个函数队列,有一点该文件没有体现,后面文件当中会遇到,这里说明一下,boot_hook_add这个是在一个函数队列中添加一个函数。然后就是执行:
for pi_source_file in/lib/preinit/*
. $pi_source_file
done
循环执行/lib/preinit 目录下的一个文件,这里分析02_default_set_state,首先看看他的内容。
#!/bin/sh
define_default_set_state(){
. /etc/diag.h
}
boot_hook_add pre_main define_set_state
可以看到他就是在preinit_main 函数队列中增加一个函数,这个函数就是简单的执行一个脚本。当运行preinit_main 的时候,队列中的所有函数就会一次的执行。其他文件可以自行分析,都比较简单。最后在preini脚本中执行preinit_main。(作为初学者的我并不会。。)执行完这个脚本后init进程会根据inittab文件执行其他的启动项。
1.4inittab分析
inittab为linux初始化文件系统时init初始化文件系统用到的配置文件。这个文件负责设置init初始化程序初始化脚本在哪里;每个运行级初始化时命令的运行;开机,关机,重启对应的命令;个运行级登陆时运行的命令。
如果存在/etc/libtab文件,busybox init程序解析他,然后按照他的知识来创建各种子进程,否则使用默认的配置创建子进程。/etc/libtab 文件中每个条目用来定义一个子进程,并且确定他的启动方法,格式如下:
<id>:<runlevels>:<action>:<process>
1:id:表示这个子进程要用的控制台,如果省略,则使用与init进程一样的控制台。
2:runlevel:这个字段没有意义,可以省略。在linux下是具有意义的。
3:action:表示init进程如何控制这个子进程,下面会给出一个表格
4:process:这个就是我们要执行的可执行程序,当然也可以是一个脚本,如果process字段钱有一个“-”字符,这个程序就被称作是交互的。
名称 | 执行条件 | 说明 |
sysinit | 系统启动后最先执行 | 指定初始化脚本路径,当然只 执行一次,init进程等待他结束、 继续执行其他的动作。 |
wait | 系统执行完sysinit进程之后 | 只执行一次,init进程等待它结束 才继续执行其他动作 |
once | 系统执行完wait进程之后 | 只执行一次,init进程不会等待他结束 |
respawn | 启动完once进程后 | init进程检测发现子进程退出时,重新启动他,永不 结束,比如shell命令解释器 |
askfirst | 启动完respawn进程后 | 与respawn类似,不过init进程先输出“Please press enter toactive thisconsole”,等待用户输入 回车后才启动子进程。 |
shutdown | 当系统关机时 | 及重启。关闭系统时执行的程序 |
restart | 系统重启时 | init进程重启时执行的程序,通常是init程序本身先重新读取,解析/etc/initlab文件,在执行restart程序 |
ctrl+alt+del | 按下ctrl+alt+del按键的时候 | 按下ctrl+alt+del按键的时候执行的程序 |
下面是我自己的openwrt里的initlab内容:
::sysinit:/etc/init.d/rcS S boot
::shutdown:/etc/init.d/rcS K shutdown
::askconsole:/bin/ash --login
1.5总结
这里就不写了
在没有你的世界里,爱你,叶铮。