1. 程式人生 > >Android情景分析之詳解init程序(以啟動zygote為例)

Android情景分析之詳解init程序(以啟動zygote為例)

概述

init是linux系統中使用者空間的第一個程序。由於Android是基於linux核心的,所以init也是Android系統中使用者空間的第一個程序,它的程序號為1。

作為系統中的第一個使用者空間程序,init程序被賦予了很多及其重要的工作職責。

1.      init程序建立系統中幾個關鍵程序,例如zygote等。

今天我們主要以第1點來詳細說明下init程序的工作。

init程序的main函式

init程序的主要程式碼如下:

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;
    
    ...
    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);

    ...

    // 屬性服務初始化
    property_init();

    get_hardware_name(hardware, &revision);

    process_kernel_cmdline();

    // selinux安全機制初始化
    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_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();

    // 解析init.rc配置檔案
    INFO("reading config file\n");
    init_parse_config_file("/init.rc");
    
    /*
	解析完init.rc配置檔案後,會得到一系列的Action,action_for_each_trigger函式用來執行early-init階段的Action。
	init將動作執行的時間劃分為4個階段:early-init、init、early-boot、boot。由於有些動作必須要在其他動作完成後才能執行,所以就有了先後之分。哪些動作屬於哪個階段由配置檔案決定。
    */
    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 */
    // 執行init階段的動作
    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");

    // 執行early-boot和boot階段的動作
    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

    // 無限迴圈,用來處理各種訊息
    for(;;) {
        int nr, i, timeout = -1;

        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
        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.      解析init.rc配置檔案。

2.      執行各個階段的動作,建立zygote的工作就是在其中的某個階段完成的。

3.      初始化property service(屬性服務)。

4.      init進入一個無限迴圈,並且等到一些事情的發生。


解析init.rc

init的main函式主要是解析了init.rc檔案,然後執行相應的初始化操作。我們先來看看init_parse_config_file函式的實現:

int init_parse_config_file(const char *fn)
{
    char *data;
    data = read_file(fn, 0); // 讀取inic.rc檔案的內容
    if (!data) return -1;
	
// 開始解析
    parse_config(fn, data);
    DUMP();
    return 0;
}

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;

    nargs = 0;
    state.filename = fn;
    state.line = 0;
    state.ptr = s;
    state.nexttoken = 0;
    state.parse_line = parse_line_no_op; // 設定解析函式,在android4.1上此函式為空

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

	// 開始解析init.rc檔案內容,以行為單位
    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:	// 文字內容,設定為args引數
            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);
    }
}

