Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android6.0 init 深入分析

Android6.0 init 深入分析

編輯:關於Android編程

之前寫過一篇關於android5.0 init的介紹,這篇博客是介紹android6.0init,之前有的代碼介紹不詳細。而且分析 解析init.rc那塊代碼也沒有結合init.rc介紹。

一、 main函數的一些准備工作

下面我們分析下源碼:

int main(int argc, char** argv) {
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }

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

由於ueventd watchdogd是公用代碼,所以啟動的時候根據文件名來判斷是哪個進程,繼續分析:

    // Clear the umask.
    umask(0);

    add_environment("PATH", _PATH_DEFPATH);//添加環境變量

    bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 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.
    if (is_first_stage) {
        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);
    }

這塊代碼主要添加環境變量,以及掛載各種文件系統。

    open_devnull_stdio();
    klog_init();
    klog_set_level(KLOG_NOTICE_LEVEL);//log的初始化

    NOTICE("init%s started!\n", is_first_stage ? "" : " second stage");

    if (!is_first_stage) {
        // Indicate that booting is in progress to background fw loaders, etc.
        close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));//啟動的時候創建一個.booting文件

        property_init();//屬性初始化

        // If arguments are passed both on the command line and in DT,
        // properties set in DT always have priority over the command-line ones.
        process_kernel_dt();
        process_kernel_cmdline();

        // Propogate the kernel variables to internal variables
        // used by init as well as the current required properties.
        export_kernel_boot_props();//設置一些屬性
    }

我們看上面函數先是open_devnull_stdio函數,這個函數就是把標准輸入,輸出,錯誤輸出重定義到空設備上。然後創建一個.booting文件代表系統在啟動,做了一些屬性的初始化,以及一些boot相關的系統屬性設置獲取等。我們先看下open_devnull_stdio代碼:

 

void open_devnull_stdio(void)
{
    // Try to avoid the mknod() call if we can. Since SELinux makes
    // a /dev/null replacement available for free, let's use it.
    int fd = open("/sys/fs/selinux/null", O_RDWR);
    if (fd == -1) {
        // OOPS, /sys/fs/selinux/null isn't available, likely because
        // /sys/fs/selinux isn't mounted. Fall back to mknod.
        static const char *name = "/dev/__null__";
        if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
            fd = open(name, O_RDWR);
            unlink(name);
        }
        if (fd == -1) {
            exit(1);
        }
    }

    dup2(fd, 0);
    dup2(fd, 1);
    dup2(fd, 2);
    if (fd > 2) {
        close(fd);
    }
}
property_init()函數主要是屬性的初始化,這個我們在之前分析屬性系統的那篇博客分析過了。
我們再來看process_kernel_dt函數

 

static void process_kernel_dt(void)
{
    static const char android_dir[] = "/proc/device-tree/firmware/android";

    std::string file_name = android::base::StringPrintf("%s/compatible", android_dir);

    std::string dt_file;
    android::base::ReadFileToString(file_name, &dt_file);
    if (!dt_file.compare("android,firmware")) {//compatible文件內容是否是android,firmware
        ERROR("firmware/android is not compatible with 'android,firmware'\n");
        return;
    }

    std::unique_ptrdir(opendir(android_dir), closedir);
    if (!dir)
        return;

    struct dirent *dp;
    while ((dp = readdir(dir.get())) != NULL) {//讀取目錄的每個文件
        if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible"))
            continue;

        file_name = android::base::StringPrintf("%s/%s", android_dir, dp->d_name);

        android::base::ReadFileToString(file_name, &dt_file);
        std::replace(dt_file.begin(), dt_file.end(), ',', '.');

        std::string property_name = android::base::StringPrintf("ro.boot.%s", dp->d_name);//每個文件名作為屬性名,裡面的內容作為屬性值
        property_set(property_name.c_str(), dt_file.c_str());
    }
}

上面這個函數主要是在/proc/device-tree/firmware/android 這個目錄下,先看compatible文件內容是否是android,firmware。然後這個目錄下每個文件名作為屬性,文件裡面的內容作為屬性值。這裡話就是ro.boot.hareware ro.boot.name這兩個屬性值。

 

root@lte26007:/proc/device-tree/firmware/android # ls
compatible
hardware
name
繼續看process_kernel_cmdline函數

 

static void process_kernel_cmdline(void)
{
    /* don't expose the raw commandline to nonpriv processes */
    chmod("/proc/cmdline", 0440);

    /* first pass does the common stuff, and finds if we are in qemu.
     * second pass is only necessary for qemu to export all kernel params
     * as props.
     */
    import_kernel_cmdline(false, import_kernel_nv);
    if (qemu[0])
        import_kernel_cmdline(true, import_kernel_nv);
}

import_kernel_cmdline函數就是讀取proc/cmdline中的內容,然後調用import_kernel_nv函數設置系統屬性

void import_kernel_cmdline(bool in_qemu, std::function import_kernel_nv)
{
    char cmdline[2048];
    char *ptr;
    int fd;

    fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC);
    if (fd >= 0) {
        int n = read(fd, cmdline, sizeof(cmdline) - 1);
        if (n < 0) n = 0;

        /* get rid of trailing newline, it happens */
        if (n > 0 && cmdline[n-1] == '\n') n--;

        cmdline[n] = 0;
        close(fd);
    } else {
        cmdline[0] = 0;
    }

    ptr = cmdline;
    while (ptr && *ptr) {
        char *x = strchr(ptr, ' ');
        if (x != 0) *x++ = 0;
        import_kernel_nv(ptr, in_qemu);
        ptr = x;
    }
}

在import_kernel_nv函數中設置系統屬性,但是一定要有androidboot這樣的關鍵字眼才會設置ro.boot這樣的屬性。這塊在我們的設備cmdline中沒有這樣的字眼,也就不會設置這些屬性。

