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