再來看看keywords.h對init.rc檔案中關鍵字的定義

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)
KEYWORD(class_stop,  COMMAND, 1, do_class_stop)
KEYWORD(class_reset, COMMAND, 1, do_class_reset)
KEYWORD(console,     OPTION,  0, 0)
KEYWORD(critical,    OPTION,  0, 0)
KEYWORD(disabled,    OPTION,  0, 0)
KEYWORD(domainname,  COMMAND, 1, do_domainname)
KEYWORD(exec,        COMMAND, 1, do_exec)
KEYWORD(export,      COMMAND, 2, do_export)
KEYWORD(group,       OPTION,  0, 0)
KEYWORD(hostname,    COMMAND, 1, do_hostname)
KEYWORD(ifup,        COMMAND, 1, do_ifup)
KEYWORD(insmod,      COMMAND, 1, do_insmod)
KEYWORD(import,      SECTION, 1, 0)
KEYWORD(keycodes,    OPTION,  0, 0)
KEYWORD(mkdir,       COMMAND, 1, do_mkdir)
KEYWORD(mount_all,   COMMAND, 1, do_mount_all)
KEYWORD(mount,       COMMAND, 3, do_mount)
KEYWORD(on,          SECTION, 0, 0)
KEYWORD(oneshot,     OPTION,  0, 0)
KEYWORD(onrestart,   OPTION,  0, 0)
KEYWORD(powerctl,    COMMAND, 1, do_powerctl)
KEYWORD(restart,     COMMAND, 1, do_restart)
KEYWORD(restorecon,  COMMAND, 1, do_restorecon)
KEYWORD(rm,          COMMAND, 1, do_rm)
KEYWORD(rmdir,       COMMAND, 1, do_rmdir)
KEYWORD(seclabel,    OPTION,  0, 0)
KEYWORD(service,     SECTION, 0, 0)
KEYWORD(setcon,      COMMAND, 1, do_setcon)
KEYWORD(setenforce,  COMMAND, 1, do_setenforce)
KEYWORD(setenv,      OPTION,  2, 0)
KEYWORD(setkey,      COMMAND, 0, do_setkey)
KEYWORD(setprop,     COMMAND, 2, do_setprop)
KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)
KEYWORD(setsebool,   COMMAND, 2, do_setsebool)
KEYWORD(socket,      OPTION,  0, 0)
KEYWORD(start,       COMMAND, 1, do_start)
KEYWORD(stop,        COMMAND, 1, do_stop)
KEYWORD(swapon_all,  COMMAND, 1, do_swapon_all)
KEYWORD(trigger,     COMMAND, 1, do_trigger)
KEYWORD(symlink,     COMMAND, 1, do_symlink)
KEYWORD(sysclktz,    COMMAND, 1, do_sysclktz)
KEYWORD(user,        OPTION,  0, 0)
KEYWORD(wait,        COMMAND, 1, do_wait)
KEYWORD(write,       COMMAND, 2, do_write)
KEYWORD(copy,        COMMAND, 2, do_copy)
KEYWORD(chown,       COMMAND, 2, do_chown)
KEYWORD(chmod,       COMMAND, 2, do_chmod)
KEYWORD(loglevel,    COMMAND, 1, do_loglevel)
KEYWORD(load_persist_props,    COMMAND, 0, do_load_persist_props)
KEYWORD(ioprio,      OPTION,  0, 0)

Init.rc檔案的主要內容如下(system\core\rootdir\init.rc):

# Copyright (C) 2012 The Android Open Source Project
#
# IMPORTANT: Do not create world writable files or directories.
# This is a common source of Android security bugs.
#

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

# 根據上面的分析可知,on關鍵字標示一個section,對應的名字為early-init
on early-init 
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_adj -16

    # Set the security context for the init process.
    # This should occur before anything else (e.g. ueventd) is started.
    setcon u:r:init:s0

    start ueventd

# create mountpoints
    mkdir /mnt 0775 root system
…
# 又一個新的section,名為boot
on boot
...
	# class_start是一個COMMAND,對應處理函式是do_class_start
    class_start core
    class_start main
…

# system server cannot write to /proc/sys files, so proxy it through init
on property:sys.sysctl.extra_free_kbytes=*
    write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes}

## Daemon processes to be run by init.
##
service ueventd /sbin/ueventd
    class core
    critical
    seclabel u:r:ueventd:s0

…
# 下面這個section的意思是,如果屬性ro.kernel.qemu為1,那麼執行相應的COMMAND ,# 即start adbd
on property:ro.kernel.qemu=1
    start adbd

...
#  service關鍵字也是一個SECTION,對應的SECTION名為zygote。
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd

service drm /system/bin/drmserver
    class main
    user drm
    group drm system inet drmrpc
...

從上面對init.rc檔案大致分析可知

1、  一個section的內容從這個標識section的關鍵字開始,到下一個標識section的地方結束。

2、  Init.rc中出現了名為early-init和boot的section,這裡的early-init和boot就是前面介紹的4個動作執行階段中的early-init和boot。也就是說在boot階段執行的動作都是由boot這個section定義的。

另外,zygote被放在了一個service section中,下面以zygote這個section為例,具體介紹下service是如何解析的。


解析zygote 服務