static void import_kernel_nv(char *name, bool for_emulator)
{
    char *value = strchr(name, '=');
    int name_len = strlen(name);

    if (value == 0) return;
    *value++ = 0;
    if (name_len == 0) return;

    if (for_emulator) {
        /* in the emulator, export any kernel option with the
         * ro.kernel. prefix */
        char buff[PROP_NAME_MAX];
        int len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );

        if (len < (int)sizeof(buff))
            property_set( buff, value );
        return;
    }

    if (!strcmp(name,"qemu")) {
        strlcpy(qemu, value, sizeof(qemu));
    } else if (!strncmp(name, "androidboot.", 12) && name_len > 12) {
        const char *boot_prop_name = name + 12;
        char prop[PROP_NAME_MAX];
        int cnt;

        cnt = snprintf(prop, sizeof(prop), "ro.boot.%s", boot_prop_name);
        if (cnt < PROP_NAME_MAX)
            property_set(prop, value);
    }
}

再來看export_kernel_boot_props這個函數,它也就是設置一些屬性,設置ro屬性根據之前ro.boot這類的屬性值,如果沒有設置成unknown,像之前我們有ro.boot.hardware, 那我們就可以設置root.hardware這樣的屬性。

static void export_kernel_boot_props() {
    struct {
        const char *src_prop;
        const char *dst_prop;
        const char *default_value;
    } prop_map[] = {
        //{ "ro.boot.serialno",   "ro.serialno",   "", },
        { "ro.boot.mode",       "ro.bootmode",   "unknown", },
        { "ro.boot.baseband",   "ro.baseband",   "unknown", },
        { "ro.boot.bootloader", "ro.bootloader", "unknown", },
        { "ro.boot.hardware",   "ro.hardware",   "unknown", },
        { "ro.boot.revision",   "ro.revision",   "0", },
    };
    for (size_t i = 0; i < ARRAY_SIZE(prop_map); i++) {
        char value[PROP_VALUE_MAX];
        int rc = property_get(prop_map[i].src_prop, value);
        property_set(prop_map[i].dst_prop, (rc > 0) ? value : prop_map[i].default_value);
    }
}

下面這塊都是selinux相關的,我們就不分析了。

    // Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
    selinux_initialize(is_first_stage);

    // If we're in the kernel domain, re-exec init to transition to the init domain now
    // that the SELinux policy has been loaded.
    if (is_first_stage) {
        if (restorecon("/init") == -1) {
            ERROR("restorecon failed: %s\n", strerror(errno));
            security_failure();
        }
        char* path = argv[0];
        char* args[] = { path, const_cast("--second-stage"), nullptr };
        if (execv(path, args) == -1) {
            ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
            security_failure();
        }
    }

    // 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.
    INFO("Running restorecon...\n");
    restorecon("/dev");
    restorecon("/dev/socket");
    restorecon("/dev/__properties__");
    restorecon_recursive("/sys");

然後創建了一個epoll的fd

    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    if (epoll_fd == -1) {
        ERROR("epoll_create1 failed: %s\n", strerror(errno));
        exit(1);
    }

繼續分析,signal_handler_init函數主要是當子進程被kill之後,會在父進程接受一個信號。處理這個信號的時候往sockpair一端寫數據,而另一端的fd是加入的epoll中。這塊我們後面會專門其一節講解。而property_load_boot_defaults就是解析根目錄的default.prop中的屬性,然後設置到屬性中去。start_prperty_service就是把接受屬性的socket的fd加入epoll中,也定義了處理函數,屬性之前博客專門分析過了。

    signal_handler_init();

    property_load_boot_defaults();
    start_property_service();

看看signal_handler_init函數就是處理子進程kill時的情況。

static void SIGCHLD_handler(int) {
    if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
        ERROR("write(signal_write_fd) failed: %s\n", strerror(errno));
    }
}

void signal_handler_init() {
    // Create a signalling mechanism for SIGCHLD.
    int s[2];
    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
        ERROR("socketpair failed: %s\n", strerror(errno));
        exit(1);
    }

    signal_write_fd = s[0];
    signal_read_fd = s[1];

    // Write to signal_write_fd if we catch SIGCHLD.
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = SIGCHLD_handler;
    act.sa_flags = SA_NOCLDSTOP;
    sigaction(SIGCHLD, &act, 0);

    reap_any_outstanding_children();

    register_epoll_handler(signal_read_fd, handle_signal);
}


二、解析init.rc

下面我們開始分析解析init.rc並且結合init.rc一起看

init.rc的語言我們可以看這篇博客,主要是init.rc主要有Actions和Service兩種,具體看這篇博客http://blog.csdn.net/kc58236582/article/details/52042331。

我們通過init_parse_config_file函數來解析init.rc,先把文件數據讀取到data中,然後調用parse_config來解析數據。

int init_parse_config_file(const char* path) {
    INFO("Parsing %s...\n", path);
    Timer t;
    std::string data;
    if (!read_file(path, &data)) {
        return -1;
    }

    data.push_back('\n'); // TODO: fix parse_config.
    parse_config(path, data);
    dump_parser_state();

    NOTICE("(Parsing %s took %.2fs.)\n", path, t.duration());
    return 0;
}

我們先來看看dump_parser_state函數,當解析完之後我們可以在這個函數中打印所有的service和action。

void dump_parser_state() {
    if (false) {
        struct listnode* node;
        list_for_each(node, &service_list) {
            service* svc = node_to_item(node, struct service, slist);
            INFO("service %s\n", svc->name);
            INFO("  class '%s'\n", svc->classname);
            INFO("  exec");
            for (int n = 0; n < svc->nargs; n++) {
                INFO(" '%s'", svc->args[n]);
            }
            INFO("\n");
            for (socketinfo* si = svc->sockets; si; si = si->next) {
                INFO("  socket %s %s 0%o\n", si->name, si->type, si->perm);
            }
        }

        list_for_each(node, &action_list) {
            action* act = node_to_item(node, struct action, alist);
            INFO("on ");
            char name_str[256] = "";
            build_triggers_string(name_str, sizeof(name_str), act);
            INFO("%s", name_str);
            INFO("\n");

            struct listnode* node2;
            list_for_each(node2, &act->commands) {
                command* cmd = node_to_item(node2, struct command, clist);
                INFO("  %p", cmd->func);
                for (int n = 0; n < cmd->nargs; n++) {
                    INFO(" %s", cmd->args[n]);
                }
                INFO("\n");
            }
            INFO("\n");
        }
    }
}

好回到正題看parse_config函數,來解析從init.rc文件中獲取的數據。

