Android -- Android Init进程的处理流程分析


最近在看Android Init进程的处理流程,现记录如下。

在Android中,Init进程是Linux内核启动后创建的第一个用户进程,地位非常重要。Init进程的可执行文件在/system/core/init/目录下,我们直接看Init进程的main()函数,该函数的代码处理流程较长,我们分两大段来分析。首先看第一大段:

int main(int argc, char** argv) {//检测启动程序的文件名,如果是ueventd或者watchdogd,则执行相应守护进程的主函数,然后退出    if (!strcmp(basename(argv[0]), "ueventd")) {        return ueventd_main(argc, argv);    }    if (!strcmp(basename(argv[0]), "watchdogd")) {        return watchdogd_main(argc, argv);    }    // Clear the umask.    umask(0);//umask设置用户创建文件的默认属性;默认情况下文件的属性是022,这里参数为0,意为该进程创建的文件的属性值将为0777    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、devpts、proc和sysfs都是文件系统        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);    }    // We must have some place other than / to create the device nodes for    // kmsg and null, otherwise we won't be able to remount / read-only    // later on. Now that tmpfs is mounted on /dev, we can actually talk    // to the outside world.    open_devnull_stdio();//把标准输入、标准输出、标准错误重定向到空设备文件"/dev/__null__"    klog_init();//创建/dev/__kmsg__设备节点,让进程可以使用kernel的log系统来输出log    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.        //在/dev目录下创建.booting空文件,表示初始化正在进行;is_booting()函数会依靠这个文件判断进程是否正在初始化        //进程初始化结束后,.booting文件将会被删除        close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));        property_init();//初始化Android属性系统,Android中的属性系统在各个进程间都可以访问,这里创建了一块共享区域来存储属性值        // 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();//解析/proc/cmdline文件,获得kernel的启动参数,将结果保存到几个属性中        // Propogate the kernel variables to internal variables        // used by init as well as the current required properties.        export_kernel_boot_props();//将一些系统属性发布到系统中    }    // Set up SELinux, including loading the SELinux policy if we're in the kernel domain.    selinux_initialize(is_first_stage);//初始化SELinux    // 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.    //SELinux初始化的一部分工作    INFO("Running restorecon...\n");    restorecon("/dev");    restorecon("/dev/socket");    restorecon("/dev/__properties__");    restorecon_recursive("/sys");    ...    return 0;}
函数一开始,会首先判断当前启动程序名是否是ueventd或者watchdogd,如果是,则会走对应的初始化流程后,并终止程序。

首先,主程序会创建一些需要的目录,并挂载几个文件系统到系统中;其次,会重定向标准输入、标准输出、标准错误流到/dev/__null_设备文件下,并初始化内核Log系统,使我们此时可以输出log(因为此时Android的Log系统还未初始化);接着,处理kernel启动参数,设置系统默认属性,并对SELinux的内容进行一些初始化操作等。

我们这里只看一些重要的跟init.rc文件相关的处理内容,其他的部分可以参考代码中的注释加以理解。

接下来分析第二段重要代码,它包含了init.rc文件解析和init进程如何变成守护进程的操作:

//epoll轮询与select机制类似,但它更高效;我们可以向epoll_fd中添加我们想要监听的一组fd,当有某个fd有事件产生时,它就会根据我们事先注册的结果//根据获取到的epoll_event事件信息,调用epoll_event.data.ptr这个函数指针来处理监听到的事件    epoll_fd = epoll_create1(EPOLL_CLOEXEC);//创建epoll 句柄,并设置FD_CLOEXEC;后续会注册属性监听事件、组合键盘事件、信号处理事件的fd到该epoll_fd中    if (epoll_fd == -1) {        ERROR("epoll_create1 failed: %s\n", strerror(errno));        exit(1);    }    signal_handler_init();//初始化signal信号事件处理,会signal_read_fd注册到epoll_fd中,通过epoll轮询检测事件,handle_signal()实际处理监听到的信号事件    property_load_boot_defaults();//解析default.prop文件,把文件中的属性值解析并发布到系统中    start_property_service();//启动属性服务,会创建一个socket 句柄,并将该fd注册到epoll_fd中;通过epoll轮询查询属性请求,并注册handle_property_set_fd()为实际事件处理函数;    init_parse_config_file("/init.rc");//解析init.rc文件//将指定的action加入到action_queue(一个单向链表结构)中,每个action由一个函数指针和表示名字的字符串组成    action_for_each_trigger("early-init", action_add_queue_tail);//调用queue_builtin_action()函数动态生成一个action加入到action_queue中,每个action由一个函数指针和表示名字的字符串组成    // 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");//注册组合键盘消息监听处理机制,会将/dev/keychord目录的一个fd注册到epoll_fd中,通过epoll轮询事件消息,注册handle_keychord()实际处理组合键盘事件    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);//将指定的action加入到action_queue中    // 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()函数动态生成一个action加入到action_queue中,每个action由一个函数指针和表示名字的字符串组成    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");    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);//bootchart是一个用可视化方式对启动过程进行性能分析的工具;需要定时唤醒进程        epoll_event ev;        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));//开始轮询,epoll_wait()等待事件产生        if (nr == -1) {            ERROR("epoll_wait failed: %s\n", strerror(errno));        } else if (nr == 1) {            ((void (*)()) ev.data.ptr)();//调用epoll_event事件存储的函数指针处理事件        }    }
epoll机制跟select机制是类似的,两者都可以处理一组fd,监听它们的读写情况,当关注的fd有事件产生时,我们可以进行处理;只不过epoll机制比select机制更高效,所以这里采用了epoll机制进行轮询,而非select。
//epoll轮询与select机制类似,但它更高效;我们可以向epoll_fd中添加我们想要监听的一组fd,当有某个fd有事件产生时,它就会根据我们事先注册的结果//根据获取到的epoll_event事件信息,调用epoll_event.data.ptr这个函数指针来处理监听到的事件    epoll_fd = epoll_create1(EPOLL_CLOEXEC);//创建epoll句柄,并设置FD_CLOEXEC;后续会注册属性监听事件、组合键盘事件、信号处理事件的fd到该epoll_fd中    if (epoll_fd == -1) {        ERROR("epoll_create1 failed: %s\n", strerror(errno));        exit(1);    }
我们调用epoll_create()函数创建了一个epoll句柄,并保存到全局变量epoll_fd中。
signal_handler_init();//初始化signal信号事件处理,会signal_read_fd注册到epoll_fd中,通过epoll轮询检测事件,handle_signal()实际处理监听到的信号事件start_property_service();//启动属性服务,会创建一个socket 句柄,并将该fd注册到epoll_fd中;通过epoll轮询查询属性请求,并注册handle_property_set_fd()为实际事件处理函数;
接着,我们初始化signal和property处理系统,两函数的处理流程类似:创建、获取一个socket的文件描述符fd,通过epoll_ctl()将有兴趣的fd添加到epoll_fd进行监听。

先看signal事件部分的处理:

static void reap_any_outstanding_children() {    while (wait_for_one_process()) {    }}static void handle_signal() {    // Clear outstanding requests.    char buf[32];    read(signal_read_fd, buf, sizeof(buf));    reap_any_outstanding_children();}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;//设置信号处理函数句柄,当有信号产生时,会向上面创建的socket写入数据,epoll监控到该socket对中的fd可读时,就会调用注册的函数去处理该事件    act.sa_flags = SA_NOCLDSTOP;//设置标志,表示只有当子进程终止时才接受SIGCHID信号    sigaction(SIGCHLD, &act, 0);//初始化SIGCHLD信号处理方式    reap_any_outstanding_children();    register_epoll_handler(signal_read_fd, handle_signal);}
调用socketpair()创造一对未命名的、相互连接的UNIX域套接字,接着创建sigaction结构实例,初始化函数句柄、设置标志位,最终设置SIGCHLD信息处理方方式。

reap_any_outstanding_children()会调用wait_for_one_process()循环等待有进程终止的信号,并对它进行处理,这部分后面再分析。

register_epoll_handler(signal_read_fd, handle_signal)函数就是把我们关注的fd添加到epoll_fd中,让它轮询查询:

void register_epoll_handler(int fd, void (*fn)()) {    epoll_event ev;    ev.events = EPOLLIN;//对文件描述符可读    ev.data.ptr = reinterpret_cast(fn);//保存指定的函数指针,用于后续的事件处理    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {//向epoll_fd添加要监听的fd,比如property、keychord和signal事件监听        ERROR("epoll_ctl failed: %s\n", strerror(errno));    }}
注意,这里指定了signal事件处理的函数句柄:handle_signal()。

属性服务事件的监听处理与signal类似:

void start_property_service() {    property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,                                    0666, 0, 0, NULL);    if (property_set_fd == -1) {        ERROR("start_property_service socket creation failed: %s\n", strerror(errno));        exit(1);    }    listen(property_set_fd, 8);    register_epoll_handler(property_set_fd, handle_property_set_fd);}

/* * create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR * ("/dev/socket") as dictated in init.rc. This socket is inherited by the * daemon. We communicate the file descriptor's value via the environment * variable ANDROID_SOCKET_ENV_PREFIX ("ANDROID_SOCKET_foo"). */int create_socket(const char *name, int type, mode_t perm, uid_t uid,                  gid_t gid, const char *socketcon){    struct sockaddr_un addr;    int fd, ret;    char *filecon;    if (socketcon)        setsockcreatecon(socketcon);    fd = socket(PF_UNIX, type, 0);    if (fd < 0) {        ERROR("Failed to open socket '%s': %s\n", name, strerror(errno));        return -1;    }    if (socketcon)        setsockcreatecon(NULL);    memset(&addr, 0 , sizeof(addr));    addr.sun_family = AF_UNIX;    snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",             name);    ret = unlink(addr.sun_path);    if (ret != 0 && errno != ENOENT) {        ERROR("Failed to unlink old socket '%s': %s\n", name, strerror(errno));        goto out_close;    }    filecon = NULL;    if (sehandle) {        ret = selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK);        if (ret == 0)            setfscreatecon(filecon);    }    ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));    if (ret) {        ERROR("Failed to bind socket '%s': %s\n", name, strerror(errno));        goto out_unlink;    }    setfscreatecon(NULL);    freecon(filecon);    chown(addr.sun_path, uid, gid);    chmod(addr.sun_path, perm);    INFO("Created socket '%s' with mode '%o', user '%d', group '%d'\n",         addr.sun_path, perm, uid, gid);    return fd;out_unlink:    unlink(addr.sun_path);out_close:    close(fd);    return -1;}

void register_epoll_handler(int fd, void (*fn)()) {    epoll_event ev;    ev.events = EPOLLIN;//对文件描述符可读    ev.data.ptr = reinterpret_cast(fn);//注册指定的函数指针,用于后续的事件处理    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {//向epoll_fd添加要监听的fd,比如property、keychord和signal事件监听        ERROR("epoll_ctl failed: %s\n", strerror(errno));    }}

先按设置创建一个socket,并会在/dev/socket目录下创建一个对应的设备文件,接着在此socket上进行绑定并开始监听,这表明此处是服务端,会有客户端连接此socket,并发送属性设置、读取请求;epoll监听到该fd有数据可读时,就会调用注册的函数句柄handle_property_set_fd()处理这个请求。这一部分内容只是将signal、property要监听的fd加入到了epoll_fd中,还未真正开始轮询事件。

再接着看:

init_parse_config_file("/init.rc");//解析init.rc文件
这是真正解析init.rc文件的函数:
int init_parse_config_file(const char* path) {    INFO("Parsing %s...\n", path);    Timer t;    std::string data;    if (!read_file(path, &data)) {//将init.rc配置文件读入内存        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;}
先把init.rc配置文件的内容读到内存中,再调用parse_config()函数进行解析:
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);//是一个section块,进行处理                } 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) {//处理init.rc文件中的import配置,它的作用是导入其他的配置文件,扩展功能         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文件的解析流程如下图描述:

如果我们遇到了新的一行,并且它是一个section块,我们就调用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://action        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;}
parse_new_section()根据三个关键字分别处理:

  • "service":调用parse_service()初始化一个结构service,并把它添加到service_list列表中去;把行处理函数设置为parse_line_service(),以解析它的Options。
  • "on":调用parse_action()初始化一个action结构,并把它添加到action_list列表中去;把行处理函数设置为parse_line_action(),以解析它的Commands。
  • "import":调用parse_import()初始化一个import结构,并把它添加到import_list列表中去。
这里涉及到了几个链表结构,它们的定义、初始化过程是:
static list_declare(service_list);//init.rc中解析出的service保存到此列表中static list_declare(action_list);//init.rc中解析出的action保存到此列表中static list_declare(action_queue);//将要执行的action列表
#define list_declare(name) \    struct listnode name = { \        .next = &name, \        .prev = &name, \    }
而listnode结构体的定义是:
struct listnode{    struct listnode *next;    struct listnode *prev;};

从listnode的定义可知,这三个链表结构都是双向链表;但有一点很奇怪,该链表没有定义数据域,那通过什么方式来获取节点的数据呢?这个在后面会分析。

在看Service、Action的解析过程之前,有必要看下init.rc文件中所使用的指令对应的函数集,这直接影响着各个指令所代表的具体操作是什么。init.rc所使用的各个关键字定义在keywords.h中:

#ifndef KEYWORDint do_bootchart_init(int nargs, char **args);int do_class_start(int nargs, char **args);int do_class_stop(int nargs, char **args);int do_class_reset(int nargs, char **args);int do_domainname(int nargs, char **args);int do_enable(int nargs, char **args);int do_exec(int nargs, char **args);int do_export(int nargs, char **args);int do_hostname(int nargs, char **args);int do_ifup(int nargs, char **args);int do_insmod(int nargs, char **args);int do_installkey(int nargs, char **args);int do_mkdir(int nargs, char **args);int do_mount_all(int nargs, char **args);int do_mount(int nargs, char **args);int do_powerctl(int nargs, char **args);int do_restart(int nargs, char **args);int do_restorecon(int nargs, char **args);int do_restorecon_recursive(int nargs, char **args);int do_rm(int nargs, char **args);int do_rmdir(int nargs, char **args);int do_setprop(int nargs, char **args);int do_setrlimit(int nargs, char **args);int do_setusercryptopolicies(int nargs, char **args);int do_start(int nargs, char **args);int do_stop(int nargs, char **args);int do_swapon_all(int nargs, char **args);int do_trigger(int nargs, char **args);int do_symlink(int nargs, char **args);int do_sysclktz(int nargs, char **args);int do_write(int nargs, char **args);int do_copy(int nargs, char **args);int do_chown(int nargs, char **args);int do_chmod(int nargs, char **args);int do_loglevel(int nargs, char **args);int do_load_persist_props(int nargs, char **args);int do_load_system_props(int nargs, char **args);int do_verity_load_state(int nargs, char **args);int do_verity_update_state(int nargs, char **args);int do_wait(int nargs, char **args);#define __MAKE_KEYWORD_ENUM__#define KEYWORD(symbol, flags, nargs, func) K_##symbol,enum {    K_UNKNOWN,#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(class_reset, COMMAND, 1, do_class_reset)    KEYWORD(class_start, COMMAND, 1, do_class_start)    KEYWORD(class_stop,  COMMAND, 1, do_class_stop)    KEYWORD(console,     OPTION,  0, 0)    KEYWORD(copy,        COMMAND, 2, do_copy)    KEYWORD(critical,    OPTION,  0, 0)    KEYWORD(disabled,    OPTION,  0, 0)    KEYWORD(domainname,  COMMAND, 1, do_domainname)    KEYWORD(enable,      COMMAND, 1, do_enable)    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(import,      SECTION, 1, 0)    KEYWORD(insmod,      COMMAND, 1, do_insmod)    KEYWORD(installkey,  COMMAND, 1, do_installkey)    KEYWORD(ioprio,      OPTION,  0, 0)    KEYWORD(keycodes,    OPTION,  0, 0)    KEYWORD(load_system_props,     COMMAND, 0, do_load_system_props)    KEYWORD(load_persist_props,    COMMAND, 0, do_load_persist_props)    KEYWORD(loglevel,    COMMAND, 1, do_loglevel)    KEYWORD(mkdir,       COMMAND, 1, do_mkdir)    KEYWORD(mount_all,   COMMAND, 1, do_mount_all)    KEYWORD(mount,       COMMAND, 3, do_mount)    KEYWORD(oneshot,     OPTION,  0, 0)    KEYWORD(onrestart,   OPTION,  0, 0)    KEYWORD(on,          SECTION, 0, 0)    KEYWORD(powerctl,    COMMAND, 1, do_powerctl)    KEYWORD(restart,     COMMAND, 1, do_restart)    KEYWORD(restorecon,  COMMAND, 1, do_restorecon)    KEYWORD(restorecon_recursive,  COMMAND, 1, do_restorecon_recursive)    KEYWORD(rm,          COMMAND, 1, do_rm)    KEYWORD(rmdir,       COMMAND, 1, do_rmdir)    KEYWORD(seclabel,    OPTION,  0, 0)    KEYWORD(service,     SECTION, 0, 0)    KEYWORD(setenv,      OPTION,  2, 0)    KEYWORD(setprop,     COMMAND, 2, do_setprop)    KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)    KEYWORD(setusercryptopolicies,   COMMAND, 1, do_setusercryptopolicies)    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(symlink,     COMMAND, 1, do_symlink)    KEYWORD(sysclktz,    COMMAND, 1, do_sysclktz)    KEYWORD(trigger,     COMMAND, 1, do_trigger)    KEYWORD(user,        OPTION,  0, 0)    KEYWORD(verity_load_state,      COMMAND, 0, do_verity_load_state)    KEYWORD(verity_update_state,    COMMAND, 0, do_verity_update_state)    KEYWORD(wait,        COMMAND, 1, do_wait)    KEYWORD(write,       COMMAND, 2, do_write)    KEYWORD(writepid,    OPTION,  0, 0)#ifdef __MAKE_KEYWORD_ENUM__    KEYWORD_COUNT,};#undef __MAKE_KEYWORD_ENUM__#undef KEYWORD#endif
从头文件的内容可以得知,里面的KEYWORD宏是否定义,会直接影响到文件中的定义过程;接着看init_parser.cpp中是如何使用该头文件的:
...#include "keywords.h"#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"};#undef KEYWORD...
我们可以看到,在init_parser.cpp文件中,对keywords.h引用了两次;而这两次引用,会因KEYWORD宏的定义改变,而得到一些初始化结果。我们对这两次引用分别做分析。

第一次引用时,KEYWORD宏未定义,此时做的操作是声明了很多个方法,这些方法就是某些指令对应的功能代码实现;并且定义了两个宏:

#define __MAKE_KEYWORD_ENUM__#define KEYWORD(symbol, flags, nargs, func) K_##symbol,//##表示连接
这样ifndef KEYWORD...endif段的主要内容就结束了了,接着定义了一个枚举结构,这个枚举结构通过之前定义的KEYWORD宏实现,并只用了第一个参数。至此,我们就得到了一个由许多类似K_chmod、K_class这样的关键字填充的枚举定义。

再看第二次引用的结果。继第一次引用之后,init_parser.cpp对KEYWORD宏又进行了定义:

#define KEYWORD(symbol, flags, nargs, func) \    [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },//#sysbol:关键字名称;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"};//一些宏定义函数,协助操作keyword_info数组#define kw_is(kw, type) (keyword_info[kw].flags & (type))//判断当前解析的内容是不是Command类型#define kw_name(kw) (keyword_info[kw].name)//获取关键字名称#define kw_func(kw) (keyword_info[kw].func)//获取关键字的处理函数#define kw_nargs(kw) (keyword_info[kw].nargs)//获取该关键字处理函数的参数个数

此时第二次引用keyword.h,由于KEYWORD已经定义,__MAKE_KEYWORD_ENUM__未定义;这时就是用KEYWORD第二次定义的形式(此次使用四个参数),去初始化keyword_info数组。这个数组,在后续对Action的解析中,会被用来查找与某个Command对应的功能函数。

在Service、Action的解析过程中,用到了lookup_keyword()函数。lookup_keyword()就是根据传入的关键字,返回K_xxx结构的关键字供解析过程判断当前解析的是哪些指令,解析过程同时也用到了上面介绍过的宏定义函数,它的作用已经做了说明。

我们先看servcie的解析过程:

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]);    if (svc) {//不允许出现重名的服务        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);    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);    return svc;}//解析service定义中配置的Optionstatic 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:        svc->flags |= SVC_DISABLED;        svc->flags |= SVC_RC_DISABLED;        break;    case K_ioprio:        if (nargs != 3) {            parse_error(state, "ioprio optin usage: ioprio  \n");        } else {            svc->ioprio_pri = strtoul(args[2], 0, 8);            if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {                parse_error(state, "priority value must be range 0 - 7\n");                break;            }            if (!strcmp(args[1], "rt")) {                svc->ioprio_class = IoSchedClass_RT;            } else if (!strcmp(args[1], "be")) {                svc->ioprio_class = IoSchedClass_BE;            } else if (!strcmp(args[1], "idle")) {                svc->ioprio_class = IoSchedClass_IDLE;            } else {                parse_error(state, "ioprio option usage: ioprio  <0-7>\n");            }        }        break;    case K_group:        if (nargs < 2) {            parse_error(state, "group option requires a group id\n");        } else if (nargs > NR_SVC_SUPP_GIDS + 2) {            parse_error(state, "group option accepts at most %d supp. groups\n",                        NR_SVC_SUPP_GIDS);        } else {            int n;            svc->gid = decode_uid(args[1]);            for (n = 2; n < nargs; n++) {                svc->supp_gids[n-2] = decode_uid(args[n]);            }            svc->nr_supp_gids = n - 2;        }        break;    case K_keycodes:        if (nargs < 2) {            parse_error(state, "keycodes option requires atleast one keycode\n");        } else {            svc->keycodes = (int*) malloc((nargs - 1) * sizeof(svc->keycodes[0]));            if (!svc->keycodes) {                parse_error(state, "could not allocate keycodes\n");            } else {                svc->nkeycodes = nargs - 1;                for (i = 1; i < nargs; i++) {                    svc->keycodes[i - 1] = atoi(args[i]);                }            }        }        break;    case K_oneshot:        svc->flags |= SVC_ONESHOT;        break;    case K_onrestart://解析根据onrestart的内容,填充Command结构体的内容;保存service重启时需要执行的命令集合        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;        }        cmd = (command*) malloc(sizeof(*cmd) + sizeof(char*) * nargs);        cmd->func = kw_func(kw);        cmd->nargs = nargs;        memcpy(cmd->args, args, sizeof(char*) * nargs);        list_add_tail(&svc->onrestart.commands, &cmd->clist);//将解析的命令集合保存起来        break;    case K_critical:        svc->flags |= SVC_CRITICAL;        break;    case K_setenv: { /* name value */        if (nargs < 3) {            parse_error(state, "setenv option requires name and value arguments\n");            break;        }        svcenvinfo* ei = (svcenvinfo*) calloc(1, sizeof(*ei));        if (!ei) {            parse_error(state, "out of memory\n");            break;        }        ei->name = args[1];        ei->value = args[2];        ei->next = svc->envvars;        svc->envvars = ei;        break;    }    case K_socket: {/* name type perm [ uid gid context ] */        if (nargs < 4) {//解析要创建的socket信息并保存            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;        }        socketinfo* si = (socketinfo*) calloc(1, sizeof(*si));        if (!si) {            parse_error(state, "out of memory\n");            break;        }        si->name = args[1];        si->type = args[2];        si->perm = strtoul(args[3], 0, 8);        if (nargs > 4)            si->uid = decode_uid(args[4]);        if (nargs > 5)            si->gid = decode_uid(args[5]);        if (nargs > 6)            si->socketcon = args[6];        si->next = svc->sockets;        svc->sockets = si;        break;    }    case K_user:        if (nargs != 2) {            parse_error(state, "user option requires a user id\n");        } else {            svc->uid = decode_uid(args[1]);        }        break;    case K_seclabel:        if (nargs != 2) {            parse_error(state, "seclabel option requires a label string\n");        } else {            svc->seclabel = args[1];        }        break;    case K_writepid:        if (nargs < 2) {            parse_error(state, "writepid option requires at least one filename\n");            break;        }        svc->writepid_files_ = new std::vector;        for (int i = 1; i < nargs; ++i) {            svc->writepid_files_->push_back(args[i]);        }        break;    default:        parse_error(state, "invalid option '%s'\n", args[0]);    }}

结构体service是init.rc中定义的服务的代码表示,它会保存定义该服务时所配置的所有参数。我们看到最后将解析的service添加到service_list列表中:

static inline void list_add_tail(struct listnode *head, struct listnode *item){    item->next = head;    item->prev = head->prev;    head->prev->next = item;    head->prev = item;}
可以看出,service_list是一个首尾循环的双向链表,action_list和action_queue也是这样。parse_line_service()解析完配置的option后,会将数据写入service结构中。另外,我们从service解析的流程中看出,Android中并不允许在init.rc文件中配置多个重复的service。

再看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));    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);        /* XXX add to hash */    return act;}//填充actionstatic 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);//将解析到的Command添加进列表中

