init进程是由内核启动的第一个也是惟一的一个用户进程,它根据配置文件决定启动哪些程序,比如执行某些脚本,启动shell,运行用户指定的程序等。init进程是后续所有进程的发起者,比如init进程启动/bin/sh程序后,才能够在控制台上输入各种命令。

init进程的执行程序通常是sbin/init,上面讲述的init进程的作用只不过是/sbin/init这个程序的功能。在嵌入式领域,通常使用Busybox集成的init程序.嵌入式根目录下的bin,sbin和usr目录以及linuxc通常就是Busybox。

1、在kernel/init/main.c的init函数中有如下代码:

 if(execute_command)
 execve(execute_command,argv_init,envp_init);
 execve("/sbin/init",argv_init,envp_init);

bootloader会传给内核的main函数 init=/linuxrc这个参数,于是就会执行下面的这句:

execute_command = "linuxrc",

busybox中_install目录下的linuxrc是Busybox的一个软链接,指向/bin/busybox,而/sbin/init 也是/bin/busybox的符号链接,因此这个linxrc基本没有实际的意义只是一个连接作用。我们可以重写linuxrc,添加自己的一些初始化的东西。这样就可以把Linux内核中的init程序和Busybox中的init程序结合起来了。

网上说嵌入式中的busybox的脚本执行顺序为:

/sbin/init -> /etc/inittab -> /etc/init.d/rcS

2、Busybox init进程启动流程

Busybox是目标板系统上执行的第一个应用程序,当调用Busybox它会执行Busybox自身的init进程。

Busybox initt 程序对应的代码在init/init.c文件中。其对应的流程图如下:

其中与构建根文件系统关系密切的是控制台的初始化,对inittab文件的解释及执行。从图中我们可以看出,Busybox init启动的第一个函数是int init_main(int argc UNUSED_PARAM, char **argv),在这里可以设置信号的处理函数,初始化控制台,最重要的是解析inittab中内容。

init_main方法先通过parse_inittab分析inittab文件,将该文件中的每一行通过 new_init_action(uint8_t action_type, const char *command, const char *cons)添加到init_action_list列表中。其中cons就是每行的id字段。

init_action_list的定义如下:

struct init_action { 

    struct init_action * next; 
    pid_t pid;                            
    uint8_t action_type;                  
    char terminal[ CONSOLE_NAME_SIZE] ;     
    char command[ COMMAND_SIZE] ;           
} ; 

static struct init_action * init_action_list = NULL ;

若不支持ENABLE_FEATURE_USE_INITTAB或支持ENABLE_FEATURE_USE_INITTAB但inittab文件不存在,则执行一个默认的操作,如下:

    if ( ENABLE_FEATURE_USE_INITTAB) 
        file = fopen ( INITTAB, "r" ) ; 
    else 
        file = NULL ; 


    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 ; 
    }

INIT_SCRIPT。INIT_SCRIPT的定义如下:

#define INIT_SCRIPT  "/etc/init.d/rcS" 

即,BusyBox init默认的初始化脚本是/etc/init.d/rcS。