static void parse_config(const char *fn, const std::string& data)
{
    struct listnode import_list;
    struct listnode *node;
    char *args[INIT_PARSER_MAXARGS];

    int nargs = 0;

    parse_state state;
    state.filename = fn;
    state.line = 0;
    state.ptr = strdup(data.c_str());  // TODO: fix this code!
    state.nexttoken = 0;
    state.parse_line = parse_line_no_op;//這裡的函數是空實現

    list_init(&import_list);
    state.priv = &import_list;

    for (;;) {
        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)) {
                    state.parse_line(&state, 0, 0);
                    parse_new_section(&state, kw, nargs, args);
                } else {
                    state.parse_line(&state, nargs, args);
                }
                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;

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

我們先來看看next_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;//賦值state->text
textresume:
    for (;;) {
        switch (*x) {
        case 0:
            goto textdone;
        case ' ':
        case '\t':
        case '\r'://碰到空什麼的,直接返回T_TEXT
            x++;
            goto textdone;
        case '\n':
            state->nexttoken = T_NEWLINE;//碰到回車換行直接nexttoken是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':
                    /* \   -> line continuation */
                if (x[1] != '\n') {
                    x++;
                    continue;
                }
            case '\n':
                    /* \  -> 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;
}

看這個函數的代碼,我們只需要知道。當我們普通的進來,沒有碰到換行,只有碰到空格的話,返回T_TEXT,並且nextoken為null。

我們再來看T_TEXT的時候只是在數組裡面保存了state.text的內容,然後繼續下一次。當我們直到碰到/n,回車換行。這個時候返回T_TEXT,但是nexttoken是T_NEWLINE

這樣下次,就直接返回T_NEWLINE了,當返回T_NEWLINE直接調用lookup_keyword函數。

    for (;;) {
        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)) {
                    state.parse_line(&state, 0, 0);
                    parse_new_section(&state, kw, nargs, args);
                } else {
                    state.parse_line(&state, nargs, args);
                }
                nargs = 0;
            }
            break;
        case T_TEXT:
            if (nargs < INIT_PARSER_MAXARGS) {
                args[nargs++] = state.text;
            }
            break;
        }
    }

lookup_keyword函數就是看第一個單詞返回一個K_**的值而已。

static int lookup_keyword(const char *s)
{
    switch (*s++) {
    case 'b':
        if (!strcmp(s, "ootchart_init")) return K_bootchart_init;
        break;
    case 'c':
        if (!strcmp(s, "opy")) return K_copy;
        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, "nable")) return K_enable;
        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;
        if (!strcmp(s, "nstallkey")) return K_installkey;
        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;
        if (!strcmp(s, "oad_system_props")) return K_load_system_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;

再來看這個宏

#define kw_is(kw, type) (keyword_info[kw].flags & (type))

來看看它的定義,首先先說下宏定義##代表後面是連接起來的,#代表就是後面這個變量

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

static struct {
    const char *name;
    int (*func)(int nargs, char **args);
    unsigned char nargs;
    unsigned char flags;
} keyword_info[KEYWORD_COUNT] = {
    [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
#include "keywords.h"
};

這樣我們再來看下keywords.h這個頭文件,這裡就比較明白是它是解析各個關鍵詞是屬於SECTION,COMMAND,OPTION的

#ifndef KEYWORD//因為前面定義了KEYWORD
int do_bootchart_init(int nargs, char **args);
......
#endif
    KEYWORD(bootchart_init,        COMMAND, 0, do_bootchart_init)
    KEYWORD(chmod,       COMMAND, 2, do_chmod)
    KEYWORD(chown,       COMMAND, 2, do_chown)
    KEYWORD(class,       OPTION,  0, 0)
......
    KEYWORD(import,      SECTION, 1, 0)
.....
.....
    KEYWORD(service,     SECTION, 0, 0)
    KEYWORD(writepid,    OPTION,  0, 0)
#ifdef __MAKE_KEYWORD_ENUM__
    KEYWORD_COUNT,
};
#undef __MAKE_KEYWORD_ENUM__
#undef KEYWORD
#endif

這樣我們就可以通過kw_is(kw, SECTION)來判斷是否屬於SECTION

我們來看下函數,如果是SECTION,剛開始調用state.parse_line也是空實現

                if (kw_is(kw, SECTION)) {
                    state.parse_line(&state, 0, 0);
                    parse_new_section(&state, kw, nargs, args);
                } else {
                    state.parse_line(&state, nargs, args);
                }

再來看看parse_new_section函數

static 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://如果是service
        state->context = parse_service(state, nargs, args);
        if (state->context) {
            state->parse_line = parse_line_service;
            return;
        }
        break;
    case K_on://是on
        state->context = parse_action(state, nargs, args);
        if (state->context) {
            state->parse_line = parse_line_action;
            return;
        }
        break;
    case K_import://是import
        parse_import(state, nargs, args);
        break;
    }
    state->parse_line = parse_line_no_op;
}

 

2.1 解析service

我們先來看下如果是service,先調用parse_service函數
 

static void *parse_service(struct parse_state *state, int nargs, char **args)
{
    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;
    }

    service* svc = (service*) service_find_by_name(args[1]);//找service
    if (svc) {//如果找到該service,說明重復了
        parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
        return 0;
    }

    nargs -= 2;
    svc = (service*) calloc(1, sizeof(*svc) + sizeof(char*) * nargs);//new一個service
    if (!svc) {
        parse_error(state, "out of memory\n");
        return 0;
    }
    svc->name = strdup(args[1]);//各種初始化
    svc->classname = "default";
    memcpy(svc->args, args + 2, sizeof(char*) * nargs);
    trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
    svc->args[nargs] = 0;
    svc->nargs = nargs;
    list_init(&svc->onrestart.triggers);
    cur_trigger->name = "onrestart";
    list_add_tail(&svc->onrestart.triggers, &cur_trigger->nlist);
    list_init(&svc->onrestart.commands);
    list_add_tail(&service_list, &svc->slist);//把service放進service_list
    return svc;
}

state->parse_line賦值了parse_line_service函數了。然後我們再出這個函數看看,當你再來一行新的,這個時候不是SECTION,就要調用parse_line_service函數來解析了。

        case T_NEWLINE:
            state.line++;
            if (nargs) {
                int kw = lookup_keyword(args[0]);
                if (kw_is(kw, SECTION)) {
                    state.parse_line(&state, 0, 0);
                    parse_new_section(&state, kw, nargs, args);
                } else {
                    state.parse_line(&state, nargs, args);
                }
                nargs = 0;
            }
            break;

我們來看下parse_line_service函數:下面就是解析各種參數,然後填充service變量而已。

static void parse_line_service(struct parse_state *state, int nargs, char **args)
{
    struct service *svc = (service*) 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_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:

 

2.2 解析on關鍵字

下面我們來看下解析on關鍵字的

 

    case K_on:
        state->context = parse_action(state, nargs, args);
        if (state->context) {
            state->parse_line = parse_line_action;
            return;
        }
        break;
先看下parse_action函數

 

static void *parse_action(struct parse_state *state, int nargs, char **args)
{
    struct trigger *cur_trigger;
    int i;
    if (nargs < 2) {
        parse_error(state, "actions must have a trigger\n");
        return 0;
    }

    action* act = (action*) calloc(1, sizeof(*act));//新建aciton
    list_init(&act->triggers);

    for (i = 1; i < nargs; i++) {
        if (!(i % 2)) {
            if (strcmp(args[i], "&&")) {//有的觸發器有幾個條件,比如可以兩個屬性同事滿足
                struct listnode *node;
                struct listnode *node2;
                parse_error(state, "& is the only symbol allowed to concatenate actions\n");
                list_for_each_safe(node, node2, &act->triggers) {
                    struct trigger *trigger = node_to_item(node, struct trigger, nlist);
                    free(trigger);
                }
                free(act);
                return 0;
            } else
                continue;
        }
        cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
        cur_trigger->name = args[i];
        list_add_tail(&act->triggers, &cur_trigger->nlist);
    }

    list_init(&act->commands);
    list_init(&act->qlist);
    list_add_tail(&action_list, &act->alist);//把aciton加入action_list中
        /* XXX add to hash */
    return act;
}

這裡新建一個action,然後加入action_list中。主要觸發器可以有幾個條件。比如滿足兩個屬性要求,然後保存在action的的triggers中。

同樣我們再來看看parse_line_action函數,這個函數就是各種命令了。

static void parse_line_action(struct parse_state* state, int nargs, char **args)
{
    struct action *act = (action*) state->context;
    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;
    }
    command* cmd = (command*) malloc(sizeof(*cmd) + sizeof(char*) * nargs);
    cmd->func = kw_func(kw);
    cmd->line = state->line;
    cmd->filename = state->filename;
    cmd->nargs = nargs;
    memcpy(cmd->args, args, sizeof(char*) * nargs);
    list_add_tail(&act->commands, &cmd->clist);// 加入到act->commands
}

