Android笔记 - Android启动之Android Framework启动

Posted demonyan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android笔记 - Android启动之Android Framework启动相关的知识,希望对你有一定的参考价值。

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.c

int 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.c

void 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.c

int 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.h

struct 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.c

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

static 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.c

void service_start(struct service *svc, const char *dynamic_args)

    struct stat s;
    pid_t pid;
    int needs_console;
    int n;
    char *scon = NULL;
    int rc;

    /* 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.c

int 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.c

static 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)

以上是关于Android笔记 - Android启动之Android Framework启动的主要内容,如果未能解决你的问题,请参考以下文章

Android笔记 - Android启动之Android Framework启动

Android学习笔记之Intent

Android学习笔记之Intent

深入浅出Android之学习笔记

Android 进阶——系统启动之Android init.rc脚本解析

Android 进阶——系统启动之Android init.rc脚本解析