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

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<char*>("--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<void*>(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<name> ("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<void*>(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 KEYWORD
int 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定义中配置的Option
static void parse_line_service(struct parse_state *state, int nargs, char **args)
{
    struct service *svc = (service*) state->context;
    struct command *cmd;
    int i, kw, kw_nargs;

    if (nargs == 0) {
        return;
    }

    svc->ioprio_class = IoSchedClass_NONE;

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

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

            if (!strcmp(args[1], "rt")) {
                svc->ioprio_class = IoSchedClass_RT;
            } else if (!strcmp(args[1], "be")) {
                svc->ioprio_class = IoSchedClass_BE;
            } else if (!strcmp(args[1], "idle")) {
                svc->ioprio_class = IoSchedClass_IDLE;
            } else {
                parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");
            }
        }
        break;
    case K_group:
        if (nargs < 2) {
            parse_error(state, "group option requires a group id\n");
        } else if (nargs > NR_SVC_SUPP_GIDS + 2) {
            parse_error(state, "group option accepts at most %d supp. groups\n",
                        NR_SVC_SUPP_GIDS);
        } else {
            int n;
            svc->gid = decode_uid(args[1]);
            for (n = 2; n < nargs; n++) {
                svc->supp_gids[n-2] = decode_uid(args[n]);
            }
            svc->nr_supp_gids = n - 2;
        }
        break;
    case K_keycodes:
        if (nargs < 2) {
            parse_error(state, "keycodes option requires atleast one keycode\n");
        } else {
            svc->keycodes = (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<std::string>;
        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;
}
//填充action
static void parse_line_action(struct parse_state* state, int nargs, char **args)
{
    struct action *act = (action*) state->context;
    int kw, n;

    if (nargs == 0) {
        return;
    }

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

    n = kw_nargs(kw);
    if (nargs < n) {
        parse_error(state, "%s requires %d %s\n", args[0], n - 1,
            n > 2 ? "arguments" : "argument");
        return;
    }
    command* cmd = (command*) malloc(sizeof(*cmd) + sizeof(char*) * nargs);
    cmd->func = kw_func(kw);
    cmd->line = state->line;
    cmd->filename = state->filename;
    cmd->nargs = nargs;
    memcpy(cmd->args, args, sizeof(char*) * nargs);
    list_add_tail(&act->commands, &cmd->clist);//将解析到的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<char*>(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.<servicename>属性中,供别处获取
}
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.<servicename>属性中:

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 <serviceclass>:启动所有指定服务名称下的未运行服务;由前面的介绍可知,虽然这里它是关键字,但实际上它代表了一个函数操作。

在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 <serviceclass>的参数名称,如果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 <name>:给Service指定一个名字。所有同名字的服务可以同时启动和停止。如果不通过class选项指定一个名字,则默认是“default”。从这就可以看出class <name>关键字跟class_start <serviceclass>关键字之间的联系了;比如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 <serviceclass>语句,它就会去启动所有通过class指定了"serviceclass"且不带disabled标志的服务;这就是init.rc中启动服务的方式。

待续......























  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值