這裡注意是kw_func宏,就是和之前那個宏一樣,這裡是選擇每個命令的處理函數。

 

2.3 處理import

處理import我們來看下parse_import函數,這個函數很簡單就把import的文件名保存在import_list中。

static void parse_import(struct parse_state *state, int nargs, char **args)
{
    struct listnode *import_list = (listnode*) state->priv;
    char conf_file[PATH_MAX];
    int ret;

    if (nargs != 2) {
        ERROR("single argument needed for import\n");
        return;
    }

    ret = expand_props(conf_file, args[1], sizeof(conf_file));
    if (ret) {
        ERROR("error while handling import on line '%d' in '%s'\n",
              state->line, state->filename);
        return;
    }

    struct import* import = (struct import*) calloc(1, sizeof(struct import));
    import->filename = strdup(conf_file);
    list_add_tail(import_list, &import->list);
    INFO("Added '%s' to import list\n", import->filename);
}

最後我們來看下當所有init.rc中的關鍵字解析完之後,就會遍歷import_list,然後調用init_parse_config_file函數再來解析該文件。

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

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

所以一般在init.rc中import的文件,放入action service列表中,會比直接在init.rc中的service和aciton靠後。


三、加入執行隊列

在解析init.rc文件後,這節將介紹把Action加入執行隊列中。

    action_for_each_trigger("early-init", action_add_queue_tail);

    // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    // ... so that we can start queuing up actions that require stuff from /dev.
    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");

    // Trigger all the boot actions to get us started.
    action_for_each_trigger("init", 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");

    // Don't mount filesystems or start core system services in charger mode.
    char bootmode[PROP_VALUE_MAX];
    if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) {
        action_for_each_trigger("charger", action_add_queue_tail);
    } else {
        action_for_each_trigger("late-init", 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");

我們先來看action_for_each_trigger函數

void action_for_each_trigger(const char *trigger,
                             void (*func)(struct action *act))
{
    struct listnode *node, *node2;
    struct action *act;
    struct trigger *cur_trigger;

    list_for_each(node, &action_list) {//遍歷每個action
        act = node_to_item(node, struct action, alist);
        list_for_each(node2, &act->triggers) {//遍歷每個action的triggers
            cur_trigger = node_to_item(node2, struct trigger, nlist);
            if (!strcmp(cur_trigger->name, trigger)) {//是否與傳入的trigger名字匹配
                func(act);//調用回調函數
            }
        }
    }
}

我們再來看下傳入的回調函數action_add_queue_tail,這個函數就是把aciton加入執行列表中。

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

1. 這樣的話像第一句,就是在所有的aciton中是否有early-init這樣的trigger,有的話加入執行列表。

    action_for_each_trigger("early-init", action_add_queue_tail);

我們看下init.rc中early-init中的內容,設置了init進程的adj,開啟ueventd進程等。

on early-init
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_score_adj -1000

    # Set the security context of /adb_keys if present.
    restorecon /adb_keys

    start ueventd

    #add for amt
    mkdir /amt 0775 root system

下面我們再來看下queue_builtin_action函數,這個函數的話就是直接創建一個action,然後新建command,關鍵是func會調函數設置好。最後把action加入執行隊列中。

void queue_builtin_action(int (*func)(int nargs, char **args), const char *name)
{
    action* act = (action*) calloc(1, sizeof(*act));
    trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
    cur_trigger->name = name;
    list_init(&act->triggers);
    list_add_tail(&act->triggers, &cur_trigger->nlist);
    list_init(&act->commands);
    list_init(&act->qlist);

    command* cmd = (command*) calloc(1, sizeof(*cmd));
    cmd->func = func;
    cmd->args[0] = const_cast(name);
    cmd->nargs = 1;
    list_add_tail(&act->commands, &cmd->clist);

    list_add_tail(&action_list, &act->alist);
    action_add_queue_tail(act);
}

2. 因此這裡我們看下wait_for_coldboot_done_action函數,這函數就是等待/dev/.coldboot_done文件

static int wait_for_coldboot_done_action(int nargs, char **args) {
    Timer t;

    NOTICE("Waiting for %s...\n", COLDBOOT_DONE);
    // Any longer than 1s is an unreasonable length of time to delay booting.
    // If you're hitting this timeout, check that you didn't make your
    // sepolicy regular expressions too expensive (http://b/19899875).
    if (wait_for_file(COLDBOOT_DONE, 1)) {
        ERROR("Timed out waiting for %s\n", COLDBOOT_DONE);
    }

    NOTICE("Waiting for %s took %.2fs.\n", COLDBOOT_DONE, t.duration());
  

wait_for_file等待/dev/.coldboot_done文件,超時時間設置的是1秒。

int wait_for_file(const char *filename, int timeout)
{
    struct stat info;
    uint64_t timeout_time_ns = gettime_ns() + timeout * UINT64_C(1000000000);
    int ret = -1;

    while (gettime_ns() < timeout_time_ns && ((ret = stat(filename, &info)) < 0))
        usleep(10000);

    return ret;
}

3. mix_hwrng_into_linux_rng_action函數從硬件PNG的設備文件/dev/hw_random讀取512字節並寫到LinuxRNG設備文件dev/urandom中。

4. keychord_init_action初始化組合鍵監聽模塊,這個函數調用了keychord_init函數

 

static int keychord_init_action(int nargs, char **args)
{
    keychord_init();
    return 0;
}
void keychord_init() {
    service_for_each(add_service_keycodes);

    // Nothing to do if no services require keychords.
    if (!keychords) {
        return;
    }

    keychord_fd = TEMP_FAILURE_RETRY(open("/dev/keychord", O_RDWR | O_CLOEXEC));
    if (keychord_fd == -1) {
        ERROR("could not open /dev/keychord: %s\n", strerror(errno));
        return;
    }

    int ret = write(keychord_fd, keychords, keychords_length);
    if (ret != keychords_length) {
        ERROR("could not configure /dev/keychord %d: %s\n", ret, strerror(errno));
        close(keychord_fd);
    }

    free(keychords);
    keychords = nullptr;

    register_epoll_handler(keychord_fd, handle_keychord);
}
keychord_init函數先是遍歷各個service,然後調用add_service_keycodes函數,在add_service_keycodes函數中,主要看service有沒有keycodes這個變量,有的話將新建一個keychord,然後將service的keycodes保存在這個變量中。最後還有一個全局的keychords,所以的數據最後都是可以通過這個全局指針找到。

 

void add_service_keycodes(struct service *svc)
{
    struct input_keychord *keychord;
    int i, size;

    if (svc->keycodes) {
        /* add a new keychord to the list */
        size = sizeof(*keychord) + svc->nkeycodes * sizeof(keychord->keycodes[0]);
        keychords = (input_keychord*) realloc(keychords, keychords_length + size);
        if (!keychords) {
            ERROR("could not allocate keychords\n");
            keychords_length = 0;
            keychords_count = 0;
            return;
        }

        keychord = (struct input_keychord *)((char *)keychords + keychords_length);
        keychord->version = KEYCHORD_VERSION;
        keychord->id = keychords_count + 1;
        keychord->count = svc->nkeycodes;
        svc->keychord_id = keychord->id;

        for (i = 0; i < svc->nkeycodes; i++) {
            keychord->keycodes[i] = svc->keycodes[i];
        }
        keychords_count++;
        keychords_length += size;
    }
}

然後我們把keychords這個全局變量數據寫入/dev/keychord文件中,最後調用register_epoll_handler函數把這個fd注冊到epoll中。

    int ret = write(keychord_fd, keychords, keychords_length);
    if (ret != keychords_length) {
        ERROR("could not configure /dev/keychord %d: %s\n", ret, strerror(errno));
        close(keychord_fd);
    }

    free(keychords);
    keychords = nullptr;

    register_epoll_handler(keychord_fd, handle_keychord);

最後在這個fd有數據來的時候,我們讀取出來,通過service_find_by_keychord看與哪個service的的keychord匹配,匹配的話就把service啟動。但是前提是and_enabled是running。

static void handle_keychord() {
    struct service *svc;
    char adb_enabled[PROP_VALUE_MAX];
    int ret;
    __u16 id;

    // Only handle keychords if adb is enabled.
    property_get("init.svc.adbd", adb_enabled);
    ret = read(keychord_fd, &id, sizeof(id));
    if (ret != sizeof(id)) {
        ERROR("could not read keychord id\n");
        return;
    }

    if (!strcmp(adb_enabled, "running")) {
        svc = service_find_by_keychord(id);
        if (svc) {
            INFO("Starting service %s from keychord\n", svc->name);
            service_start(svc, NULL);
        } else {
            ERROR("service for keychord %d not found\n", id);
        }
    }
}

5. console_init_action是顯示A N D R O I D 字樣的logo。

static int console_init_action(int nargs, char **args)
{
    char console[PROP_VALUE_MAX];
    if (property_get("ro.boot.console", console) > 0) {
        snprintf(console_name, sizeof(console_name), "/dev/%s", console);
    }

    int fd = open(console_name, O_RDWR | O_CLOEXEC);
    if (fd >= 0)
        have_console = 1;//是否有控制台
    close(fd);

    fd = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
    if (fd >= 0) {
        const char *msg;
            msg = "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"  // console is 40 cols x 30 lines
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "             A N D R O I D ";
        write(fd, msg, strlen(msg));//顯示android字樣
        close(fd);
    }

    return 0;
}

6. action_for_each_trigger("init", action_add_queue_tail); 觸發init觸發器, 主要是mount一些設備,還有創建一些目錄。

on init
    sysclktz 0

    # Backward compatibility.
    symlink /system/etc /etc
    symlink /sys/kernel/debug /d

    # Link /vendor to /system/vendor for devices without a vendor partition.
    symlink /system/vendor /vendor

    # Create cgroup mount point for cpu accounting
    mkdir /acct
    mount cgroup none /acct cpuacct
    mkdir /acct/uid

    # Create cgroup mount point for memory
    mount tmpfs none /sys/fs/cgroup mode=0750,uid=0,gid=1000
......

7. mix_hwrng_into_linux_rng_action也是和RNG相關
8. charger和late-init,根據ro.bootmode來觸發charger還是late-init觸發器

    char bootmode[PROP_VALUE_MAX];
    if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) {
        action_for_each_trigger("charger", action_add_queue_tail);
    } else {
        action_for_each_trigger("late-init", action_add_queue_tail);
    }

late-init內容如下:

on late-init
    trigger early-fs
    trigger fs
    trigger post-fs

    # Load properties from /system/ + /factory after fs mount. Place
    # this in another action so that the load will be scheduled after the prior
    # issued fs triggers have completed.
    trigger load_system_props_action//加載系統屬性

    # Now we can mount /data. File encryption requires keymaster to decrypt
    # /data, which in turn can only be loaded when system properties are present
    trigger post-fs-data
    trigger load_persist_props_action//加載persist屬性

    # Remove a file to wake up anything waiting for firmware.
    trigger firmware_mounts_complete

    trigger early-boot
    trigger boot//這裡面啟動main core服務

而on charger就會啟動一個charger進程

 

on charger
    class_start charger

 

service charger /charger
    seclabel u:r:healthd:s0
    oneshot

9. queue_property_triggers_action就是看現在那些aciton滿足條件,把它加入執行列中。

static int queue_property_triggers_action(int nargs, char **args)
{
    queue_all_property_triggers();
    /* enable property triggers */
    property_triggers_enabled = 1;
    return 0;
}

 

void queue_all_property_triggers()
{
    queue_property_triggers(NULL, NULL);
}

最後調用queue_property_triggers,遍歷所有的aciton是屬性的那種,只要滿足條件加入執行隊列。

void queue_property_triggers(const char *name, const char *value)
{
    struct listnode *node, *node2;
    struct action *act;
    struct trigger *cur_trigger;
    bool match;
    int name_length;

    list_for_each(node, &action_list) {
        act = node_to_item(node, struct action, alist);
        match = !name;
        list_for_each(node2, &act->triggers) {
            cur_trigger = node_to_item(node2, struct trigger, nlist);
            if (!strncmp(cur_trigger->name, "property:", strlen("property:"))) {
                const char *test = cur_trigger->name + strlen("property:");
                if (!match) {
                    name_length = strlen(name);
                    if (!strncmp(name, test, name_length) &&
                        test[name_length] == '=' &&
                        (!strcmp(test + name_length + 1, value) ||
                        !strcmp(test + name_length + 1, "*"))) {
                        match = true;
                        continue;
                    }
                }
                const char* equals = strchr(test, '=');
                if (equals) {
                    char prop_name[PROP_NAME_MAX + 1];
                    char value[PROP_VALUE_MAX];
                    int length = equals - test;
                    if (length <= PROP_NAME_MAX) {
                        int ret;
                        memcpy(prop_name, test, length);
                        prop_name[length] = 0;

                        /* does the property exist, and match the trigger value? */
                        ret = property_get(prop_name, value);
                        if (ret > 0 && (!strcmp(equals + 1, value) ||
                                        !strcmp(equals + 1, "*"))) {
                            continue;
                        }
                    }
                }
            }
            match = false;
            break;
        }
        if (match) {
            action_add_queue_tail(act);
        }
    }
}


四、屬性系統

屬性會在start_property_service函數中,把屬性的socket 的fd加入到了epoll中,init主要是檢測屬性發生改變時,有哪些action滿足條件需要觸發。以及一些persist屬性保存。ctl屬性開啟 關閉service等。

具體的我們在之前的博客http://blog.csdn.net/kc58236582/article/details/51939322,已經分析的比較詳細了,這裡就不說了。
 


五、執行執行隊列中的Action

執行命令主要是在main函數中的while循環中調用execute_one_command,因為執行隊列會不斷變化,所以需要在while循環中不斷調用這個函數。

    while (true) {
        if (!waiting_for_exec) {
            execute_one_command();
            restart_processes();
        }

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

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

        bootchart_sample(&timeout);

        epoll_event ev;
        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
        if (nr == -1) {
            ERROR("epoll_wait failed: %s\n", strerror(errno));
        } else if (nr == 1) {
            ((void (*)()) ev.data.ptr)();
        }
    }

我們來看下這個函數,比較簡單先調用action_remove_queue_head函數,然後獲取command,最後調用command的func回調函數。

void execute_one_command() {
    Timer t;

    char cmd_str[256] = "";
    char name_str[256] = "";

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

        build_triggers_string(name_str, sizeof(name_str), cur_action);

        INFO("processing action %p (%s)\n", cur_action, name_str);
        cur_command = get_first_command(cur_action);
    } else {
        cur_command = get_next_command(cur_action, cur_command);
    }

    if (!cur_command) {
        return;
    }

    int result = cur_command->func(cur_command->nargs, cur_command->args);
    if (klog_get_level() >= KLOG_INFO_LEVEL) {
        for (int i = 0; i < cur_command->nargs; i++) {
            strlcat(cmd_str, cur_command->args[i], sizeof(cmd_str));
            if (i < cur_command->nargs - 1) {
                strlcat(cmd_str, " ", sizeof(cmd_str));
            }
        }
        char source[256];
        if (cur_command->filename) {
            snprintf(source, sizeof(source), " (%s:%d)", cur_command->filename, cur_command->line);
        } else {
            *source = '\0';
        }
        INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
             cmd_str, cur_action ? name_str : "", source, result, t.duration());
    }
}


 

