busybox-1.7.0分析

busybox-1.7.0分析  

没有内挂载根文件系统时,内核启动后会出现以下提示:

panic("Noinit found.  Try passing init= option tokernel.");

原因就是没挂载根文件系统

 

1.在挂载根文件系统后,linux内核的最终目的是运行应用程序,要想了解应用程序是怎么运行起来的,就要分析init_post()函数,其中有如下源码:

if(execute_command) {

 run_init_process(execute_command);

  printk(KERN_WARNING "Failed to execute%s.  Attempting "

    "defaults...\n", execute_command);

 }

 run_init_process("/sbin/init");

 run_init_process("/etc/init");

 run_init_process("/bin/init");

 run_init_process("/bin/sh");

 

如果没有在命令行指定:init=xxx的话,就会调用:

 run_init_process("/sbin/init");

 run_init_process("/etc/init");

 run_init_process("/bin/init");

 run_init_process("/bin/sh");

中的一个,这是内核启动的第一个,也是唯一一个应用进程!

一般会运行第一个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)首先是设置信号
    signal(SIGHUP,exec_signal);
    signal(SIGQUIT,exec_signal);
    signal(SIGUSR1,shutdown_signal);
    signal(SIGUSR2,shutdown_signal);
    signal(SIGINT,ctrlaltdel_signal);
    signal(SIGTERM,shutdown_signal);
    signal(SIGCONT,cont_handler);
    signal(SIGSTOP,stop_handler);
    signal(SIGTSTP,stop_handler);
2)初始化/dev/console
console_init();
3)解析配置文件
if (argc > 1
    && (!strcmp(argv[1], "single") ||!strcmp(argv[1], "-s") || LONE_CHAR(argv[1], '1'))
    ){
    }else {
        parse_inittab();
    }
内核启动/sbin/init时没有传入任何参数,所以进入parse_inittab()函数,

我们进入到该函数:
file = fopen(INITTAB,"r");        //打开配置文件"/etc/inittab"
#defineINITTAB      "/etc/inittab"

 

由此可以知道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(file == NULL)
{
    new_init_action(CTRLALTDEL,"reboot","");                

  new_init_action(SHUTDOWN, "umount -a-r", "");

  if (ENABLE_SWAPONOFF)new_init_action(SHUTDOWN, "swapoff -a", ""); //不执行

    new_init_action(RESTART,"init", "");
    new_init_action(ASKFIRST,bb_default_login_shell,"");        

 new_init_action(ASKFIRST,bb_default_login_shell,VC_2);      

  new_init_action(ASKFIRST,bb_default_login_shell,VC_3);        

 new_init_action(ASKFIRST,bb_default_login_shell,VC_4);
    new_init_action(SYSINIT,INIT_SCRIPT, "");
    return;
#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

 

接下来我们具体分析 下new_init_action这个函数:

(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)

 strcmp(a->terminal,cons)

 a->action =action

对上面定义的结构体赋初始值

(3)for (a = last = init_action_list; a; a =a->next) :把这个结构体放入链表

总结一下就是当解析一个inittab时,会创建许多struct init_action结构体,对这些结构体赋值并加入连表里;

 

接着分析init_main:

init_main

    parse_inittab

    run_actions(SYSINIT);//系统启动期间首先启动action为sysinit、wait、once的子进程

    run_actions(WAIT);

    run_actions(ONCE);

    while(1){

    run_actions(RESPAWN); //系统正常运行期间,启动action为respawn、askfirst的两类子进程

    run_actions(ASKFIRST); //并监视他们,发现某个子进程退出时,就重新启动它!

    

    }

 

到此为止,整个大的框架我们就分析完了,下面我们就用最简单的语言来做一个总结:

 

首先u-boot的start.S文件里有入口函数,进行了一系列的硬件初始化、设置命令、参数等工作之后,调用theKernel函数跳转到内核的入口函数,在head.S文件里面,接着会做解析参数等工作,最终调用run_init_process启动第一个也是唯一一个init进程,这个init进程的入口函数在busybox的init.c函数里面,接下来就会读取配置文件启动各个应用程序!

 

 

 

一个最小根文件系统必须的项:

1./dev/console  这个在内核启动时打开:sys_open((const char __user *) "/dev/console",O_RDWR, 0) 

用于标准输入输出错误

 /dev/null             当不设置标准输入输出标准出错时,就会定位到/dev/null

2.init本身,即busybox

3./etc/inittab配置文件

4.inittab配置文件中指定的应用程序或脚本

5.相应的库(如C库)


http://blog.sina.com.cn/s/blog_7943319e01018g40.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值