Zygote對應的service section內容如下:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    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:
        state->context = parse_service(state, nargs, args);
        if (state->context) {
            state->parse_line = parse_line_service;
            return;
        }
        break;
    case K_on:
        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;
}

其中,解析service時,用到了parse_service和parse_line_service這兩個函式,在介紹這兩個函式之前,我們首先看看init是如何組織這個service的。

service結構體

struct service {
	/*
		用來連線所有的services。Init中有一個全域性的service_list變數,專門用來儲存解析配置檔案後得到的service。 
	*/
    struct listnode slist;

    const char *name;			// service的名字,在我們的例子中為zygote
    const char *classname;		// service所屬的class名字,預設為default

    unsigned flags;			// service的屬性
    pid_t pid;					// 程序號
    time_t time_started;    /* 上一次啟動時間 */
    time_t time_crashed;    /* 上一次死亡時間*/
    int nr_crashed;         /* 死亡次數 */
    
    uid_t uid;
    gid_t gid;
    gid_t supp_gids[NR_SVC_SUPP_GIDS];
    size_t nr_supp_gids;

    char *seclabel;
	/*
		有些service需要使用socket來通訊,下面這個socketinfo用來描述socket的相關資訊。
		我們的zygote也使用了socket,配置檔案中的內容是socket zygote stream 660 root system。
		它表示將建立一個AF_STREAM型別的socket(其實就是TCP socket),該socket的名為zygote,讀寫許可權是660。
	*/
    struct socketinfo *sockets;
	// service一般執行在一個單獨的程序中,envvars用來描述建立這個程序時所需的環境
	// 變數資訊。
    struct svcenvinfo *envvars;
	/*
		雖然onrestart關鍵字是一個OPTION,可是這個OPTION後面一般都跟著一個COMMAND,action結構體就是用來儲存command資訊的。
	*/
    struct action onrestart;  /* Actions to execute on restart. */
    
    /* keycodes for triggering this service via /dev/keychord */
    int *keycodes;
    int nkeycodes;
    int keychord_id;

	// io優先順序
    int ioprio_class;
    int ioprio_pri;

    int nargs; 	// 引數個數
    /* "MUST BE AT THE END OF THE STRUCT" */
    char *args[1];	// 用於儲存引數內容
};

瞭解了service結構,那麼其中的action是怎麼儲存的呢,我們再來看看action結構體。

struct action {
	/*
		一個action結構可以被鏈入三個不同的雙向連結串列中,其中alist用來儲存所有的action,
		qlist用來連結那些等待執行的action,tlist用來連結那些待某些條件滿足後就需要執行的action。
	*/    
	/* 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;	// 名字一般為"onrestart"
	
	/*
		一個command的結構列表,command結構如下:
		struct command
		{
        		/* list of commands in an action */
    			struct listnode clist;
    			int (*func)(int nargs, char **args);
    			int nargs;
    			char *args[1];
		};
	*/
    	struct listnode commands;
    	struct command *current;
};

解析parse_service

瞭解了關鍵的結構體之後,我們接下來看看parse_service函式的具體思路:

static void *parse_service(struct parse_state *state, int nargs, char **args)
{
    struct service *svc;
// ….省略引數檢測

// 根據服務名在全域性服務連結串列service_list裡面查詢,如果已經有了,則返回
    svc = service_find_by_name(args[1]);
    if (svc) {
        parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
        return 0;
    }

	// 分配service結構記憶體
    nargs -= 2;
    svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
    if (!svc) {
        parse_error(state, "out of memory\n");
        return 0;
    }
    svc->name = args[1]; // 服務名,此處為zygote
    svc->classname = "default"; // 設定classname為default,此處很關鍵
    memcpy(svc->args, args + 2, sizeof(char*) * nargs); // 拷貝引數,此處拷貝的是字元指標
    svc->args[nargs] = 0;
    svc->nargs = nargs;	// 儲存引數個數
    svc->onrestart.name = "onrestart"; 
    list_init(&svc->onrestart.commands); // 初始化action結構的commands連結串列
    list_add_tail(&service_list, &svc->slist); // 將當前服務新增到service_list連結串列中
    return svc;
}

