Init 進程詳解

Android 內核加載完成後,就會啓動init進程,init進程是Android系統用戶空間的第一個進程。init程序放在系統根目錄下,init進程代碼位於源碼的目錄“system/core/init”下面。

下面我們來分析init進程的啓動過程

1. 分析入口函數

進程init入口函數是main,具體實現文件的路徑是:
system\core\init\init.c
分析main函數:

int main(int argc, char **argv)
{
    int fd_count = 0;
    struct pollfd ufds[4];
    char *tmpdev;
    char* debuggable;
    char tmp[32];
    int property_set_fd_init = 0;
    int signal_fd_init = 0;
    int keychord_fd_init = 0;
    bool is_charger = false;

    if (!strcmp(basename(argv[0]), "ueventd"))
        return ueventd_main(argc, argv);

    if (!strcmp(basename(argv[0]), "watchdogd"))
        return watchdogd_main(argc, argv);

    /* clear the umask */
    umask(0);

        /* Get the basic filesystem setup we need put
         * together in the initramdisk on / and then we'll
         * let the rc file figure out the rest.
         */
     //創建用戶空間文件目錄,讓rc能夠分辯這些目錄    
    mkdir("/dev", 0755);
    mkdir("/proc", 0755);
    mkdir("/sys", 0755);

    mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
    mkdir("/dev/pts", 0755);
    mkdir("/dev/socket", 0755);
    mount("devpts", "/dev/pts", "devpts", 0, NULL);
    mount("proc", "/proc", "proc", 0, NULL);
    mount("sysfs", "/sys", "sysfs", 0, NULL);

        /* indicate that booting is in progress to background fw loaders, etc */
     /* 檢測/dev/.booting文件是否可讀寫和創建*/
        close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));

        /* We must have some place other than / to create the
         * device nodes for kmsg and null, otherwise we won't
         * be able to remount / read-only later on.
         * Now that tmpfs is mounted on /dev, we can actually
         * talk to the outside world.
         */
     //重定向標準輸入輸出,錯誤輸出到"/dev/__null__"
    open_devnull_stdio();
    //設置Init日誌輸出設備爲"/dev/_ksg_" 
    klog_init();
    //  初始化屬性
    property_init();

    //從/proc/cpuinfo文件中獲取Hardware字段的值
    get_hardware_name(hardware, &revision);
    //處理內核命令行參數,主要是導入內核命令行參數和用屬性值設置內核變量
    process_kernel_cmdline();

    union selinux_callback cb;
    cb.func_log = klog_write;
    selinux_set_callback(SELINUX_CB_LOG, cb);

    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);
    //初始化SElinux,如果被禁止,則直接返回
    selinux_initialize();
    /* These directories were necessarily created before initial policy load
     * and therefore need their security context restored to the proper value.
     * This must happen before /dev is populated by ueventd.
     */
    restorecon("/dev");
    restorecon("/dev/socket");
    restorecon("/dev/__properties__");
    restorecon_recursive("/sys");

    is_charger = !strcmp(bootmode, "charger");

    INFO("property init\n");
    if (!is_charger)
        property_load_boot_defaults();

    INFO("reading config file\n");
    //解析/init.rc文件
    init_parse_config_file("/init.rc");
    //解析完/init.rc文件後,會得到一系列Action,按順序執行其中的Action.
    action_for_each_trigger("early-init", action_add_queue_tail);

    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    queue_builtin_action(keychord_init_action, "keychord_init");
    queue_builtin_action(console_init_action, "console_init");

    /* execute all the boot actions to get us started */
    action_for_each_trigger("init", action_add_queue_tail);

    /* skip mounting filesystems in charger mode */
    if (!is_charger) {
        action_for_each_trigger("early-fs", action_add_queue_tail);
        action_for_each_trigger("fs", action_add_queue_tail);
        action_for_each_trigger("post-fs", action_add_queue_tail);
        action_for_each_trigger("post-fs-data", action_add_queue_tail);
    }

    /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
     * wasn't ready immediately after wait_for_coldboot_done
     */
    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");

    queue_builtin_action(property_service_init_action, "property_service_init");
    queue_builtin_action(signal_init_action, "signal_init");
    queue_builtin_action(check_startup_action, "check_startup");

    if (is_charger) {
        action_for_each_trigger("charger", action_add_queue_tail);
    } else {
        action_for_each_trigger("early-boot", action_add_queue_tail);
        action_for_each_trigger("boot", action_add_queue_tail);
    }

        /* run all property triggers based on current state of the properties */
    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");


#if BOOTCHART
    queue_builtin_action(bootchart_init_action, "bootchart_init");
#endif
    //進入無限循環,建立init的子進程(init是所有進程的父進程)
    for(;;) {
        int nr, i, timeout = -1;
       //執行命令(子進程對應的命令),該函數會從Action隊列裏讀一個action和command進行執行
        execute_one_command();
        restart_processes();

        if (!property_set_fd_init && get_property_set_fd() > 0) {
            ufds[fd_count].fd = get_property_set_fd();
            ufds[fd_count].events = POLLIN;
            ufds[fd_count].revents = 0;
            fd_count++;
            property_set_fd_init = 1;
        }
        if (!signal_fd_init && get_signal_fd() > 0) {
            ufds[fd_count].fd = get_signal_fd();
            ufds[fd_count].events = POLLIN;
            ufds[fd_count].revents = 0;
            fd_count++;
            signal_fd_init = 1;
        }
        if (!keychord_fd_init && get_keychord_fd() > 0) {
            ufds[fd_count].fd = get_keychord_fd();
            ufds[fd_count].events = POLLIN;
            ufds[fd_count].revents = 0;
            fd_count++;
            keychord_fd_init = 1;
        }

        if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000;
            if (timeout < 0)
                timeout = 0;
        }

        if (!action_queue_empty() || cur_action)
            timeout = 0;

#if BOOTCHART
    //bootchart性能分析工具,收集系統和硬盤相關的信息,寫入磁盤以便其它的程序使用
        if (bootchart_count > 0) {
            if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)
                timeout = BOOTCHART_POLLING_MS;
            if (bootchart_step() < 0 || --bootchart_count == 0) {
                bootchart_finish();
                bootchart_count = 0;
            }
        }
#endif
        //等待下一個命令的提交
        nr = poll(ufds, fd_count, timeout);
        if (nr <= 0)
            continue;

        for (i = 0; i < fd_count; i++) {
            if (ufds[i].revents == POLLIN) {
                if (ufds[i].fd == get_property_set_fd())
                    handle_property_set_fd();
                else if (ufds[i].fd == get_keychord_fd())
                    handle_keychord();
                else if (ufds[i].fd == get_signal_fd())
                    handle_signal();
            }
        }
    }

    return 0;
}

init實際上就分爲如下兩個部分:
(1)初始化
初始化主要包括建立“dev”,”/proc”等目錄,初始化屬性,執行init.rc等初始化文件中的action等

(2)使用for循環,無限建立子進程的過程。

在Linux系統中,init進程是所有進程的父進程。輸入ps命令,所有進程都是由這個進程在for循環中創建的,如果這個進程崩潰,那麼android系統無法正常運行

# 2.配置文件詳解

在init進程中。配置文件是指文件init.rc,其路徑是:

system\core\rootdir\init.rc

2.1 init.rc 簡介

文件init.rc是一個可配置的初始化文件,相同目錄下的其它rc:

import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.trace.rc

定製了廠商可以配置的額外的初始化配置信息,具體格式爲:

/init.${ro.hardware}.rc

解析init.rc時,是按行讀取的,#表示註釋。

文件init.rc 包含4種狀態類別,分別是Actions、Commands、Services和Options。當聲明一個Service或者Action的時候,它將隱式聲明1個section.它之後跟隨的Command或者Option都將屬於這個section。Action和Service不能夠重名。

(1)Actions
Actions 就是在某種條件下觸發的一系列的命令,通常有一個trigger,形式如下所示:

on <trigger>
   <command>
   <command>

(2)Service
Service 的結構如下所示:

service <name> <pathname> <argument>
   <option>
   <option>

(3)Option
Option 是 Service 的修飾詞,主要包括如下選項

1.  critical

表明這是一個非常重要的服務。如果該服務4分鐘內退出大於4次,系統將會重啓並進入 Recovery (恢復)模式。

2. disabled

 表明這個服務不會同與他同trigger (觸發器)下的服務自動啓動。該服務必須被明確的按名啓動。

3.  setenv <name><value>

在進程啓動時將環境變量<name>設置爲<value>。

4.  socket <name><type> <perm> [ <user> [ <group> ] ]

   Create a unix domain socketnamed /dev/socket/<name> and pass

   its fd to the launchedprocess.  <type> must be"dgram", "stream" or "seqpacket".

   User and group default to0.

   創建一個unix域的名爲/dev/socket/<name> 的套接字,並傳遞它的文件描述符給已啓動的進程。<type> 必須是 "dgram","stream""seqpacket"。用戶和組默認是05.  user <username>

在啓動這個服務前改變該服務的用戶名。此時默認爲 root。

6.  group <groupname> [<groupname> ]*

在啓動這個服務前改變該服務的組名。除了(必需的)第一個組名,附加的組名通常被用於設置進程的補充組(通過setgroups函數),檔案默認是root。

7.  oneshot

   服務退出時不重啓。

8.  class <name>

   指定一個服務類。所有同一類的服務可以同時啓動和停止。如果不通過class選項指定一個類,則默認爲"default"類服務。

9. onrestart

    當服務重啓,執行一個命令(下詳)。

(4)trigger.
trigger支持的選項:

1.  boot

   這是init執行後第一個被觸發Trigger,也就是在 /init.rc被裝載之後執行該Trigger

2.  <name>=<value>

   當屬性<name>被設置成<value>時被觸發。例如,

on property:vold.decrypt=trigger_reset_main

    class_reset main

3.  device-added-<path>

    當設備節點被添加時觸發

4.  device-removed-<path>

   當設備節點被移除時添加

5. service-exited-<name>

   會在一個特定的服務退出時觸發

(5) command命令

1.  exec <path> [<argument> ]*

  創建和執行一個程序(<path>)。在程序完全執行前,init將會阻塞。由於它不是內置命令,應儘量避免使用exec ,它可能會引起init執行超時。

2.  export <name> <value>

在全局環境中將 <name>變量的值設爲<value>。(這將會被所有在這命令之後運行的進程所繼承)

3.  ifup <interface>

   啓動網絡接口

4.  import <filename>

   指定要解析的其他配置文件。常被用於當前配置文件的擴展

5.  hostname <name>

   設置主機名

6.  chdir <directory>

   改變工作目錄

7.  chmod <octal-mode><path>

   改變文件的訪問權限

8.  chown <owner><group> <path>

   更改文件的所有者和組

9.  chroot <directory>

  改變處理根目錄

10.  class_start<serviceclass>

   啓動所有指定服務類下的未運行服務。

11  class_stop<serviceclass>

  停止指定服務類下的所有已運行的服務。

12.  domainname <name>

   設置域名

13.  insmod <path>

   加載<path>指定的驅動模塊

14.  mkdir <path> [mode][owner] [group]

   創建一個目錄<path> ,可以選擇性地指定mode、owner以及group。如果沒有指定,默認的權限爲755,並屬於root用戶和 root組。

15. mount <type> <device> <dir> [<mountoption> ]*

   試圖在目錄<dir>掛載指定的設備。<device> 可以是mtd@name的形式指定一個mtd塊設備。<mountoption>包括 "ro"、"rw"、"re

16.  setkey

   保留,暫時未用

17.  setprop <name><value>

   將系統屬性<name>的值設爲<value>。

18. setrlimit <resource> <cur> <max>

   設置<resource>的rlimit (資源限制)

19.  start <service>

   啓動指定服務(如果此服務還未運行)。

20.stop<service>

   停止指定服務(如果此服務在運行中)。

21. symlink <target> <path>

   創建一個指向<path>的軟連接<target>。

22. sysclktz <mins_west_of_gmt>

   設置系統時鐘基準(0代表時鐘滴答以格林威治平均時(GMT)爲準)

23.  trigger <event>

  觸發一個事件。用於Action排隊

24.  wait <path> [<timeout> ]

等待一個文件是否存在,當文件存在時立即返回,或到<timeout>指定的超時時間後返回,如果不指定<timeout>,默認超時時間是5秒。

25. write <path> <string> [ <string> ]*

向<path>指定的文件寫入一個或多個字符串。  

Android系統系統根目錄中有一個init.rc文件,它事實上只是位於內存中,如果想要修改它,必須修改Rom中的內核鏡像(boot.img)

2 分析init.rc 的過程

init中對init.rc文件的解析,是通過init_parse_config_file文件進行的:

int init_parse_config_file(const char *fn)
{
    char *data;
    data = read_file(fn, 0);
    if (!data) return -1;

    parse_config(fn, data);
    DUMP();
    return 0;
}

調用 read_file函數將init.rc文件讀入到char*數組中,再調用 parse_config對其進行解析

