busybox里的init


busybox被大家比作瑞士軍刀,主要是它以很小的體積提供給我們很多很有用的shell指令。但是這里我們要關注的是busybox的init。

內容有些多,我們結合它的init.c的代碼做簡單介紹(busybox-1.11.2/init/init.c)。

1、首先busybox的init會嘗試只讀方式打開inittab,並讀取里面的配置:

static void parse_inittab(void)
{
    FILE *file;
    char buf[COMMAND_SIZE];

    if (ENABLE_FEATURE_USE_INITTAB)
        file = fopen(INITTAB, "r");
    else
        file = NULL;
    ...
    while (fgets(buf, COMMAND_SIZE, file) != NULL) {
        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;

        /* Skip leading spaces */
        id = skip_whitespace(buf);
        /* Trim the trailing '\n' */
        *strchrnul(id, '\n') = '\0';
        /* Skip the line if it is a comment */
        if (*id == '#' || *id == '\0')
            continue;

        /* Line is: "id:runlevel_ignored:action:command" */
        runlev = strchr(id, ':');
        if (runlev == NULL /*|| runlev[1] == '\0' - not needed */)
            goto bad_entry;
        action = strchr(runlev + 1, ':');
        if (action == NULL /*|| action[1] == '\0' - not needed */)
            goto bad_entry;
        command = strchr(action + 1, ':');
        if (command == NULL || command[1] == '\0')
            goto bad_entry;

        *command = '\0'; /* action => ":action\0" now */
        for (a = actions; a[0]; a += strlen(a) + 1) {
            if (strcmp(a + 1, action + 1) == 0) {
                *runlev = '\0';
                if (*id != '\0') {
                    if (strncmp(id, "/dev/", 5) == 0)
                        id += 5;
                    strcpy(tmpConsole, "/dev/");
                    safe_strncpy(tmpConsole + 5, id,
                        sizeof(tmpConsole) - 5);
                    id = tmpConsole;
                }
                new_init_action((uint8_t)a[0], command + 1, id);
                goto next_line;
            }
...
}

從上面,我們可以看到inittab是以#號作為注釋,然后其余部分的格式是:id:runlevel_ignored:action:command(留意這里的runlevel_ignored,busybox里面的runlevel_ignored沒有意義;也可以說busybox里面只有一個runlevel。)

而這里一共定義了STR_SYSINIT、STR_RESPAWN、STR_ASKFIRST、STR_WAIT、STR_ONCE、STR_CTRLALTDEL、STR_SHUTDOWN、STR_RESTART等一共8種action。

分析完inittab文件后,init把他們都放到了init_action_list里面,供后面使用。

 

2、執行具體的action是在run_actions()里面。比如,我們要執行SYSINIT,那么,我們就調用run_action(SYSINIT),如此,會從init_action_list里面找到所有標記為SYSINIT的條目,然后調用run來執行它。

static void run_actions(int action_type)
{
    struct init_action *a, *tmp;

    for (a = init_action_list; a; a = tmp) {
        tmp = a->next;
        if (a->action_type & action_type) {
            /* a->terminal of "" means "init's console" */
            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 /*, strerror(errno)*/);
                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)) {
                /* Only run stuff with pid==0.  If they have
                 * a pid, that means it is still running */
                if (a->pid == 0) {
                    a->pid = run(a);
                }
            }
        }
    }
}

這里有兩個地方需要留意:

1)在run()函數里面有調用fork,也就是,run_actions會把每個command都放到一個新的progress里面去執行。

2)RESPAWN和ASKFIRST是不會從action_list里面刪除掉的!!!所以,標記為這兩個ACTION的操作,在run之前會檢查進程在不在,不在的話就重啟,在的話什么都不做,繼續執行下一跳。

 

3、init_main

init_main()里面描述了init的動作過程。在parse_inittab之后,動作過程基本如下:

1)按SYSINIT、WAIT、ONCE,做好init的准備工作。具體比如,run_actions(SYSINIT)。

2)開始一個死循環。執行RESPAWN和ASKFIRST,如果這些process不存在,就重啟他們;監視子進程是否有掛掉的,如果有,就把他們的屍體拾掇拾掇,然后登記在冊。

 

4.關於/etc/init.d/rcS。在parse_inittab() 里面找到了它的蹤跡,它是在找不到inittab的情況下默認執行的動作之一;同時,其它一些實現中,rcS 會在 inittab 中被指定為 sysinit 執行腳本,與其他 inittab 任務並列存在。

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

    /* No inittab file -- set up some default behavior */
    if (file == NULL) {
        /* Reboot on Ctrl-Alt-Del */
        new_init_action(CTRLALTDEL, "reboot", "");
        /* Umount all filesystems on halt/reboot */
        new_init_action(SHUTDOWN, "umount -a -r", "");
        /* Swapoff on halt/reboot */
        if (ENABLE_SWAPONOFF)
            new_init_action(SHUTDOWN, "swapoff -a", "");
        /* Prepare to restart init when a QUIT is received */
        new_init_action(RESTART, "init", "");
        /* Askfirst shell on tty1-4 */
        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);
        /* sysinit */
        new_init_action(SYSINIT, INIT_SCRIPT, "");

        return;
    }

這里是講,如果我們沒有定義inittab,我們就在init_action_list里面添加上面羅列的這些action。

最后一個,INIT_SCRIP 指向的就是/etc/init.d/rcS ,唯一需要修改的 SYSINIT 動作,也就是由 /etc/init.d/rcS 來剩余的系統初始化動作。

 

5、inittab編程

busybox的inittab文件與通常的inittab不同,它沒有runlevel的概念,語句功能上也有限制。

inittab語句的標准格式是<id>:<runlevels>:<action>:<command>

各字段的含義如下:
<id>: id字段與通常的inittab中的含義不同,它代表的是這個語句中process執行所在的tty設備,內容就是/dev目錄中tty設備的文件名。由於是運行process的tty設備的文件名,所以也不能象通常的inittab那樣要求每條語句id的值唯一。
<runlevels>: 此字段完全被忽略。
<action>: 為下列這些值之一:
sysinit, respawn, askfirst, wait,once, restart, ctrlaltdel, shutdown
其含義與通常的inittab的定義相同。特別提一下askfirst,它的含義與respawn相同,只是在運行process前,會打出一句話 “please press Enter to active this console”,然后等用戶在終端上敲入回車鍵后才運行process。
<command>: 指定要運行process的命令行,比如下面,我們要在init完成后啟動dropbear,那么它的命令行參數是dropbear -F -d /etc/persistent/dropbear_dss_host_key -r /etc/。

下面是該系統使用的inittab:

# Executed on startup
::sysinit:/etc/rc.d/rc.sysinit
# Start an "askfirst" shell on the console (whatever that may be)
ttyS0::askfirst:/sbin/getty -L ttyS0 115200 vt100
# Stuff to do when restarting the init process
::restart:/sbin/init
# Run daemons
::wait:/usr/etc/rc.d/rc start
# Stuff to do before rebooting
::shutdown:/etc/rc.d/rc stop
::shutdown:/etc/rc.d/rc.stop
::shutdown:/bin/umount -a -r
null::respawn:/bin/infctld -m -c
null::respawn:/bin/lighttpd -D -f /etc/lighttpd.conf
null::respawn:/bin/dropbear -F -d /etc/persistent/dropbear_dss_host_key -r /etc/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM