init 进程进入 main 函数后,主要完成以下四项工作。第一,在根文件系统中创建目录作为挂载点,然后挂载虚拟文件系统;第二, 解析启动脚本文件 init.rc;第三,根据启动脚本 init.rc 的解析结果开启 Android Framework 核心服务进程;第四,监听事件,重启服务。
1. 在根文件系统中创建目录,挂载虚拟文件系统
虚拟文件系统不会保存在外部存储设备中,开机时由内核产生。除了 tmpfs 外,这些文件系统用作内核空间和用户空间的访问接口。它们的基本信息如下表所示:

文件系统 挂载点 主要用途
tmpfs /dev 用于保存临时文件,提高设备访问效率
devpts /dev/pts 支持虚拟终端设备
proc /proc 访问内核数据结构的接口
sysfs /sys 访问系统数据的接口,特点是one-value-per-file

通过系统调用 mount 挂载虚拟文件系统,代码实现如下所示。

代码路径:system/core/init/init.cint main(int argc, char **argv){    ......    mkdir("/dev", 0755);    mkdir("/proc", 0755);    mkdir("/sys", 0755);    mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");    mkdir("/dev/pts", 0755);    mkdir("/dev/socket", 0755);    mount("devpts", "/dev/pts", "devpts", 0, NULL);    mount("proc", "/proc", "proc", 0, NULL);    mount("sysfs", "/sys", "sysfs", 0, NULL);    ......}

2. 解析启动脚本文件 init.rc
init.rc 文件在系统根目录下,包含了 Android Framework 的初始化内容,其内部会 import 其他的初始化文件,比如 init.environ.rc 文件,init.usb.rc 文件,init.{ro.hardware}.rc 文件,以及 init.${ro.zygote}.rc 文件。
init.rc 按照一种特定的语法描述,也就是 Android 初始化语言 (Android Init Language) 来描述。Android 初始化语言的帮助文档位于 system/core/init/readme.txt
Android 初始化语言有以下六个基本概念。
Action
Action 由关键字 on 来声明。Action 格式如下:

on <trigger>   <command>   <command>   ...

<trigger> 是触发条件,同时也是 Action 的名字。Action 满足触发条件时就会执行 <command>

Service
Service 由关键字 service 来声明。系统服务进程比如 servicemanager, zygote, uevent, installd 等都是一个 Service。Service 格式如下:

service <name> <pathname> [ <argument> ]*   <option>   <option>   ...

<name>表示 service 的名字,必须要指定
<pathname>表示 service 的执行路径,必须要指定
<argument>表示传给 service 的参数,可以为0个或多个
<option>表示 service 启动的配置参数

Section
Section 是 init.rc 的基本组成单元。每个Action 或者 Service 都是一个 Section。需要注意的是 Section 的名字不能重复,否则只有第一个 Section 定义有效。

Option
Option 来用修饰 Service,由它来指定何时以及如何启动 Service。比如,critical 表示一个 Service 是系统关键服务,退出后 init 进程会重启服务。如果一分钟内该服务退出四次,就会触发整个系统重启。

Trigger
Trigger 表示一个自定义的触发条件,用来触发 Action 中 Command 的执行。early-init, init, early-fs, fs, post-fs, post-fs-data, charger, early-boot , boot等都是一个 Trigger。

Command
Command 是最小功能单元,表示一个 Linux 命令或者一个方法调用。

解析 init.rc 的工作由函数init_parse_config_file 来完成。具体细节可以参考初始化文件(init.rc)解析 这篇文章。解析完成后,得到一个 service_list 链表和一个 action_list 链表。其中,service_list 链表用来保存 service 的解析结果,action_list 链表用来保存 action 的解析结果。如下图所示:

除了启动脚本文件 init.rc 可以添加 action 到 action_list 链表外,调用 queue_builtin_action 函数也可以添加 action 到 action_list 链表,同时该函数还会调用 action_add_queue_tail 将该 action 添加到 action_queue 链表末尾,以便后面执行 action 中的 command。

代码路径:system/core/init/init_parser.cvoid queue_builtin_action(int (*func)(int nargs, char **args), char *name){           struct action *act;    struct command *cmd;    act = calloc(1, sizeof(*act));    act->name = name;    list_init(&act->commands);    list_init(&act->qlist);    cmd = calloc(1, sizeof(*cmd));    cmd->func = func;    cmd->args[0] = name;    list_add_tail(&act->commands, &cmd->clist);    list_add_tail(&action_list, &act->alist);    action_add_queue_tail(act);} 

为了让 action 按照一定的顺序触发,使用 action_for_each_trigger 函数来根据 trigger 的内容(early-init -> init -> early-fs -> fs -> post-fs -> post-fs-data -> charger -> early-boot -> boot)对 action 在链表中的顺序进行调整。调整后的 action 由 action_queue 链表来保存。过程如下所示:

代码路径:system/core/init/init.cint main(int argc, char **argv){    ......    action_for_each_trigger("early-init", action_add_queue_tail);     queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");     queue_builtin_action(keychord_init_action, "keychord_init");     queue_builtin_action(console_init_action, "console_init");     /* execute all the boot actions to get us started */     action_for_each_trigger("init", action_add_queue_tail);     /* skip mounting filesystems in charger mode */     if (!is_charger) {         action_for_each_trigger("early-fs", action_add_queue_tail);         action_for_each_trigger("fs", action_add_queue_tail);         action_for_each_trigger("post-fs", action_add_queue_tail);         action_for_each_trigger("post-fs-data", action_add_queue_tail);     }     queue_builtin_action(property_service_init_action, "property_service_init");     queue_builtin_action(signal_init_action, "signal_init");     queue_builtin_action(check_startup_action, "check_startup");     if (is_charger) {         action_for_each_trigger("charger", action_add_queue_tail);     } else {         action_for_each_trigger("early-boot", action_add_queue_tail);         action_for_each_trigger("boot", action_add_queue_tail);     }     /* run all property triggers based on current state of the properties */     queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");     ......}