static void parse_config(const char *fn, char *s)
{
    struct parse_state state;
    struct listnode import_list;
    struct listnode *node;
    char *args[INIT_PARSER_MAXARGS];
    int nargs;
    //初始化parse_state結構體
    nargs = 0;
    state.filename = fn;
    state.line = 0;
    state.ptr = s;
    state.nexttoken = 0;
    state.parse_line = parse_line_no_op;//空函數,無任何操作,在parse_new_section中會對賦具體操作

    list_init(&import_list);
    state.priv = &import_list;
      //  開始獲取每一個token,然後分析這些token,每一個token就是有空格、字表符和回車符分隔的字符串 
    for (;;) {
        /*  next_token函數相當於詞法分析器  */
        switch (next_token(&state)) {
        case T_EOF:
            state.parse_line(&state, 0, 0);
            goto parser_done;
        case T_NEWLINE:
            state.line++;
            if (nargs) {
                //讀取完一行後,對已經讀取的該行的第一個關鍵字進行查找
                int kw = lookup_keyword(args[0]);

                if (kw_is(kw, SECTION)) {//查看讀取的關鍵字是否是Section ,只有service和on滿足
                    state.parse_line(&state, 0, 0);
                    parse_new_section(&state, kw, nargs, args);
                } else {
                    state.parse_line(&state, nargs, args);//on 和 service對應不同的操作
                }
                nargs = 0;
            }
            break;
        case T_TEXT:
            if (nargs < INIT_PARSER_MAXARGS) {
                args[nargs++] = state.text;
            }
            break;
        }
    }

parser_done:
    list_for_each(node, &import_list) {
         struct import *import = node_to_item(node, struct import, list);
         int ret;

         INFO("importing '%s'", import->filename);
         ret = init_parse_config_file(import->filename);
         if (ret)
             ERROR("could not import file '%s' from '%s'\n",
                   import->filename, fn);
    }
}

通過上面代碼我們知道,在for循環中對init.rc的內容進行了解析,以一行一行的形式進行讀取

一個完整的編譯器(或解析器)最開始需要進行詞法和語法分析,詞法分析就是在源代碼文件中挑出一個個的Token,也就是說,詞法分析器的返回值是 Token,而語法分析器的輸入就是詞法分析器的輸出。也就是說,語法分析器需要分析一個個的token,而不是一個個的字符。由於init解析語言很簡 單,所以就將詞法和語法分析器放到了一起。詞法分析器就是next_token函數,而語法分析器就是T_NEWLINE分支中的代碼。這些就清楚多了。 現在先看看next_token函數(在parser.c文件中實現)是如何獲取每一個token的。

int next_token(struct parse_state *state)
{
    char *x = state->ptr;
    char *s;

    if (state->nexttoken) {
        int t = state->nexttoken;
        state->nexttoken = 0;
        return t;
    }

    for (;;) {
        switch (*x) {
        case 0:
            state->ptr = x;
            return T_EOF;
        case '\n':
            x++;
            state->ptr = x;
            return T_NEWLINE;
        case ' ':
        case '\t':
        case '\r':
            x++;
            continue;
        case '#':
            while (*x && (*x != '\n')) x++;
            if (*x == '\n') {
                state->ptr = x+1;
                return T_NEWLINE;
            } else {
                state->ptr = x;
                return T_EOF;
            }
        default:
            goto text;
        }
    }

textdone:
    state->ptr = x;
    *s = 0;
    return T_TEXT;
text:
    state->text = s = x;
textresume:
    for (;;) {
        switch (*x) {
        case 0:
            goto textdone;
        case ' ':
        case '\t':
        case '\r':
            x++;
            goto textdone;
        case '\n':
            state->nexttoken = T_NEWLINE;
            x++;
            goto textdone;
        case '"':
            x++;
            for (;;) {
                switch (*x) {
                case 0:
                        /* unterminated quoted thing */
                    state->ptr = x;
                    return T_EOF;
                case '"':
                    x++;
                    goto textresume;
                default:
                    *s++ = *x++;
                }
            }
            break;
        case '\\':
            x++;
            switch (*x) {
            case 0:
                goto textdone;
            case 'n':
                *s++ = '\n';
                break;
            case 'r':
                *s++ = '\r';
                break;
            case 't':
                *s++ = '\t';
                break;
            case '\\':
                *s++ = '\\';
                break;
            case '\r':
                    /* \ <cr> <lf> -> line continuation */
                if (x[1] != '\n') {
                    x++;
                    continue;
                }
            case '\n':
                    /* \ <lf> -> line continuation */
                state->line++;
                x++;
                    /* eat any extra whitespace */
                while((*x == ' ') || (*x == '\t')) x++;
                continue;
            default:
                    /* unknown escape -- just copy */
                *s++ = *x++;
            }
            continue;
        default:
            *s++ = *x++;
        }
    }
    return T_EOF;
}

next_token函數的代碼還是很多的,不過原理到很簡單。就是逐一讀取init.rc文件(還有import導入的初始化文件)的字符,並將 由空格、“/t”和“/r”分隔的字符串挑出來,並通過state->text返回。如果返回了正常的token,next_token函數就返回 T_TEXT。如果一行結束,就返回T_NEWLINE,如果init.rc文件的內容已讀取完,就返回T_EOF。當返回T_NEWLINE時,開始語 法分析(由於init初始化語言是基於行的,所以語言分析實際上就是分析init.rc文件的每一行,只是這些行已經被分解成一個個token了)

現在回到parse_config函數,先看一下T_TEXT分支。該分支將獲得的每一行的token都存儲在args數組中。現在來看 T_NEWLINE分支。該分支的代碼涉及到一個state.parse_line函數指針,該函數指針指向的函數負責具體的分析工作。但我們發現,一看 是該函數指針指向了一個空函數parse_line_no_op,實際上,一開始該函數指針什麼都不做,只是爲了使該函數一開始不至於爲null,否則調 用出錯。

(2)每讀完一行內容換行到下一行時,使用函數lookup_keyword分析已經讀取的一行的第一個參數