action的解析过程跟service的解析有些类似,不过它并没有做同名action的判断处理;所以,Android中允许定义重复的action。

另外,代码中使用到的宏定义函数代码也贴出来:

#define list_for_each(node, list) \    for (node = (list)->next; node != (list); node = node->next)#define list_for_each_reverse(node, list) \    for (node = (list)->prev; node != (list); node = node->prev)#define list_for_each_safe(node, n, list) \    for (node = (list)->next, n = node->next; \         node != (list); \         node = n, n = node->next)static inline void list_init(struct listnode *node){    node->next = node;    node->prev = node;}static inline void list_add_tail(struct listnode *head, struct listnode *item){    item->next = head;    item->prev = head->prev;    head->prev->next = item;    head->prev = item;}
再看对import的处理:
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);}
此处,解析完import后,会存储到import_list中。我们知道import是导入一个新的rc配置文件来扩展功能的,而对新导入的rc配置文件的处理是在解析完当前.rc配置后才去处理的。Init处理import_list时,会先遍历这个列表取出每一个import结构对象,在分别对import中保存的文件名对应的rc配置文件调用init_parse_config_file()函数来解析;这个过程与前面介绍的内容是一致的:
static void parse_config(const char *fn, const std::string& data){    ...parser_done:    list_for_each(node, &import_list) {//处理init.rc文件中的import配置,它的作用是导入其他的配置文件,扩展功能         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);    }}
我们在前面看listnode的定义,发现它没有定义数据域,那么它是怎么获取一个节点的数据呢?看node_to_item()的处理:
#define node_to_item(node, container, member) \    (container *) (((char*) (node)) - offsetof(container, member))
这里的offsetof宏用来计算member在container结构中的偏移量;所以列表节点对应的数据对象的地址其实是通过node节点的指针计算到的。

看完了init_parse_config_file()处理流程后,我们再接着看它后面的内容:

//将指定的action加入到action_queue(一个单向链表结构)中,每个action由一个函数指针和表示名字的字符串组成    action_for_each_trigger("early-init", action_add_queue_tail);//调用queue_builtin_action()函数动态生成一个action加入到action_queue中,每个action由一个函数指针和表示名字的字符串组成    // 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");//注册组合键盘消息监听处理机制,会将/dev/keychord目录的一个fd注册到epoll_fd中,通过epoll轮询事件消息,注册handle_keychord()实际处理组合键盘事件    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);//将指定的action加入到action_queue中    // 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()函数动态生成一个action加入到action_queue中,每个action由一个函数指针和表示名字的字符串组成    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
这一块代码主要涉及到两个函数的调用:action_for_each_trigger()、queue_builtin_action()。

分别来看:

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_list        act = node_to_item(node, struct action, alist);//获取相应的action对象        list_for_each(node2, &act->triggers) {//遍历action_list            cur_trigger = node_to_item(node2, struct trigger, nlist);//获取相应的trigger对象            if (!strcmp(cur_trigger->name, trigger)) {//找到符合条件的action                func(act);//调用传入的函数句柄            }        }    }}
void action_add_queue_tail(struct action *act){    if (list_empty(&act->qlist)) {        list_add_tail(&action_queue, &act->qlist);    }}
从代码可知action_for_each_trigger()函数就是找到action_list中所有trigger跟参数匹配的action,然后调用回调函数处理这些action;而传入的回调函数是:action_add_queue_tail(),它的作用就是将这些action加入到action_queue中。其实,这里的"early-init"、"init"、"charger"、"late-init"等action代表了init执行过程中的几个时间点,这点可以从init.rc中的定义可以知道,哪些操作属于哪个时间点,是由配置文件决定中的定义决定的;这些不同的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);}void action_add_queue_tail(struct action *act){    if (list_empty(&act->qlist)) {        list_add_tail(&action_queue, &act->qlist);    }}

从代码可知,queue_builtin_action()会新创建一个action,并把它加入到action_queue中。新创建的action由传入的函数指针和代表名称的字符串组成。老版本的Android中是直接调用这些函数来完成初始化工作的,但是,这些函数的处理可能会依赖init.rc里定义的一些命令和服务的执行情况。所以现在把这些初始化函数以Action的形式加入到执行列表中,我们就可以控制它们的调用、执行顺序了。

插入的函数大概功能是:
  • wait_for_coldboot_done_action():等待冷插拔设备初始化完成。
  • mix_hwrng_into_linux_rng_action():从硬件RNG的设备文件/dev/hw_random中读取512字节并写到Linux RNG的设备文件/dev/urandom中。
  • keychord_init_action():初始化组合键监听模块。
  • console_init_action():在屏幕上显示Android字样的Logo。
  • queue_property_triggers_action():检查Action列表中通过修改属性来触发的Action,查看相关的属性是否已经设置,如果已经设置,则加入到action_queue中。
我们再看init.cpp::main()函数的最后一部分处理:
 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);//bootchart是一个用可视化方式对启动过程进行性能分析的工具;需要定时唤醒进程        epoll_event ev;        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));//开始轮询,epoll_wait()等待事件产生        if (nr == -1) {            ERROR("epoll_wait failed: %s\n", strerror(errno));        } else if (nr == 1) {            ((void (*)()) ev.data.ptr)();//调用epoll_event事件存储的函数指针处理事件        }    }

最后,处理过程会进入一个无限while()循环,每次循环开始都会调用execute_one_command()获取action_queue列表中的一个action(其实就是执行该action中的各条Command),然后执行、并从action_queue移除掉它:

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

循环调用restart_processes()去重新启动service_list中的带有SVC_RESTARTING标志的服务(这个服务已经退出但需要重新启动)。

我们再看下restart_processes()的处理:

static void restart_processes(){    process_needs_restart = 0;    service_for_each_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);        }    }}
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;    }}
//为启动的服务fork()进程,并按照配置所需,创建socket、设置属性等;最后去执行该服务对应的应用程序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));    svc->time_started = 0;    // Running processes require no additional work --- if they're in the    // process of exiting, we've ensured that they will immediately restart    // on exit, unless they are ONESHOT.    if (svc->flags & SVC_RUNNING) {        return;    }    bool needs_console = (svc->flags & SVC_CONSOLE);    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;    }//设置安全上下文    char* scon = NULL;    if (is_selinux_enabled() > 0) {        if (svc->seclabel) {            scon = strdup(svc->seclabel);            if (!scon) {                ERROR("Out of memory while starting '%s'\n", svc->name);                return;            }        } else {            char *mycon = NULL, *fcon = NULL;            INFO("computing context for service '%s'\n", svc->args[0]);            int rc = getcon(&mycon);            if (rc < 0) {                ERROR("could not get context while starting '%s'\n", svc->name);                return;            }            rc = getfilecon(svc->args[0], &fcon);            if (rc < 0) {                ERROR("could not get context while starting '%s'\n", svc->name);                freecon(mycon);                return;            }            rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);            if (rc == 0 && !strcmp(scon, mycon)) {                ERROR("Warning!  Service %s needs a SELinux domain defined; please fix!\n", svc->name);            }            freecon(mycon);            freecon(fcon);            if (rc < 0) {                ERROR("could not get context while starting '%s'\n", svc->name);                return;            }        }    }    NOTICE("Starting service '%s'...\n", svc->name);    pid_t pid = fork();//fork()子进程    if (pid == 0) {//pid = 0,表示在子进程中;在子进程中处理具体的创建过程        struct socketinfo *si;        struct svcenvinfo *ei;        char tmp[32];        int fd, sz;        umask(077);        if (properties_initialized()) {//属性系统初始化完成后,将/dev/__properties__设备文件的描述符发布到系统中            get_property_workspace(&fd, &sz);            snprintf(tmp, sizeof(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);//如果某服务声明需要socket,则按需创建socket,并把它的fd发布到系统中        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, si->socketcon ?: scon);            if (s >= 0) {                publish_socket(si->name, s);//发布socket的fd            }        }        freecon(scon);        scon = NULL;        if (svc->writepid_files_) {            std::string pid_str = android::base::StringPrintf("%d", pid);            for (auto& file : *svc->writepid_files_) {                if (!android::base::WriteStringToFile(pid_str, file)) {                    ERROR("couldn't write %s to %s: %s\n",                          pid_str.c_str(), file.c_str(), strerror(errno));                }            }        }        if (svc->ioprio_class != IoSchedClass_NONE) {            if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {                ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",                      getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));            }        }        if (needs_console) {//如果需要console控制台            setsid();            open_console();//打开/dev/console设备文件,将标准输入、标准输出、标准错误流定位到该设备文件        } else {            zap_stdio();//否则还是将标准输入、标准输出、标准错误定位到/dev/null设备文件上        }        if (false) {            for (size_t n = 0; svc->args[n]; n++) {                INFO("args[%zu] = '%s'\n", n, svc->args[n]);            }            for (size_t n = 0; ENV[n]; n++) {                INFO("env[%zu] = '%s'\n", n, ENV[n]);            }        }        setpgid(0, getpid());        // As requested, set our gid, supplemental gids, and uid.        if (svc->gid) {            if (setgid(svc->gid) != 0) {                ERROR("setgid failed: %s\n", strerror(errno));                _exit(127);            }        }        if (svc->nr_supp_gids) {            if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) {                ERROR("setgroups failed: %s\n", strerror(errno));                _exit(127);            }        }        if (svc->uid) {            if (setuid(svc->uid) != 0) {                ERROR("setuid failed: %s\n", strerror(errno));                _exit(127);            }        }        if (svc->seclabel) {            if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) {                ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));                _exit(127);            }        }        if (!dynamic_args) {            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {                ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));            }        } else {            char *arg_ptrs[INIT_PARSER_MAXARGS+1];            int arg_idx = svc->nargs;            char *tmp = strdup(dynamic_args);            char *next = tmp;            char *bword;            /* Copy the static arguments */            memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));            while((bword = strsep(&next, " "))) {                arg_ptrs[arg_idx++] = bword;                if (arg_idx == INIT_PARSER_MAXARGS)                    break;            }            arg_ptrs[arg_idx] = NULL;//execve()用来执行参数filename字符串所代表的文件路径,第二个参数是利用指针数组来传递给执行文件,并且//需要以空指针(NULL)结束,最后一个参数则为传递给执行文件的新环境变量数组。            execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);//在子进程中启动我们在init.rc中指定的应用程序        }        _exit(127);    }    freecon(scon);    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");//将服务的执行结果写入到init.svc.属性中,供别处获取}
restart_processes()函数会检查service_list中的每一项服务,凡是带有SVC_RESTARTING标志的,都会去调用restart_service_if_needed()函数。

restart_service_if_needed()函数又会调用servcie_start()函数来启动服务。service_start()函数中会为该服务fork()一个新的进程,如果该服务声明了socket,同时也会帮它创建一个socket且进行bind,并将该socket的fd以键值对的形式发布到系统中:ANDROID_SOCKET_"socket_name" = "socket_fd";好让别处能有途径获取到这个创建的socket的文件描述符并使用它。最后,在子进程中,传入在.rc文件中配置的参数,并调用execve()函数去执行该服务对应的主程序;那么这个服务就已经启动了。最后会将服务的启动结果写入到init.svc.属性中:

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

再执行一个命令和启动了所有的服务进程后,Init进程会开启epoll轮询(epoll_wait()),等待受关注的事件的发生(signal、property和keychord):

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);//bootchart是一个用可视化方式对启动过程进行性能分析的工具;需要定时唤醒进程        epoll_event ev;        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));//开始轮询,epoll_wait()等待事件产生        if (nr == -1) {            ERROR("epoll_wait failed: %s\n", strerror(errno));        } else if (nr == 1) {            ((void (*)()) ev.data.ptr)();//调用epoll_event事件存储的函数指针处理事件        }
epoll可以设置等待超时的时间,参数为-1表示无限等待,参数为0表示立即返回,参数为正值表示要等待的时间。代码中,timeout的初始值为-1。如果还有服务需要启动,则会把timeout设置为下次启动服务的时间;process_needs_restart在restart_service_if_needed()中有设置动作。如果action_queue中还有action需要执行,则会将timeout置0。