六、kill 進程處理以及再次開啟service進程

之前我們在分析signal_handler_init函數的時候沒有詳細說,現在說下這個函數。

 

void signal_handler_init() {
    // Create a signalling mechanism for SIGCHLD.
    int s[2];
    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
        ERROR("socketpair failed: %s\n", strerror(errno));
        exit(1);
    }

    signal_write_fd = s[0];
    signal_read_fd = s[1];

    // Write to signal_write_fd if we catch SIGCHLD.
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = SIGCHLD_handler;
    act.sa_flags = SA_NOCLDSTOP;
    sigaction(SIGCHLD, &act, 0);//子進程終結發給父進程的信號

    reap_any_outstanding_children();

    register_epoll_handler(signal_read_fd, handle_signal);
}
我們先來看下信號的處理函數,SIGCHLD_handler就是往socketpair的一端寫入數據

 

 

static void SIGCHLD_handler(int) {
    if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
        ERROR("write(signal_write_fd) failed: %s\n", strerror(errno));
    }
}
然後sockpair的另一端,注冊到epoll中去,我們也來看下處理函數handle_signal,讀取了sockpair中的內容後,調用了reap_any_outstanding_children函數,這個函數在signal_handler_init函數裡面也調用了。

 

 

static void handle_signal() {
    // Clear outstanding requests.
    char buf[32];
    read(signal_read_fd, buf, sizeof(buf));

    reap_any_outstanding_children();
}
我們來看下reap_any_outstanding_children函數,直接while循環調用了wait_for_one_process函數

 