int lookup_keyword(const char *s)
{
    switch (*s++) {
    case 'c':
    if (!strcmp(s, "opy")) return K_copy;
        if (!strcmp(s, "apability")) return K_capability;
        if (!strcmp(s, "hdir")) return K_chdir;
        if (!strcmp(s, "hroot")) return K_chroot;
        if (!strcmp(s, "lass")) return K_class;
        if (!strcmp(s, "lass_start")) return K_class_start;
        if (!strcmp(s, "lass_stop")) return K_class_stop;
        if (!strcmp(s, "lass_reset")) return K_class_reset;
        if (!strcmp(s, "onsole")) return K_console;
        if (!strcmp(s, "hown")) return K_chown;
        if (!strcmp(s, "hmod")) return K_chmod;
        if (!strcmp(s, "ritical")) return K_critical;
        break;
    case 'd':
        if (!strcmp(s, "isabled")) return K_disabled;
        if (!strcmp(s, "omainname")) return K_domainname;
        break;
    case 'e':
        if (!strcmp(s, "xec")) return K_exec;
        if (!strcmp(s, "xport")) return K_export;
        break;
    case 'g':
        if (!strcmp(s, "roup")) return K_group;
        break;
    case 'h':
        if (!strcmp(s, "ostname")) return K_hostname;
        break;
    case 'i':
        if (!strcmp(s, "oprio")) return K_ioprio;
        if (!strcmp(s, "fup")) return K_ifup;
        if (!strcmp(s, "nsmod")) return K_insmod;
        if (!strcmp(s, "mport")) return K_import;
        break;
    case 'k':
        if (!strcmp(s, "eycodes")) return K_keycodes;
        break;
    case 'l':
        if (!strcmp(s, "oglevel")) return K_loglevel;
        if (!strcmp(s, "oad_persist_props")) return K_load_persist_props;
        break;
    case 'm':
        if (!strcmp(s, "kdir")) return K_mkdir;
        if (!strcmp(s, "ount_all")) return K_mount_all;
        if (!strcmp(s, "ount")) return K_mount;
        break;
    case 'o':
        if (!strcmp(s, "n")) return K_on;
        if (!strcmp(s, "neshot")) return K_oneshot;
        if (!strcmp(s, "nrestart")) return K_onrestart;
        break;
    case 'p':
        if (!strcmp(s, "owerctl")) return K_powerctl;
    case 'r':
        if (!strcmp(s, "estart")) return K_restart;
        if (!strcmp(s, "estorecon")) return K_restorecon;
        if (!strcmp(s, "mdir")) return K_rmdir;
        if (!strcmp(s, "m")) return K_rm;
        break;
    case 's':
        if (!strcmp(s, "eclabel")) return K_seclabel;
        if (!strcmp(s, "ervice")) return K_service;
        if (!strcmp(s, "etcon")) return K_setcon;
        if (!strcmp(s, "etenforce")) return K_setenforce;
        if (!strcmp(s, "etenv")) return K_setenv;
        if (!strcmp(s, "etkey")) return K_setkey;
        if (!strcmp(s, "etprop")) return K_setprop;
        if (!strcmp(s, "etrlimit")) return K_setrlimit;
        if (!strcmp(s, "etsebool")) return K_setsebool;
        if (!strcmp(s, "ocket")) return K_socket;
        if (!strcmp(s, "tart")) return K_start;
        if (!strcmp(s, "top")) return K_stop;
        if (!strcmp(s, "wapon_all")) return K_swapon_all;
        if (!strcmp(s, "ymlink")) return K_symlink;
        if (!strcmp(s, "ysclktz")) return K_sysclktz;
        break;
    case 't':
        if (!strcmp(s, "rigger")) return K_trigger;
        break;
    case 'u':
        if (!strcmp(s, "ser")) return K_user;
        break;
    case 'w':
        if (!strcmp(s, "rite")) return K_write;
        if (!strcmp(s, "ait")) return K_wait;
        break;
    }
    return K_UNKNOWN;
}

函數lookup_keyword主要對每一行的第一個字符做case判斷,然後調用strcmp命令對比剩餘的字符。對於serviceon命令,lookup_keyword函數返回K_serviceK_on.然後使用kw_is(kw,SECTION)判斷返回的kw是否屬於SECTION類型。在文件init.rc中,只有service和on滿足該類型。

(3)定義關鍵字

keyword.h文件中定義了init使用的關鍵字。

#\system\core\init\keword.h
#ifndef KEYWORD  //如果沒有定義KEYWORD宏,別走下面分支
....//聲明一些函數,這些函數就是前面所說的Action的執行函數
int do_chroot(int nargs, char **args);
int do_chdir(int nargs, char **args);
int do_class_start(int nargs, char **args);
int do_class_stop(int nargs, char **args);
...
int do_wait(int nargs, char **args);
#define __MAKE_KEYWORD_ENUM__ //定義一個宏。

//定義KEYWORD宏,雖然有4個參數,但只用第1個,其中K_##symbol中的##表示連接的意思,
//最後得到的值就是K_symbol.symbol就是init.rc中的關鍵字。

#define KEYWORD(symbol, flags, nargs, func) K_##symbol,
enum {//定義一個枚舉,這個枚舉定義了各個關鍵字的枚舉值。
    K_UNKNOWN,
#endif
    //根據上面的KEWORD定義,這裏將得到一個枚舉值 K_class
    KEYWORD(capability,  OPTION,  0, 0)
    KEYWORD(chdir,       COMMAND, 1, do_chdir)
    KEYWORD(chroot,      COMMAND, 1, do_chroot)
    KEYWORD(class,       OPTION,  0, 0)
    KEYWORD(class_start, COMMAND, 1, do_class_start) //K_class_start
    KEYWORD(class_stop,  COMMAND, 1, do_class_stop)  //K_class_stop
   ...
    KEYWORD(loglevel,    COMMAND, 1, do_loglevel)
    KEYWORD(load_persist_props,    COMMAND, 0, do_load_persist_props)
    KEYWORD(ioprio,      OPTION,  0, 0)
#ifdef __MAKE_KEYWORD_ENUM__
    KEYWORD_COUNT,
};
#undef __MAKE_KEYWORD_ENUM__
#undef KEYWORD
#endif

文件keywords.h在文件init_parser.c中被用到了兩次,具體引用代碼如下所示:

//第一次包含keyword.h,我們首先得到一個枚舉定義.
#include "keywords.h"

/*
 重新定義 KEYWORD宏,這四個參數全用上,看起來好像一個結構體,其中 #symbol表示一個字符串,其值爲"symbol"

*/

#define KEYWORD(symbol, flags, nargs, func) \
    [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },

//定義一個結構體keyword_info數組,它用來描述關鍵字關鍵字的一些屬性
struct {
    const char *name;//關鍵字的名稱
    int (*func)(int nargs, char **args);//對應關鍵字的處理函數
    unsigned char nargs;//參數個數,每個關鍵字的參數個數是固定的
    //關鍵字的屬性有三種:COMMAND,OPTION和SECTION,其中COMMAND有對應的處理函數
    unsigned char flags;
} keyword_info[KEYWORD_COUNT] = {
    [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
/*
  第二次包含keywords.h,由於已經重新定義了KEYWORD,所以以前那些枚舉值的關鍵字現在變成keyword_info數據的索引。
*/
#include "keywords.h"
};
#undef KEYWORD

#define kw_is(kw, type) (keyword_info[kw].flags & (type))
#define kw_name(kw) (keyword_info[kw].name)
#define kw_func(kw) (keyword_info[kw].func)
#define kw_nargs(kw) (keyword_info[kw].nargs)

3.解析 Service

由前面的函數lookup_kyword可知,在調用過程中中會對onservice所在的段進行進行解析,這裏首先分析service,在分析時以文件init.rc中的service zygote爲例。

1.Zygote 對應的 service section

在文件init.rc中,zygote 對應的service section的代碼如下:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main  #option 啓動main類
    socket zygote stream 660 root system  #option  開啓unix域的socket
    onrestart write /sys/android_power/request_state wake # onrestart 是option ,後面的是command
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd

解析section的入口函數是parse_new_section

void parse_new_section(struct parse_state *state, int kw,
                       int nargs, char **args)
{
    printf("[ %s %s ]\n", args[0],
           nargs > 1 ? args[1] : "");
    switch(kw) {
    case K_service: //用parse_service和parse_line_service解析service
        state->context = parse_service(state, nargs, args);
        if (state->context) {
            state->parse_line = parse_line_service;
            return;
        }
        break;
    case K_on: //用parse_action和parse_line_action解析action
        state->context = parse_action(state, nargs, args);
        if (state->context) {
            state->parse_line = parse_line_action;
            return;
        }
        break;
    case K_import:
        parse_import(state, nargs, args);
        break;
    }
    state->parse_line = parse
    _line_no_op;

}

1.service 結構體

init中使用了一個叫service的結構體來保存與service section相關的信息

#init.h
struct service {
        /* list of all services */
    //listnode是一個特殊的結構體,在內核代碼中使用非常多,一般位於另一個結構體的首部
    //主要用來將另一個結構體鏈接成一個雙向鏈表。init中有一個全局的service_list,專門用來保存解析配置文件後得到的service.
    struct listnode slist;

    const char *name;//service 名稱,與我們這個例子對應的就是"zygote"
    const char *classname; // service 所屬 class名稱,zygote對應main,默認default

    unsigned flags; //service 的屬性
    pid_t pid;      //進程號
    time_t time_started;    /* time of last start */  //上次啓動時間
    time_t time_crashed;    /* first crash within inspection window *///上次死亡時間
    int nr_crashed;         /* number of times crashed within window *///死亡次數

    uid_t uid;//用戶ID,組ID
    gid_t gid;
    gid_t supp_gids[NR_SVC_SUPP_GIDS];
    size_t nr_supp_gids;

    char *seclabel;

    /*

        有些service 要用到socket,socketinfo 描述 socketinfo 相關的信息,它是一個單向的鏈表
        struct socketinfo {
            struct socketinfo *next;
            const char *name; //socket 名字 ,本例爲zygote
            const char *type; // 類型  本例爲 AF_STREAM 其實就是TCP 類型
            uid_t uid;
            gid_t gid;
            int perm; //讀寫權限,本例爲660
};
    */
    struct socketinfo *sockets;
    //service一般運行在一個單獨的進程中,envvars用來描述這個進程的環境變量信息,也是一個鏈表。
    struct svcenvinfo *envvars;
    //雖然 onrestart 是一個 option ,但option 後面是 COMMAND,action 可以用來保存commnand信息
    struct action onrestart;  /* Actions to execute on restart. */

    /* keycodes for triggering this service via /dev/keychord */
    int *keycodes;
    int nkeycodes;
    int keychord_id;

    int ioprio_class;
    int ioprio_pri;

    int nargs;
    /* "MUST BE AT THE END OF THE STRUCT" */
    char *args[1];
}; /*     ^-------'args' MUST be at the end of this struct! */

瞭解到service結構體後,後面4個onrestart該如何處理?下面查看action結構體

struct action {
     /*
        一個action結構體可以存放在三個雙向鏈表中,其中alist用於存儲所有的action,
        qlist 用於連接那些等待執行的 action,tlist用於鏈接那些等待某些條件滿足後就需要執行的aciton

       */
        /* node in list of all actions */
    struct listnode alist;
        /* node in the queue of pending actions */
    struct listnode qlist;
        /* node in list of actions for a trigger */
    struct listnode tlist;

    unsigned hash;
    const char *name;
    //這個OPTION對應的是COMMAND鏈表,以zygote爲例,它有三個onrestart option.所以它會對應常見三個command結構體
    struct listnode commands;//commands結構體鏈表首指針
    struct command *current;//指向當前commnand結構體

    //    struct command
    //    {
   //      /* list of commands in an action */
    //       struct listnode clist;
   //       int (*func)(int nargs, char **args);
   //       int nargs;
   //       char *args[1];
       };



};

這樣通過上述兩個結構體對service進行了組織

2.函數parse_service和parse_line_service函數

在解析Service會用到兩個函數,分別是parse_service和parse_line+_service

static void *parse_service(struct parse_state *state, int nargs, char **args)
{
    struct service *svc;//聲明一個雙向鏈表指針
    if (nargs < 3) {
        parse_error(state, "services must have a name and a program\n");
        return 0;
    }
    if (!valid_name(args[1])) {
        parse_error(state, "invalid service name '%s'\n", args[1]);
        return 0;
    }
     //init 維護了一個全局的雙向 service鏈表,先判斷是否有同名的 service了
    svc = service_find_by_name(args[1]);
    if (svc) {   //如果有同名的 service ,直接返回。
        parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
        return 0;
    }

    nargs -= 2;  //參數個數 -2 ,即除去已經減去的前兩項
    svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);  //申請堆空間,將返回的地址賦給 svc.
    if (!svc) {
        parse_error(state, "out of memory\n");
        return 0;
    }
    svc->name = args[1];      //初始化 service結構體中name
    svc->classname = "default"; //默認情況下設置 classname 爲  default,結果很重要
    memcpy(svc->args, args + 2, sizeof(char*) * nargs);  // 拷貝 剩餘的參數到 args中
    svc->args[nargs] = 0; //參數數組中最後一個參數置零
    svc->nargs = nargs;  //
    svc->onrestart.name = "onrestart";
    list_init(&svc->onrestart.commands);//初始化 onrestart action 結構中command 雙向鏈表
    list_add_tail(&service_list, &svc->slist); //  把zygote這個 service 結構體添加到錽service鏈表尾部。
    return svc;
}

parse_service 函數只是搭建了一個service的骨架,處理了servie關鍵字所在行的內容。該section的剩餘部分由解析函數parse_line_service按行解析填充service結構體.

