busybox-1.7.0分析
没有内挂载根文件系统时,内核启动后会出现以下提示:
panic("Noinit found.
原因就是没挂载根文件系统
1.在挂载根文件系统后,linux内核的最终目的是运行应用程序,要想了解应用程序是怎么运行起来的,就要分析init_post()函数,其中有如下源码:
if(execute_command) {
如果没有在命令行指定:init=xxx的话,就会调用:
中的一个,这是内核启动的第一个,也是唯一一个应用进程!
一般会运行第一个init进程,就是/sbin/init,这个init命令可以通过busybox来编译得到,所以我们需要从busybox的init..c文件开始分析!
busybox其实就是诸如ls、cd、cat之类命令的组合。我们编译busybox会得到一个应用程序busybox,当在命令行输入ls时,就会连接到busybox里面的ls命令,我们运行ls的结构和busybox ls的结果一致就说明这点。而且/sbin/init也要连接到busybox,所以busybox是个非常重要的东西,为了进一步了解它,阅读源码是必要的。
我们知道启动的第一个应用程序是/sbin/init,而这个程序是对应着busybox里的init文件的,所以我们从init这个文件来分析,分析之前我们首先要明确我们的目的是启动应用程序,init进程只是一个桥梁,因此可以大致知道init进程要做的工作:
(1)读取配置文件
(2)解析配置文件
(3)执行用户程序
注释1:上面execute_command在哪里定义的呢?从下面的代码中可以看出来:
static int__init init_setup(char *str)
{
unsignedint i;
execute_command = str;
for (i =1; i < MAX_INIT_ARGS; i++)
argv_init[i] = NULL;
return1;
}
__setup("init=", init_setup);
根据上一讲的内容,当我们在命令行参数里设置了init为某个值,那么就会调用init_setup()函数,在这个函数里,将init的值给了execute_command
2.下面我们来阅读busybox中init程序的源码,在init.c中的init_main()中:
1)首先是设置信号
2)初始化/dev/console
console_init();
3)解析配置文件
if (argc > 1
内核启动/sbin/init时没有传入任何参数,所以进入parse_inittab()函数,
我们进入到该函数:
file = fopen(INITTAB,"r");
#defineINITTAB
由此可以知道init进程读取的配置文件就是/etc/inittab, inittab文件的每一行对应一个子进程
busybox中的inittab文件中规定了/etc/inittab内容的填写格式如下:
<id>:<runlevel>:<action>:<process>
Id:id会加上一个/dev前缀作为一个控制终端(stdin,stdout,stderr)
Runlevel:忽略
Action:执行的时机,包括SYSINIT,WAIT,ONCE,RESPAWN,ASKFIRST等
Process:要执行的应用程序或者脚本
继续分析parse_inittab():
{
#if ENABLE_FEATURE_USE_INITTAB
}
如果配置文件/etc/inittab不存在,则执行if语句,init进程会直接调用new_init_action函数来创建init_action结构体,并将其放入链表,那么这个init_action链表就相当于配置文件inittab
根据if语句里的内容,我们可以反推出等效的/etc/inittab的内容如下:
::CTRLALTDEL:reboot
::SHUTDOWN:umount -a –r
::RESTART:init
::ASKFIRST:-/bin/ah
tty2:: ASKFIRST:-/bin/sh
tty3:: ASKFIRST:-/bin/sh
tty4:: ASKFIRST:-/bin/sh
::SYSINIT:/etc/init.d/rcS
接下来我们具体分析
(1)struct init_action *new_action, *a,*last;:创建struct init_action结构体,这个结构体定义是:
structinit_action {
structinit_action *next;
intaction;
pid_tpid;
charcommand[INIT_BUFFS_SIZE];
charterminal[CONSOLE_NAME_SIZE];
};
(2)strcmp(a->command,command)
对上面定义的结构体赋初始值
(3)for (a = last = init_action_list; a; a =a->next) :把这个结构体放入链表
总结一下就是当解析一个inittab时,会创建许多struct init_action结构体,对这些结构体赋值并加入连表里;
接着分析init_main:
init_main
到此为止,整个大的框架我们就分析完了,下面我们就用最简单的语言来做一个总结:
首先u-boot的start.S文件里有入口函数,进行了一系列的硬件初始化、设置命令、参数等工作之后,调用theKernel函数跳转到内核的入口函数,在head.S文件里面,接着会做解析参数等工作,最终调用run_init_process启动第一个也是唯一一个init进程,这个init进程的入口函数在busybox的init.c函数里面,接下来就会读取配置文件启动各个应用程序!
一个最小根文件系统必须的项:
1./dev/console
用于标准输入输出错误
2.init本身,即busybox
3./etc/inittab配置文件
4.inittab配置文件中指定的应用程序或脚本
5.相应的库(如C库)
http://blog.sina.com.cn/s/blog_7943319e01018g40.html