static void reap_any_outstanding_children() {
    while (wait_for_one_process()) {
    }
}

我們再來看wait_for_one_process函數,先調用了waitpid方法,pid為-1,代表監聽所有的子進程。WNOHANG代表不阻塞。當pid值返回0和-1時return false,直接while循環退出了,否則一直處理一個接著一個進程掛掉的信號。

static bool wait_for_one_process() {
    int status;
    pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));//WNOHANG代表不阻塞
    if (pid == 0) {
        return false;
    } else if (pid == -1) {
        ERROR("waitpid failed: %s\n", strerror(errno));
        return false;
    }

    service* svc = service_find_by_pid(pid);//找到service

    std::string name;
    if (svc) {
        name = android::base::StringPrintf("Service '%s' (pid %d)", svc->name, pid);
    } else {
        name = android::base::StringPrintf("Untracked pid %d", pid);
    }

    NOTICE("%s %s\n", name.c_str(), DescribeStatus(status).c_str());

    if (!svc) {
        return true;//沒找到service 直接結束處理下個進程信號
    }

    // TODO: all the code from here down should be a member function on service.

    if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {//如果不是oneshot 或者是restart的這種flag
        NOTICE("Service '%s' (pid %d) killing any children in process group\n", svc->name, pid);
        kill(-pid, SIGKILL);//kill該進程群組所有的進程
    }

    // Remove any sockets we may have created.去除socket
    for (socketinfo* si = svc->sockets; si; si = si->next) {
        char tmp[128];
        snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);
        unlink(tmp);
    }

    if (svc->flags & SVC_EXEC) {
        INFO("SVC_EXEC pid %d finished...\n", svc->pid);
        waiting_for_exec = false;
        list_remove(&svc->slist);
        free(svc->name);
        free(svc);
        return true;
    }

    svc->pid = 0;
    svc->flags &= (~SVC_RUNNING);//去除running的flag

    // 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;//oneshot而且沒有restart的flag,附上disabled的flag
    }

    // Disabled and reset processes do not get restarted automatically.
    if (svc->flags & (SVC_DISABLED | SVC_RESET))  {//已經reset或者disabled直接結束
        svc->NotifyStateChange("stopped");
        return true;
    }

    time_t now = gettime();
    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 true;
            }
        } else {
            svc->time_crashed = now;
            svc->nr_crashed = 1;
        }
    }

    svc->flags &= (~SVC_RESTART);
    svc->flags |= SVC_RESTARTING;// restarting代表重啟中

    // Execute all onrestart commands for this service.
    struct listnode* node;
    list_for_each(node, &svc->onrestart.commands) {//有onrestart的command命令的,重啟的時候要先調用命令
        command* cmd = node_to_item(node, struct command, clist);
        cmd->func(cmd->nargs, cmd->args);
    }
    svc->NotifyStateChange("restarting");
    return true;
}