3. 根据启动脚本 init.rc 的解析结果开启核心服务进程
通过之前的初始化工作得到 action_queue 链表后,调用 execute_one_command 函数遍历 action_queue 链表,并执行链表中 action 的 command,实际上是调用 command 中 func 函数指针指向的处理函数。

代码路径:system/core/init/init.hstruct command {           /* list of commands in an action */    struct listnode clist;    int (*func)(int nargs, char **args);    int nargs;    char *args[1];};

下面以 boot action (boot 既是 action 的触发条件,又可以作为 action 的名称) 为例,梳理 zygote 服务进程的启动流程,其他核心服务进程的启动过程也类似。

在这些启动的核心服务进程中,其中两个最重要的核心服务进程是 zygote 进程和 servicemanager 进程。

zygote 进程是 Android 启动后的第一个虚拟机进程。它主要负责启动 system_server 进程,以及所有的应用程序进程,所以也称为孵化器进程。zygote 进程会启动子进程 system_server,system_server 进程在 nativeInit 函数中启动 Native 系统服务比如 SensorService,在 ServerThread 的 initAndLoop 函数中启动 Java 系统服务比如 ActivityManagerService, PackageManagerService, ContentService 等,并将系统服务注册进 ServiceManager。

servicemanager 进程是 Binder 进程间通信机制的核心之一。它负责管理系统中所有的 Service 组件,提供 Service 组件的注册服务,并且向 Client 组件提供获取 Service 代理对象的服务。

以下是 boot action 在 init.rc 文件中的内容:

on boot    ......    class_start core    class_start main

在调用 init_parse_config_file 函数完成解析后,action_queue 链表中名字为 boot 的 action 包含两个 command,这两个 command 的 func 都赋值为 do_class_start,只是一个 command 的参数为 core;另一个 command 的参数为 main。当 execute_one_command 遍历到 boot 这个 action 时,依次执行这两个 command,即调用 do_class_start 函数。

代码路径:system/core/init/builtins.cint do_class_start(int nargs, char **args){    /* Starting a class does not start services * which are explicitly disabled. They must * be started individually. */    service_for_each_class(args[1], service_start_if_not_disabled);    return 0;}

do_class_start 内部调用service_for_each_class 函数遍历前面解析得到的 service_list,如果发现某个 service 的 classname 与参数 args[1] 相同,则调用 service_start_if_not_disabled 函数启动该 service。

代码路径:system/core/init/builtins.cstatic void service_start_if_not_disabled(struct service *svc){    if (!(svc->flags & SVC_DISABLED)) {        service_start(svc, NULL);    }}

zygote 在 init.rc 中的内容如下,由于 classname 为 main,和上述第二个 command 的参数相同,而且 flags 不为 SVC_DISABLED,因此系统开始调用 service_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

根据 Android 初始化语言的语法,可以知道 zygote 服务进程的名字是 zygote ,执行程序路径为 /system/bin/app_process,参数为 -Xzygote /system/bin --zygote --start-system-server。创建一个名字为 zygote 的 socket,这个 socket 用于接收 ActivityManagerService 发送过来的新建应用程序进程的请求。zygote 进程重启时会往 sysfs 中写入命令,同时会重启 media 服务和 netd 服务。

service_start 函数首先使用 fork 函数克隆出一个子进程。然后在子进程中创建 socket ,并在环境变量中设置创建的 socket 信息。最后调用系统函数 execve 开始执行 /system/bin/app_process 程序。