需要注意的是,Init进程并不是把命令列表中的命令一次执行完,而是和epoll_wait()交替执行。这里主要的考虑执行完所有命令太耗时,如果这期间有事件到来,处理就会耽搁。因此,每执行一条列表中的Command,就检查一次epll的事件。根据前面介绍的向epoll_fd注册需要监听的fd部分的内容,当有事件到来时,epoll_wait()接收事件,就会相应的调用我们注册的事件处理函数来处理事件。


分析到这里,我们只看到了某个服务退出、但需要重新启动的过程,而没有看到一开始启动服务的过程,这是怎么回事呢?其实,init.rc中定义的服务要启动,是靠class_start这个关键字来实现的:class_start :启动所有指定服务名称下的未运行服务;由前面的介绍可知,虽然这里它是关键字,但实际上它代表了一个函数操作。

在keywords.h这个文件中定义了class_startt关键字的对应的函数:

#ifndef KEYWORD...int do_class_start(int nargs, char **args);...enum {    ...    KEYWORD(class_start, COMMAND, 1, do_class_start)    ...};...
从文件可以看出,class_start 指令对应的函数是do_class_start(),它会启动一个不带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);//这里的args[1]就是init.rc中定义的class_start 的参数名称,如果main、core等    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);        }    }}static void service_start_if_not_disabled(struct service *svc){    if (!(svc->flags & SVC_DISABLED)) {//启动一个不带disabled标志的服务        service_start(svc, NULL);    } else {        svc->flags |= SVC_DISABLED_START;    }}