這個函數主要將service的flags賦值,一般的進程被kill 之後最後會被附上SVC_RESTARTING這個flag,而且又onrestart的,先執行其command。對於已經是disabled和reset的service直接結束,對於是oneshot而且沒有restart flag的service,直接附上disabled這個flag。

我們再來看看service的NotifyStateChange函數,主要是設置init.svc.(service的name)這個屬性為這個service最新的狀態。

 

void service::NotifyStateChange(const char* new_state) {
    if (!properties_initialized()) {
        // If properties aren't available yet, we can't set them.
        return;
    }

    if ((flags & SVC_EXEC) != 0) {
        // 'exec' commands don't have properties tracking their state.
        return;
    }

    char prop_name[PROP_NAME_MAX];
    if (snprintf(prop_name, sizeof(prop_name), "init.svc.%s", name) >= PROP_NAME_MAX) {
        // If the property name would be too long, we can't set it.
        ERROR("Property name \"init.svc.%s\" too long; not setting to %s\n", name, new_state);
        return;
    }

    property_set(prop_name, new_state);
}

 

下面我們需要再結合main函數中在while循環中調用的restart_processes函數

static void restart_processes()
{
    process_needs_restart = 0;
    service_for_each_flags(SVC_RESTARTING,
                           restart_service_if_needed);
}

結合service_for_each_flags,遍歷所有的service,只要service的flags有SVC_RESTARTING的就調用restart_service_if_needed函數

void service_for_each_flags(unsigned matchflags,
                            void (*func)(struct service *svc))
{
    struct listnode *node;
    struct service *svc;
    list_for_each(node, &service_list) {
        svc = node_to_item(node, struct service, slist);
        if (svc->flags & matchflags) {
            func(svc);
        }
    }
}

在restart_service_if_needed函數中,會去除SVC_RESTARTING的flag,然後調用service_start啟動進程。

static void restart_service_if_needed(struct service *svc)
{
    time_t next_start_time = svc->time_started + 5;

    if (next_start_time <= gettime()) {
        svc->flags &= (~SVC_RESTARTING);
        service_start(svc, NULL);
        return;
    }

    if ((next_start_time < process_needs_restart) ||
        (process_needs_restart == 0)) {
        process_needs_restart = next_start_time;
    }
}