static void parse_line_service(struct parse_state *state, int nargs, char **args)
{
    struct service *svc = state->context;
    struct command *cmd;
    int i, kw, kw_nargs;

    if (nargs == 0) {
        return;
    }

    svc->ioprio_class = IoSchedClass_NONE;

    kw = lookup_keyword(args[0]);
    switch (kw) {
    case K_capability:
        break;
    case K_class:
        if (nargs != 2) {
            parse_error(state, "class option requires a classname\n");
        } else {
            svc->classname = args[1];
        }
        break;
    case K_console:
        svc->flags |= SVC_CONSOLE;
        break;
    case K_disabled:
        svc->flags |= SVC_DISABLED;
        svc->flags |= SVC_RC_DISABLED;
        break;
    case K_ioprio:
        if (nargs != 3) {
            parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");
        } else {
            svc->ioprio_pri = strtoul(args[2], 0, 8);

            if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {
                parse_error(state, "priority value must be range 0 - 7\n");
                break;
            }

            if (!strcmp(args[1], "rt")) {
                svc->ioprio_class = IoSchedClass_RT;
            } else if (!strcmp(args[1], "be")) {
                svc->ioprio_class = IoSchedClass_BE;
            } else if (!strcmp(args[1], "idle")) {
                svc->ioprio_class = IoSchedClass_IDLE;
            } else {
                parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");
            }
        }
        break;
    case K_group:
        if (nargs < 2) {
            parse_error(state, "group option requires a group id\n");
        } else if (nargs > NR_SVC_SUPP_GIDS + 2) {
            parse_error(state, "group option accepts at most %d supp. groups\n",
                        NR_SVC_SUPP_GIDS);
        } else {
            int n;
            svc->gid = decode_uid(args[1]);
            for (n = 2; n < nargs; n++) {
                svc->supp_gids[n-2] = decode_uid(args[n]);
            }
            svc->nr_supp_gids = n - 2;
        }
        break;
    case K_keycodes:
        if (nargs < 2) {
            parse_error(state, "keycodes option requires atleast one keycode\n");
        } else {
            svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0]));
            if (!svc->keycodes) {
                parse_error(state, "could not allocate keycodes\n");
            } else {
                svc->nkeycodes = nargs - 1;
                for (i = 1; i < nargs; i++) {
                    svc->keycodes[i - 1] = atoi(args[i]);
                }
            }
        }
        break;
    case K_oneshot:
        /*
          這是service的屬性,它一共有五個屬性,分別爲:

          SVC_DISABLED:不隨class 自動啓動。下面將會看到class的作用
          SVC_ONESHOT:退出後不需要重啓,也就是說這個service只啓動一次就可以了。
          SVC_RUNNING:正在運行,這是service的狀體
          SVC_CONSOLE:該service需要使用控制檯。
          SVC_CRITICAL:如果在規定時間內核service不斷重啓,則系統會重啓並進入恢復模式
          退出後會由init重啓;不使用控制檯;即使不斷重啓也不會導致系統進入恢復模式。
        */
        svc->flags |= SVC_ONESHOT;
        break;
    case K_onrestart:  //根據onrestart的內容,填充 action 結構體的內容
        nargs--;
        args++;
        kw = lookup_keyword(args[0]);
        if (!kw_is(kw, COMMAND)) {
            parse_error(state, "invalid command '%s'\n", args[0]);
            break;
        }
        kw_nargs = kw_nargs(kw);
        if (nargs < kw_nargs) {
            parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
                kw_nargs > 2 ? "arguments" : "argument");
            break;
        }
        //創建command結構體
        cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
        cmd->func = kw_func(kw);  // 根據關鍵字找到對應函數 ,本例中對應write
        cmd->nargs = nargs;
        memcpy(cmd->args, args, sizeof(char*) * nargs);
        //將新建的command加入到雙向鏈表中
        list_add_tail(&svc->onrestart.commands, &cmd->clist);
        break;
    case K_critical:
        svc->flags |= SVC_CRITICAL;
        break;
    case K_setenv: { /* name value */
        struct svcenvinfo *ei;
        if (nargs < 2) {
            parse_error(state, "setenv option requires name and value arguments\n");
            break;
        }
        ei = calloc(1, sizeof(*ei));
        if (!ei) {
            parse_error(state, "out of memory\n");
            break;
        }
        ei->name = args[1];
        ei->value = args[2];
        ei->next = svc->envvars;
        svc->envvars = ei;
        break;
    }
    case K_socket: {/* name type perm [ uid gid ] */
        struct socketinfo *si;
        if (nargs < 4) {
            parse_error(state, "socket option requires name, type, perm arguments\n");
            break;
        }
        if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")
                && strcmp(args[2],"seqpacket")) {
            parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n");
            break;
        }
        si = calloc(1, sizeof(*si));
        if (!si) {
            parse_error(state, "out of memory\n");
            break;
        }
        si->name = args[1];
        si->type = args[2];
        si->perm = strtoul(args[3], 0, 8);
        if (nargs > 4)
            si->uid = decode_uid(args[4]);
        if (nargs > 5)
            si->gid = decode_uid(args[5]);
        si->next = svc->sockets;
        svc->sockets = si;
        break;
    }
    case K_user:
        if (nargs != 2) {
            parse_error(state, "user option requires a user id\n");
        } else {
            svc->uid = decode_uid(args[1]);
        }
        break;
    case K_seclabel:
        if (nargs != 2) {
            parse_error(state, "seclabel option requires a label string\n");
        } else {
            svc->seclabel = args[1];
        }
        break;

    default:
        parse_error(state, "invalid option '%s'\n", args[0]);
    }
}

3. parse_action 和parse_line_action函數

parse_actionparse_line_action函數對 on section 進行了解析,以 on boot爲例進行解析

on boot
...
    class_start main

parse_action 函數

static void *parse_action(struct parse_state *state, int nargs, char **args)
{
    struct action *act;  
    if (nargs < 2) {
        parse_error(state, "actions must have a trigger\n");
        return 0;
    }
    if (nargs > 2) {
        parse_error(state, "actions may not have extra parameters\n");
        return 0;
    }
    act = calloc(1, sizeof(*act));//創建 action結構體
    act->name = args[1];  // boot action對應boot
    list_init(&act->commands);//初始化 action結構體中的command結構體雙向鏈表
    list_init(&act->qlist); // 初始化action結構體中的qlist結構體雙向鏈表
    list_add_tail(&action_list, &act->alist); //將創建的 action 結構體添加到init維護的action_list中
        /* XXX add to hash */
    return act;
}

parse_line_action函數