由上面分析可知,parse_service函式只是搭建了一個svc的框架,並最後把它鏈入了service_list連結串列。我們再來看看parse_line_service函式吧。

解析parse_line_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:	// 用來修改classname
        if (nargs != 2) {
            parse_error(state, "class option requires a classname\n");
        } else {
            svc->classname = args[1];
        }
        break;
    …
    case K_oneshot:
        svc->flags |= SVC_ONESHOT;
        break;
    case K_onrestart: // 開始解析onrestart後面的COMMAND
        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);
        cmd->nargs = nargs;
        memcpy(cmd->args, args, sizeof(char*) * nargs);
		// 新增到commands連結串列中
        list_add_tail(&svc->onrestart.commands, &cmd->clist);
        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]; // socket名字
        si->type = args[2]; // socket型別
        si->perm = strtoul(args[3], 0, 8); // socket讀寫許可權
        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;
    }
    …
    default:
        parse_error(state, "invalid option '%s'\n", args[0]);
    }
}

parse_line_service函式將根據配置檔案的內容填充service結構體,zygote service解析完之後的結果如下圖所示:

解析完之後:

1.      全域性的service_list連結串列將解析後的service全部連結到了一起。

2.      socketinfo也是一個雙向連結串列,這裡連結了service用到的socket資訊,zygote程序只有一個socket。

3.      onrestart通過commands指向一個commands連結串列,zygote共有4個commands。

init控制service

啟動zygote

init.rc的zygote服務中有這樣一句話:

service zygote /system/bin/app_process-Xzygote /system/bin --zygote --start-system-server

class main

服務預設建立的時候classname為“default”,這裡通過“class main”來修改classname為“main”。之後,我們再回過頭去看init.rc中有這個一句話:


在init程序啟動的boot階段,有一句“class_start main”。而class_start是一個COMMAND,其對應的處理函式是do_class_start。接著來看看do_class_start函式吧:

int do_class_start(int nargs, char **args)
{
	/*
		Args為do_class_start的引數,init.rc中只有一個引數,就是“main“、“core”等。
		service_for_each_class函式就是從service_list連結串列中找到classname和引數一致的service,
		然後呼叫service_start_if_not_disabled函式。
	*/
    	service_for_each_class(args[1], service_start_if_not_disabled);
    	return 0;
}

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

service_start_if_not_disabled函式具體做了什麼呢?接著看:

static void service_start_if_not_disabled(struct service *svc)
{
	// 如果flags不為SVC_DISABLED,那麼開始啟動服務
    if (!(svc->flags & SVC_DISABLED)) {
        service_start(svc, NULL);
    }
}