所以普通的進程,使用kill的話,哪怕進程被kill了之後,還會被init進程啟動的。我們再來看看service_start函數,先把一些flag清除

void service_start(struct service *svc, const char *dynamic_args)
{
    // 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_DISABLED_START));//清除flag
    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) {//已經啟動的service直接退出
        return;
    }

    bool needs_console = (svc->flags & SVC_CONSOLE);//需要控制台的service
    if (needs_console && !have_console) {//是否有控制台,沒有直接退出
        ERROR("service '%s' requires console\n", svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }

    struct stat s;
    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;
    }

後面是selinux的相關,後面就直接fork進程,處理一些子進程的環境等。

    char* scon = NULL;
    if (is_selinux_enabled() > 0) {
    ......
    }

    NOTICE("Starting service '%s'...\n", svc->name);

    pid_t pid = fork();
    if (pid == 0) {
        ......
        _exit(127);
    }

最後設置下service的時間,pid,以及flags狀態改成running,最後通知(設置屬性)改成running了。

    if (pid < 0) {
        ERROR("failed to start '%s'\n", svc->name);
        svc->pid = 0;
        return;
    }

    svc->time_started = gettime();
    svc->pid = pid;
    svc->flags |= SVC_RUNNING;

    if ((svc->flags & SVC_EXEC) != 0) {
        INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",
             svc->pid, svc->uid, svc->gid, svc->nr_supp_gids,
             svc->seclabel ? : "default");
        waiting_for_exec = true;
    }

    svc->NotifyStateChange("running");
}

像普通的進程我們通過kill進程會被init再次啟動,那怎樣才能kill 這個進程,又不會被init進程啟動呢,可以使用stop命令,我們看下do_stop函數。

int do_stop(int nargs, char **args)
{
    struct service *svc;
    svc = service_find_by_name(args[1]);
    if (svc) {
        service_stop(svc);
    }
    return 0;
}

調用了service_stop_or_reset只是flag為disabled

void service_stop(struct service *svc)
{
    service_stop_or_reset(svc, SVC_DISABLED);
}

service_stop_or_reset函數中,最後會kill整個進程組,而且因為把flag改成了disabled,在init中也不會啟動進程了。

static void service_stop_or_reset(struct service *svc, int how)
{
    /* The service is still SVC_RUNNING until its process exits, but if it has
     * already exited it shoudn't attempt a restart yet. */
    svc->flags &= ~(SVC_RESTARTING | SVC_DISABLED_START);//清相關flag

    if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) {
        /* Hrm, an illegal flag.  Default to SVC_DISABLED */
        how = SVC_DISABLED;
    }
        /* if the service has not yet started, prevent
         * it from auto-starting with its class
         */
    if (how == SVC_RESET) {
        svc->flags |= (svc->flags & SVC_RC_DISABLED) ? SVC_DISABLED : SVC_RESET;// 看之前是否有disabled這個flag
    } else {
        svc->flags |= how;
    }

    if (svc->pid) {
        NOTICE("Service '%s' is being killed...\n", svc->name);
        kill(-svc->pid, SIGKILL);//kill 整個進程組
        svc->NotifyStateChange("stopping");
    } else {
        svc->NotifyStateChange("stopped");
    }
}

也可以自己調用start命令,最後通過do_start函數啟動這個service。

int do_start(int nargs, char **args)
{
    struct service *svc;
    svc = service_find_by_name(args[1]);
    if (svc) {
        service_start(svc, NULL);
    }
    return 0;
}

再看下do_restart

int do_restart(int nargs, char **args)
{
    struct service *svc;
    svc = service_find_by_name(args[1]);
    if (svc) {
        service_restart(svc);
    }
    return 0;
}

在service_restart函數中,當是running狀態,直接把它kill了,然後在init處理進程信號時,會把它的flag變成restarting,之後init進程會重啟這個進程。其他的狀態就直接啟動service了。

 

void service_restart(struct service *svc)
{
    if (svc->flags & SVC_RUNNING) {
        /* Stop, wait, then start the service. */
        service_stop_or_reset(svc, SVC_RESTART);
    } else if (!(svc->flags & SVC_RESTARTING)) {
        /* Just start the service since it's not running. */
        service_start(svc, NULL);
    } /* else: Service is restarting anyways. */
}
至於stop start命令只能在init.rc中使用,但是我們可以通過ctl.start ctl.stop ctl.restart來達到這個目的。處理的話,之前在屬性系統中已經分析過了。

 



普通對一個service命令處理只有stop start restart沒有reset,而在class_reset class_stop class_start中有,我們來看看這些命令處理。

int do_class_stop(int nargs, char **args)
{
    service_for_each_class(args[1], service_stop);
    return 0;
}

int do_class_reset(int nargs, char **args)
{
    service_for_each_class(args[1], service_reset);
    return 0;
}

再結合service_for_each_class函數,我們知道class_reset class_stop 只是遍歷所有的service,看看其class是否滿足,滿足就調用service_stop 和 service_reset函數

 

void service_for_each_class(const char *classname,
                            void (*func)(struct service *svc))
{
    struct listnode *node;
    struct service *svc;
    list_for_each(node, &service_list) {
        svc = node_to_item(node, struct service, slist);
        if (!strcmp(svc->classname, classname)) {
            func(svc);
        }
    }
}
而我們再看看do_class_start有點不一樣,遍歷所有的service看看其class是否滿足然後調用service_start_if_not_disabled函數

 

int do_class_start(int nargs, char **args)
{
        /* Starting a class does not start services
         * which are explicitly disabled.  They must
         * be started individually.
         */
    service_for_each_class(args[1], service_start_if_not_disabled);
    return 0;
}

看看service_start_if_not_disabled函數只有在flags不等於SVC_DISABLED的時候才會調用service_start函數。

static void service_start_if_not_disabled(struct service *svc)
{
    if (!(svc->flags & SVC_DISABLED)) {
        service_start(svc, NULL);
    } else {
        svc->flags |= SVC_DISABLED_START;
    }
}

這樣如果調用class_stop再調用class_start也不能再次啟動這些class的service了,只有啟動那些之前調用的是reset的service。

class_stop了之後,就只能一個一個service調用start命令了。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved