Android情景分析之详解init进程(以启动zygote为例)

概述

init是linux系统中用户空间的第一个进程。由于Android是基于linux内核的,所以init也是Android系统中用户空间的第一个进程,它的进程号为1。

作为系统中的第一个用户空间进程,init进程被赋予了很多及其重要的工作职责。

1.      init进程创建系统中几个关键进程,例如zygote等。

2.      Android系统有很多属性,于是init就提供了一个property service(属性服务)来管理它们。(这篇文章讲过 http://blog.csdn.net/hu3167343/article/details/38230271)

今天我们主要以第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解析完之后的结果如下图所示:

Android情景分析之详解init进程(以启动zygote为例)_第1张图片

解析完之后:

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中有这个一句话:

Android情景分析之详解init进程(以启动zygote为例)_第2张图片

在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();
	….
}

至此,死去的服务又开始重新启动了。











你可能感兴趣的:(Android情景分析)