static void parse_line_action(struct parse_state* state, int nargs, char **args)
{
    struct command *cmd;
    struct action *act = state->context;
    int (*func)(int nargs, char **args);
    int kw, n;

    if (nargs == 0) {
        return;
    }

    kw = lookup_keyword(args[0]);
    if (!kw_is(kw, COMMAND)) {
        parse_error(state, "invalid command '%s'\n", args[0]);
        return;
    }

    n = kw_nargs(kw);
    if (nargs < n) {
        parse_error(state, "%s requires %d %s\n", args[0], n - 1,
            n > 2 ? "arguments" : "argument");
        return;
    }
    cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);//創建command命令結構體,zygote 對應boot中class_start 命令
    cmd->func = kw_func(kw); //class_start對應的處理函數是do_class_start
    cmd->nargs = nargs; 
    memcpy(cmd->args, args, sizeof(char*) * nargs); //複製do_class_start 的參數 main
    list_add_tail(&act->commands, &cmd->clist);
}

3.執行action list 隊列啓動,啓動 zygotservice

解析生成action_list 和 servie_list雙向鏈表後,會調action_for_each_trigger函數將將action_list中的action按觸發器名添加到action_queue待執行隊列中。

action_for_each_trigger("boot", action_add_queue_tail);

action_for_each_trigger函數:

void action_for_each_trigger(const char *trigger,
                             void (*func)(struct action *act))
{
    struct listnode *node;
    struct action *act;
    list_for_each(node, &action_list) {
        act = node_to_item(node, struct action, alist);
        if (!strcmp(act->name, trigger)) {
            func(act);
        }
    }
}

action_add_queue_tail函數,將action添加到queue_list中。

void action_add_queue_tail(struct action *act)
{
    if (list_empty(&act->qlist)) {
        list_add_tail(&action_queue, &act->qlist);
    }
}

接着進入最後的for循環,調用execute_one_command(void)命令執行queue_list中的action的command.

void execute_one_command(void)
{
    int ret;

    if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
        cur_action = action_remove_queue_head();
        cur_command = NULL;
        if (!cur_action)
            return;
        INFO("processing action %p (%s)\n", cur_action, cur_action->name);
        cur_command = get_first_command(cur_action);
    } else {
        cur_command = get_next_command(cur_action, cur_command);
    }

    if (!cur_command)
        return;

    ret = cur_command->func(cur_command->nargs, cur_command->args);
    INFO("command '%s' r=%d\n", cur_command->args[0], ret);
}

對於boot section 的 class_start main 命令,執行的是Builtins.c文件中的do_classs_start函數

int do_class_start(int nargs, char **args)
{
        /* Starting a class does not start services
         * which are explicitly disabled.  They must
         * be started individually.
         */
     //下面這個函數將從servie_list中找到classname爲main的service,調用service_start_if_not_disabled
     //創建該service,zygote service的class_name 恰也爲main,因此會在boot section被創建
    service_for_each_class(args[1], service_start_if_not_disabled);
    return 0;
}

zygote service的class_name 恰也爲main,因此會,在boot section被創建,調用service_start_if_not_disabled方法創建:

static void service_start_if_not_disabled(struct service *svc)
{
    if (!(svc->flags & SVC_DISABLED)) {//如果該服務的標誌位被禁止,則不執行
        service_start(svc, NULL);
    }
}

service_start函數

void service_start(struct service *svc, const char *dynamic_args)
{
    struct stat s;
    pid_t pid;
    int needs_console;
    int n;
    char *scon = NULL;
    int rc;

        /* starting a service removes it from the disabled or reset
         * state and immediately takes it out of the restarting
         * state if it was in there
         */
    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART));
    svc->time_started = 0;

        /* running processes require no additional work -- if
         * they're in the process of exiting, we've ensured
         * that they will immediately restart on exit, unless
         * they are ONESHOT
         */
    //如果該服務已經運行,則直接返回
    if (svc->flags & SVC_RUNNING) {
        return;
    }

    needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0;
    if (needs_console && (!have_console)) {
        ERROR("service '%s' requires console\n", svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }
    //service一般運行於init進程的子進程中,在子進程中運行 service 對應的具體二進制文件
    //所以啓動前應當先判斷該二進制文件是否存在,zygote 對應的二進制文件爲/system/bin/app_process
    if (stat(svc->args[0], &s) != 0) {
        ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }

    if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {
        ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",
               svc->args[0]);
        svc->flags |= SVC_DISABLED;
        return;
    }

    if (is_selinux_enabled() > 0) {
        if (svc->seclabel) {
            scon = strdup(svc->seclabel);
            if (!scon) {
                ERROR("Out of memory while starting '%s'\n", svc->name);
                return;
            }
        } else {
            char *mycon = NULL, *fcon = NULL;

            INFO("computing context for service '%s'\n", svc->args[0]);
            rc = getcon(&mycon);
            if (rc < 0) {
                ERROR("could not get context while starting '%s'\n", svc->name);
                return;
            }

            rc = getfilecon(svc->args[0], &fcon);
            if (rc < 0) {
                ERROR("could not get context while starting '%s'\n", svc->name);
                freecon(mycon);
                return;
            }

            rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
            freecon(mycon);
            freecon(fcon);
            if (rc < 0) {
                ERROR("could not get context while starting '%s'\n", svc->name);
                return;
            }
        }
    }

    NOTICE("starting '%s'\n", svc->name);

    pid = fork();  //創建子進程

    if (pid == 0) {
        struct socketinfo *si;
        struct svcenvinfo *ei;
        char tmp[32];
        int fd, sz;

        umask(077);
        if (properties_inited()) {
            //獲取屬性空間的文件描述符和大小
            get_property_workspace(&fd, &sz);
            sprintf(tmp, "%d,%d", dup(fd), sz);
            // 將屬性空間信息以 key=value形式添加到環境變量中,環境變量爲一個靜態字符串數組
            add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
        }

        for (ei = svc->envvars; ei; ei = ei->next)
            add_environment(ei->name, ei->value);//添加到環境變量中

        setsockcreatecon(scon);
        //在/dev/socket/目錄下創建socket
        for (si = svc->sockets; si; si = si->next) {
            int socket_type = (
                    !strcmp(si->type, "stream") ? SOCK_STREAM :
                        (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
             //對於zygote服務,創建目錄爲/dev/socket/zygote,zygote的文件描述符 
            int s = create_socket(si->name, socket_type,
                                  si->perm, si->uid, si->gid);
            if (s >= 0) {
                //將創建的socket的文件描述符以"ANDROID_SOCKET_socketname=fd"形式添加到環境變量中
                publish_socket(si->name, s);
            }
        }

        freecon(scon);
        scon = NULL;
        setsockcreatecon(NULL);

        if (svc->ioprio_class != IoSchedClass_NONE) {
            if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
                ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
                      getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));
            }
        }

        if (needs_console) {
            setsid();
            open_console();
        } else {
            zap_stdio();
        }