当支持ENABLE_FEATURE_USE_INITTAB且inittab文件存在时,BusyBox init会对inittab文件进行如下分析:

    //循环获取inittab文件中的每一行到buf中

    while ( fgets ( buf, COMMAND_SIZE, file ) ! = NULL ) {

        //定义action的种类 
        static const char actions[ ] = 
            STR_SYSINIT "sysinit/0" 
            STR_RESPAWN "respawn/0" 
            STR_ASKFIRST "askfirst/0" 
            STR_WAIT "wait/0" 
            STR_ONCE "once/0" 
            STR_CTRLALTDEL "ctrlaltdel/0" 
            STR_SHUTDOWN "shutdown/0" 
            STR_RESTART "restart/0" 
        ; 
        char tmpConsole[ CONSOLE_NAME_SIZE] ; 
        char * id, * runlev, * action, * command; 
        const char * a; 



        id = skip_whitespace( buf) ; 

        * strchrnul( id, '/n' ) = '/0' ;

         //若为注释,跳出本次循环   

        if ( * id = = '#' | | * id = = '/0' ) 
            continue ; 



         //获取runlev字段   
        runlev = strchr ( id, ':' ) ; 
        if ( runlev = = NULL  ) 
            goto bad_entry;

         //获取action字段   
        action = strchr ( runlev + 1, ':' ) ; 
        if ( action = = NULL  ) 
            goto bad_entry;

         //获取command字段 
        command = strchr ( action + 1, ':' ) ; 
        if ( command = = NULL | | command[ 1] = = '/0' ) 
            goto bad_entry; 


        * command = '/0' ;  
        for ( a = actions; a[ 0] ; a + = strlen ( a) + 1) {

              //查到数组actions中与action字段相同的元素   
            if ( strcmp ( a + 1, action + 1) = = 0) { 
                   //截取id字段,格式为"id/0"

                * runlev = '/0' ;

                   //若id字段非空 
                if ( * id ! = '/0' ) {

                       //若id字段带前缀/dev/,先截掉该前缀 
                    if ( strncmp ( id, "/dev/" , 5) = = 0) 
                        id + = 5;

                       //复制字符串/dev/到tmpConsole临时缓存区中 
                    strcpy ( tmpConsole, "/dev/" ) ;


                    safe_strncpy( tmpConsole + 5, id, 
                        sizeof ( tmpConsole) - 5) ;


                    id = tmpConsole; 
                }

                  //将action_type、command和控制终端id作为一init_action节点,添加到init_action_list列表中。从这里可以看出BusyBox init忽略了runlevel字段 
                new_init_action( ( uint8_t ) a[ 0] , command + 1, id) ; 
                goto next_line; 
            } 
        } 
        * command = ':' ; 

 bad_entry: 
        message( L_LOG | L_CONSOLE, "Bad inittab entry: %s" , id) ; 
 next_line: ; 
    }

2、BusyBox init分析完inittab之后,就是执行各command了。BusyBox init通过run_actions(int action_type)方法,查找init_action_list列表中action类型为action_type的所有init_action,并 为符合条件的init_action节点调用run(const struct init_action *a)。而实际上,run方法中,是通过init_exec(a->command)来执行具体的command。run_actions的源码如 下:

static void run_actions( int action_type) 
{ 
    struct init_action * a, * tmp; 
    // 遍历init_action_list列表,查找类型为action_type的节点 
    for ( a = init_action_list; a; a = tmp) { 
        tmp = a- > next;

        //查到类型为action_type的节点 
        if ( a- > action_type & action_type) { 
             // Pointless: run() will error out if open of device fails.

             //

             //if (a->terminal[0] && access(a->terminal, R_OK | W_OK)) {

             //    //message(L_LOG | L_CONSOLE, "Device %s cannot be opened in RW mode", a->terminal );

             //    delete_init_action(a);

             //} else

            if ( a- > action_type & ( SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART) ) { 
                waitfor( run( a) ) ; 
                delete_init_action( a) ; 
            } else if ( a- > action_type & ONCE) {


                run( a) ; 
                delete_init_action( a) ; 
            } else if ( a- > action_type & ( RESPAWN | ASKFIRST) ) { 



                if ( a- > pid = = 0) { 
                    a- > pid = run( a) ; 
                } 
            } 
        } 
    } 
}

由run_actions源码可知:action_type为SYSINIT、WAIT、CTRLALTDEL、SHUTDOWN 、 RESTART和ONCE的init_action,执行其command后,都会通过delete_init_action(struct init_action *action)将它从init_action_list中删除,这样做的原因可能是:这些init_action节点只会被BusyBox init执行一次,并且删除它们可提高对init_action_list其它类型的init_action节点的查找效率并节省空间。结合以下 BusyBox init对各类init_action的执行顺序,可能会更好的理解这一点。
由init_main方法可知,BusyBox init对各类init_action的执行顺序如下 :

 run_actions( SYSINIT) ;      
    run_actions( WAIT) ;      
    run_actions( ONCE) ;      
    while ( 1) {          
        run_actions( RESPAWN | ASKFIRST) ;          
        sleep ( 1) ;          
        wpid = wait( NULL ) ; 
        while ( wpid > 0) {              
            for ( a = init_action_list; a; a = a- > next) { 
                if ( a- > pid = = wpid) {                       
                    a- > pid = 0; 
                    message( L_LOG, "process '%s' (pid %d) exited. " 
                            "Scheduling for restart." , 
                            a- > command, wpid) ; 
                } 
            }              
            wpid = wait_any_nohang( NULL ) ; 
        } 
    } 
}

作者:zengjunfeng
http://blog.sina.com.cn/s/blog_735da7ae0102v2rh.html