具體來看看服務的啟動過程:

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;

    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART));
    svc->time_started = 0;

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

	// … 省略selinux相關的內容
	
	// 通過fork建立一個子程序
    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);
            add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
        }
		// 設定屬性程序的環境變數
        for (ei = svc->envvars; ei; ei = ei->next)
            add_environment(ei->name, ei->value);

        setsockcreatecon(scon);
		
	// 根據socketinfo建立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));
            int s = create_socket(si->name, socket_type,
                                  si->perm, si->uid, si->gid);
            if (s >= 0) {
                publish_socket(si->name, s);
            }
        }

        ….
		
        if (!dynamic_args) {
	/*
		呼叫execve函式來執行/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 {
            …
        }
        _exit(127);
    }

	…
	// 父程序中設定子服務程序的資訊,啟動時間、pid等。
    svc->time_started = gettime();
    svc->pid = pid;
    svc->flags |= SVC_RUNNING;
	
	// 如果屬性服務已經初始化完畢了,那麼就設定相應服務為running狀態
    if (properties_inited())
        notify_service_state(svc->name, "running");
}

原來,zygote是通過fork和execv共同建立的,但是service結構中的onrestart欄位好像沒有派上用場,原因何在?我們接著往下看。


重啟zygote

init程序在初始化的時候會呼叫signal_init函式來初始化一個socket對,主要用來父子程序間通訊。

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

    	struct sigaction act;
	memset(&act, 0, sizeof(act));
	/*
		父程序設定sa_flags為SA_NOCLDSTOP,表明只有子程序在退出時父程序才能收到SIGCHLD的訊號,處理函式為sigchld_handler
	*/
    	act.sa_handler = sigchld_handler;
    	act.sa_flags = SA_NOCLDSTOP;
    	sigaction(SIGCHLD, &act, 0);

    	/* 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();
}

因此,子程序在退出時,父程序收到SIGCHLD訊息,此時,訊號處理函式為sigchld_handler。

static void sigchld_handler(int s)
{
    write(signal_fd, &s, 1);
}

signal_fd被寫入值之後,那麼對應的signal_recv_fd便能收到訊息了。處理邏輯在init程序main函式的最後面的for迴圈中進行處理:

int get_signal_fd()
{
    return signal_recv_fd;
}

for (i = 0; i < fd_count; i++) {
    …
        else if (ufds[i].fd == get_signal_fd())
            handle_signal();
    }
}

void handle_signal(void)
{
    char tmp[32];

    /* we got a SIGCHLD - reap and restart as needed */
    read(signal_recv_fd, tmp, sizeof(tmp));
    while (!wait_for_one_process(0))
        ;
}

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;

	// 獲得退出程序的pid
    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_list連結串列,找到退出的那個服務
    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);

	/*
		如果不是SVC_ONESHOT即一次性啟動的服務,那麼就殺死該程序的所有子程序,這也是為什麼zygote死後,java世界崩潰的原因。
	*/
    if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
        kill(-pid, SIGKILL);
        NOTICE("process '%s' killing any children in process group\n", svc->name);
    }

    /* 刪除服務建立的socket資訊*/
    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);

	/* 
		如果服務程序的標誌SVC_ONESHOT置位了,則表明此服務為一次性服務,死了之後就不需要重啟,除非設定SVC_RESTART標誌來手動重啟該服務。
		此類服務死了之後,就置為SVC_DISABLED
	*/
    if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {
        svc->flags |= SVC_DISABLED;
    }

    /* 通知屬性伺服器,將該服務屬性設定為stopped */
    if (svc->flags & (SVC_DISABLED | SVC_RESET) )  {
        notify_service_state(svc->name, "stopped");
        return 0;
    }

	now = gettime();
	/*
		如果程序帶有SVC_CRITICA標誌,那麼此時如果程序在4分鐘內,死了>4次,則重啟進入到recovery模式。根據init.rc的配置可知,ueventd、healthd、servicemanager等服務享有此種特殊待遇。
	*/
    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;

	/* 
		Execute all onrestart commands for this service. 

		Zygote將執行如下操作:
		write /sys/android_power/request_state wake
		write /sys/power/state on
		restart media
		restart netd
	*/
    list_for_each(node, &svc->onrestart.commands) {
        cmd = node_to_item(node, struct command, clist);
        cmd->func(cmd->nargs, cmd->args);
    }

	/* 重新通知屬性伺服器,將該服務屬性設定為restarting */
    notify_service_state(svc->name, "restarting");
    return 0;
}

通過上面的程式碼可以知道onrestart的作用了,但是上述程式碼只是設定了相應svc的一些狀態,flag、time_crashed、nr_crashed等等。那麼真正的svc啟動是在哪裡呢?在init程序的main函式中,有一個大的無限迴圈,程式碼如下: 

for(;;) {
	int nr, i, timeout = -1;

	execute_one_command();

	// 此處就是呼叫service_start函式來重啟那些flag帶有SVC_RESTARTING的服務程序
	restart_processes();
	….
}

至此,死去的服務又開始重新啟動了。