init.rc中class关键字定义了三个分类:core(核心服务,该服务如果不启动会影响系统的运行)、main(基础服务,这些服务保障Android的正常运行)、later_start(可以晚些启动的服务)。

我们以启动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    writepid /dev/cpuset/foreground/tasks
其中
class main

就是给Zygote服务制定了一个名字;class关键字的描述为:class :给Service指定一个名字。所有同名字的服务可以同时启动和停止。如果不通过class选项指定一个名字,则默认是“default”。从这就可以看出class 关键字跟class_start 关键字之间的联系了;比如init.rc中,如果检测到设备不是加密设备,则"class_start main"这个Action就会执行,它就将启动定义中所有通过"class main"指定了名称的服务,其中就包括Zygote。

关于Android设备加密的内容可以参看邓大大的文章:

http://blog.csdn.net/innost/article/details/44519775;讲解的很透彻。

do_class_start()函数最终是调用service_start()方法去启动一个服务,这个函数之前已经分析过了。
总之,当init.rc中执行了class_start 语句,它就会去启动所有通过class指定了"serviceclass"且不带disabled标志的服务;这就是init.rc中启动服务的方式。

待续......























更多相关文章

  1. 箭头函数的基础使用
  2. Python技巧匿名函数、回调函数和高阶函数
  3. Android应用程序进程启动过程的源代码分析(1)
  4. Android应用程序注册广播接收器(registerReceiver)的过程分析
  5. android内存处理机制
  6. Android应用程序注册广播接收器(registerReceiver)的过程分析
  7. Android(安卓)无线接口层RIL(Radio Layer Interface)
  8. android 面试(二)
  9. Android(安卓)事件分发机制总结

随机推荐

  1. android camera
  2. android 进制转换 (高低位)
  3. 如何进行Android单元测试
  4. Android全屏设置的两种方式
  5. Android(安卓)使用 HTTPS SSLProtocolExc
  6. 【Android(安卓)M】Monkey命令源码及是否
  7. 【转】Android(安卓)--权限大全
  8. 【Android(安卓)M】Monkey命令源码及是否
  9. android fragment ,listview 嵌套横向Hori
  10. Android(安卓)TextView背景色、圆角、内