#if 0
        for (n = 0; svc->args[n]; n++) {
            INFO("args[%d] = '%s'\n", n, svc->args[n]);
        }
        for (n = 0; ENV[n]; n++) {
            INFO("env[%d] = '%s'\n", n, ENV[n]);
        }
#endif
        //設置uid,groupid
        setpgid(0, getpid());

    /* as requested, set our gid, supplemental gids, and uid */
        if (svc->gid) {
            if (setgid(svc->gid) != 0) {
                ERROR("setgid failed: %s\n", strerror(errno));
                _exit(127);
            }
        }
        if (svc->nr_supp_gids) {
            if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) {
                ERROR("setgroups failed: %s\n", strerror(errno));
                _exit(127);
            }
        }
        if (svc->uid) {
            if (setuid(svc->uid) != 0) {
                ERROR("setuid failed: %s\n", strerror(errno));
                _exit(127);
            }
        }
        if (svc->seclabel) {
            if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) {
                ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));
                _exit(127);
            }
        }

        if (!dynamic_args) {
            //執行/system/bin/app_process,進入到app_process main函數
            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
                ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
            }
        } else {
            char *arg_ptrs[INIT_PARSER_MAXARGS+1];
            int arg_idx = svc->nargs;
            char *tmp = strdup(dynamic_args);
            char *next = tmp;
            char *bword;

            /* Copy the static arguments */
            memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));

            while((bword = strsep(&next, " "))) {
                arg_ptrs[arg_idx++] = bword;
                if (arg_idx == INIT_PARSER_MAXARGS)
                    break;
            }
            arg_ptrs[arg_idx] = '\0';
            //執行/system/bin/app_process,進入到app_process main函數
            execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
        }
        _exit(127);
    }

    freecon(scon);

    if (pid < 0) {
        ERROR("failed to start '%s'\n", svc->name);
        svc->pid = 0;
        return;
    }
     //父進程init的處理,設置service 的信息,如啓動時間、進程號、服務標誌等
    svc->time_started = gettime();
    svc->pid = pid;
    svc->flags |= SVC_RUNNING;
   //如果init已經初始化property_area空間,則設置sevice的屬性爲running。
     //每個service對應一個屬性,zygote對應的屬性爲init.svc.zygote
    if (properties_inited())
        notify_service_state(svc->name, "running");
}

4.Service重啓

當 init 函數解釋完 init.rc 文本時,會調用queue_builtin_action函數創建一個signal_init action.初始化
init的信號機制.

 //該 action 會調用signal_init_action初始化init進程信號處理機制
    queue_builtin_action(signal_init_action, "signal_init");

該action最終會進入到 signal_init函數進行初始化

void signal_init(void)
{
    int s[2];

    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = sigchld_handler;
    act.sa_flags = SA_NOCLDSTOP;

    //設置信號處理函數。當子進程停止時,內核調用sigchld_handler方法處理該信號
    sigaction(SIGCHLD, &act, 0);
      //創造一對未命名的、相互連接的UNIX域套接字。當SIGCHLD到達內核時,會調用sigchld_handle函數往signal_fd Socket寫入信息
    /* create a signalling mechanism for the sigchld handler */
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {
        signal_fd = s[0];
        signal_recv_fd = s[1];
        fcntl(s[0], F_SETFD, FD_CLOEXEC);
        fcntl(s[0], F_SETFL, O_NONBLOCK);
        fcntl(s[1], F_SETFD, FD_CLOEXEC);
        fcntl(s[1], F_SETFL, O_NONBLOCK);
    }

    handle_signal();
}

一旦有進程終止,signal_handler函數會向 signal_fd 中socket發送數據,signal_recv_fd就會接收到。
這時poll函數會檢測到signal_revv_fd 文件的變化返回去執行handle_signal函數,進入到wait_for_one_process
函數中

static int wait_for_one_process(int block)
{
    pid_t pid;
    int status;
    struct service *svc;
    struct socketinfo *si;
    time_t now;
    struct listnode *node;
    struct command *cmd;

    while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR );
    if (pid <= 0) return -1;
    INFO("waitpid returned pid %d, status = %08x\n", pid, status);
    //找到死掉的那個 service進程id
    svc = service_find_by_pid(pid);
    if (!svc) {
        ERROR("untracked pid %d exited\n", pid);
        return 0;
    }

    NOTICE("process '%s', pid %d exited\n", svc->name, pid);

    if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
        //殺掉死掉進程的所創建的所有子進程。
        kill(-pid, SIGKILL);
        NOTICE("process '%s' killing any children in process group\n", svc->name);
    }
     //清除服務創建的sockets信息
    /* remove any sockets we may have created */
    for (si = svc->sockets; si; si = si->next) {
        char tmp[128];
        snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);
        unlink(tmp);
    }

    svc->pid = 0;
    svc->flags &= (~SVC_RUNNING);

        /* oneshot processes go into the disabled state on exit,
         * except when manually restarted. */
    if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {
        svc->flags |= SVC_DISABLED;
    }

        /* disabled and reset processes do not get restarted automatically */
    if (svc->flags & (SVC_DISABLED | SVC_RESET) )  {
        notify_service_state(svc->name, "stopped");
        return 0;
    }

    now = gettime();
    //如果4分鐘重啓次數超過4次,則進入到recovery模式
    if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) {
        if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
            if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
                ERROR("critical process '%s' exited %d times in %d minutes; "
                      "rebooting into recovery mode\n", svc->name,
                      CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
                android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
                return 0;
            }
        } else {
            svc->time_crashed = now;
            svc->nr_crashed = 1;
        }
    }

    svc->flags &= (~SVC_RESTART);
    svc->flags |= SVC_RESTARTING;
    //設置標誌位爲SVC_RESTARTING,然後執行該 service onrestart中的命令
    /* Execute all onrestart commands for this service. */
    list_for_each(node, &svc->onrestart.commands) {
        cmd = node_to_item(node, struct command, clist);
        cmd->func(cmd->nargs, cmd->args);
    }
    //設置init.svc.service_name的值爲 restarting
    notify_service_state(svc->name, "restarting");
    return 0;
}

在下次循環中,會在restart_processes()方法中重啓標誌位SVC_RESTARTING的服務
重要參考:
1.Android的init過程詳解(一):
http://www.cnblogs.com/nokiaguy/archive/2013/04/14/3020774.html
2.《深入理解Android虛擬機》
3.《深入理解android 卷1》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章