代码路径:system/core/init/init.cvoid service_start(struct service *svc, const char *dynamic_args){    struct stat s;    pid_t pid;    int needs_console;    int n;    char *scon = NULL;    int rc;    /* 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->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;    }    ......    pid = fork();    if (pid == 0) {        ......        for (si = svc->sockets; si; si = si->next) {            int socket_type = (                    !strcmp(si->type, "stream") ? SOCK_STREAM :                        (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));            int s = create_socket(si->name, socket_type,                                  si->perm, si->uid, si->gid);            if (s >= 0) {                publish_socket(si->name, s);            }        }        ......        if (!dynamic_args) {            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) { ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno)); } } ...... } ...... }

/system/bin/app_process 是 zygote 进程的可执行程序,其源代码位于 frameworks/base/cmds/app_process/app_main.cpp 文件,入口函数是 main。具体启动细节请参考老罗的文章 Android 系统进程 Zygote 启动过程的源代码分析。

4. 监听事件,重启服务
init 进程最终会进入一个无限循环。在这个无限循环中,init 进程调用系统函数 poll 监听三类事件。分别调用 get_property_set_fd 得到文件描述符 property_fd,调用 get_signal_fd 得到文件描述符 signal_fd ,get_keychord_fd 得到文件描述符 keychord_fd。这三个文件描述符的初始化过程之前通过 queue_builtin_action 添加进 action_queue,然后被触发执行。

queue_builtin_action(keychord_init_action, "keychord_init");queue_builtin_action(property_service_init_action, "property_service_init"); queue_builtin_action(signal_init_action, "signal_init"); 

其中,property_fd 用于监听属性设置操作,signal_fd 用于获取子进程退出信号,keychord_fd 用于监听外设的组合按键。代码如下:

代码路径:system/core/init/init.cint main(int argc, char **argv){    ......    for(;;) {        int nr, i, timeout = -1;        execute_one_command();        restart_processes();        if (!property_set_fd_init && get_property_set_fd() > 0) {            ufds[fd_count].fd = get_property_set_fd();            ufds[fd_count].events = POLLIN;            ufds[fd_count].revents = 0;            fd_count++;            property_set_fd_init = 1;        }        if (!signal_fd_init && get_signal_fd() > 0) {            ufds[fd_count].fd = get_signal_fd();            ufds[fd_count].events = POLLIN;            ufds[fd_count].revents = 0;            fd_count++;            signal_fd_init = 1;        }        if (!keychord_fd_init && get_keychord_fd() > 0) {            ufds[fd_count].fd = get_keychord_fd();            ufds[fd_count].events = POLLIN;            ufds[fd_count].revents = 0;            fd_count++;            keychord_fd_init = 1;        }        ......        nr = poll(ufds, fd_count, timeout);        if (nr <= 0)            continue;        for (i = 0; i < fd_count; i++) {            if (ufds[i].revents == POLLIN) {                if (ufds[i].fd == get_property_set_fd())                    handle_property_set_fd();                else if (ufds[i].fd == get_keychord_fd())                    handle_keychord();                else if (ufds[i].fd == get_signal_fd())                    handle_signal();            }        }    }    return 0;}

当监听到 property_fd 上发生了可读事件时,调用 handle_property_set_fd 函数处理事件。首先通过 socket 接收到客户端传过来的消息,然后根据消息类型设置属性或者启动相应的服务。

当监听到 signal_fd 上发生了可读事件时,调用 handle_signal 函数处理事件。通过 waitpid 得到子进程的 pid,对子进程做一些清理工作。然后根据子进程的启动配置参数判断是否需要重新,如果需要则设置其属性为 SVC_RESTARTING,最后在 restart_processes 中完成重启,过程如下所示:

代码路径:system/core/init/init.cstatic void restart_processes(){    process_needs_restart = 0;    service_for_each_flags(SVC_RESTARTING,                           restart_service_if_needed);}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;    }}

当监听到 keychord_fd 上发生了可读事件时,调用 handle_keychord 函数处理事件。通过系统调用 read 从 keychord_fd 上读取 id,再根据 id 启动相应的 keychord 服务。

至此,Android 启动流程第二阶段完成,下文开始分析应用程序 Launcher 的启动过程。

参考学习资料
1. Understanding The Linux Kernel(Third Edition)

更多相关文章

  1. C语言函数的递归(上)
  2. Android下结束进程的方法
  3. 【转】Android最佳性能实践(一)——合理管理内存
  4. Android(安卓)系统启动过程详解
  5. Android(安卓)Studio开发之 JNI 篇的简单示例
  6. android 导出jar包并使用
  7. Android:Service:AIDL实现进程通信
  8. Eclipse Android项目导入Android(安卓)Studio时常见的编译错误及
  9. 玩转Android---2D图形及动画---图片处理

随机推荐

  1. Android中AsyncTask的使用与源码分析+3.0
  2. Android平台上的11个感应器你都知道吗(转
  3. 在Android上使用GoogleMap开发程序
  4. NetSuite公司,准确,SYSPRO,ACCPAC,的Epicor和
  5. Android(安卓)JNI层确保只有一个进程的一
  6. 精通 Android(安卓)中的 tools 命名空间
  7. Android(安卓)中判断一个程序是否为输入
  8. C虾仔笔记 - 数据存储
  9. Android应用程序版本号管理(官方文档中文
  10. Android资源访问