目录
作为本书的第 2 章,可能你会觉得诧异,为何在书的开始就要介绍 Android 系统启动呢?这里有必要说明一下, Android 系统启动与本书后续很多内容都有关联,比如应用进程启动流程、四大组件原理、 AMS 、 ClassLoader 等,而 ClassLoader 又是热修复和插件化的基础,可见 Android 系统启动是十分重要并且需要首先学习的知识点,这里也建议大家先阅读完本章的内容后再去阅读后面的章节。本章简要介绍 Android 系统启动的流程,不会拘泥于源码细节,旨在让读者了解大概的流程。另外,需要提醒大家注意的一点是,阅读本章需要有一定的 CIC++基础 。
第2章共有6个小节的内容:
- 1、init进程启动过程
- 2、Zygote进程启动过程
- 3、SystemServer处理过程
- 4、Launcher启动过程
- 5、Android系统启动流程
- 6、本章小节
2.1、init 进程启动过程
init 进程是 Android 系统中用户空间的第一个进程,进程号为 1 ,是 Android 系统启动流程中一个关键的步骤,作为第一个进程,它被赋予了很多极其重要的工作职责,比如创建 Zygote (孵化器)和属性服务等。 init 进程是由多个源文件共同组成的,这些文件位于源码目录 system/core/init 中。
2.1.1、引入init 进程
为了讲解 init 进程,首先要了解 Android 系统启动流程的前几步,以引入 init 进程。
1、启动电源以及系统启动
当电源按下时引导芯片代码从预定义的地方(固化在 ROM)开始执行。加载引导程序BootLoader 到RAM 中,然后执行。
2、引导程序 Bootloader
引导程序 BootLoader 是在 Android 操作系统开始运行前的一个小程序,它的主要作用是把系统 OS 拉起来并运行。
3、Linux 内核启动
当内核启动时,设置缓存、被保护存储器、计划列表、加载驱动。在内核完成系统设置后,它首先在系统文件中寻找 init.rc 文件,井启动 init 进程。
4、init 进程启动
init 进程做的工作比较多 ,主要用来初始化和启动属性服务,也用来启动 Zygote 进程。
从上面的步骤可以看出, 当我们按下启动电源时,系统启动后会加载引导程序, 引导程序又启动 Linux 内核,在 Linux 内核加载完成后,第一件事就是要启动 init 进程。关于Android 系统启动的完整流程会在本章的 2 . 5 节进行讲解,这一节的任务就是先了解 init 进程的启动过程。
2.1.2、init 进程的入口函数
在 Linux 内核加载完成后, 它首先在系统文件中寻找 init.rc 文件, 并启动 init 进程,然后查看 init 进程的人口函数 main , 代码如下所示 :
system/core/init/init.cpp
init 的 main 函数做了很 多事情,比较复杂,我们只需关注主要的几点就可以了。在开始的时候创建和挂载启动所需的文件目录,其中挂载了 tmpfs 、 devpts 、 proc 、 sysfs 和 selinuxfs共 5 种文件系统,这些都是系统运行时目录,顾名思义,只在系统运行时才会存在,系统停止时会消失。
在注释 l 处调用 prope吗1_init 函数来对属性进行初始化,并在注释 3 处调用start_property service 函数启动属性服务,关于属性服务,后面会讲到。在注释 2 处调用signal_handler_init 函数用于设 置子进程信号处理函数,它被定义在 system/core/init/
signal_handler.cpp 中,主要用于防止 init 进程的子进程成为僵尸进程, 为了防止僵尸进程的出现,系统会在子进程暂停和终止的时候发出 SIGCHLD 信号,而 signal_handler_init 函数就是用来接收 SIGCHLD 信号的(其内部只处理进程终止的 SIGCHLD 信号)。
假设 init 进程的子进程 Zygote 终止了, signal_handler_init 函数内部会调用 handle_signal函数,经过层层的函数调用和处理,最终会找到 Zygote 进程井移除所有的 Zygote 进程的信息,再重启 Zygote 服务的启动脚本(比如 init.zygote64.rc )中带有 0町estart 选项的服务,关于 init.zygote64 .re 后面会讲到,至于 Zygote 进程本身会在注释 5 处被重启。这里只是拿Zygote 进程举个例子,其他 init 进程子进程的原理也是类似的 。
注释 4 处用来解析 init.rc 文件,解析 init.rc 的文件为 system/core/init/init_parse.cpp 文件,接下来我们查看 init.rc 里做了什么。
僵尸进程与危害
在 UNIX几inux 中,父进程使用 fork 创建子进程,在子进程终止之后,如果父进程并不知道子进程已经终止了,这时子进程虽然已经退出了,但是在系统进程表中还为它保留了一定的信息(比如进程号、退出状态、运行时间等),这个子进程就被称作僵尸进程。系统进程表是一项有限资惊,如果系统进程表被僵尸进程耗尽的话,系统就可能无怯创建新的进程了。
2.1.3、解析 init.rc
init.rc 是一个非常重要的配置文件,它是由 Android~)]始化语言 ( Android Init Language)编写的脚本,这种语言主要包含 5 种类型语句: Action 、 Command 、 Service 、 Option 和 Import。
init且的配置代码如下所示 :
system/core/rootdir/init.rc
on init
sysclktz 0
copy /proc/cmdline /dev/urandom
copy / default.prop /dev/urandom
on boot
ifup lo
hostname localhost
domainname localdomain
setrlimit 13 40 40
这里只截取了一部分代码。 on init 和 on boot 是 Action 类型语句 ,它的格式如下所示 :
on <trigger > [&& <trigger>]* //设置触发器
<command>
<command> //动作触发之后要执行的命令
为了分析如何创建 Zygote,我们主要查看 Service 类型语句,它的格式如下所示:
service <name> <pathname> [ <argument> ] * //<service 的名字〉〈执行程序路径〉〈传递参数〉
<option> // option 是 service 的修饰词,影响什么时候、如何启动 Service
<option>
...
需要注意的是, 在 Android 8.0 中对 init.rc 文件进行了拆分,每个服务对应一个 re 文件。我们要分析的 Zygote 启动脚本则在 init.zygote:XX.rc 中定义,这里拿 64 位处理器为例,init.zygote64.rc 的代码如下所示:
systemfcore/rootdir/init.zygote64.rc
service zygote /system/bin/app process64 - Xzygote /system/bin --zygote
--start-system-server
class main
priority - 20
user root
group root readproc
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
根据 Service 类型语句的格式我们来大概分析上面代码的意思。 Service 用于通知 init进程创建名为可gote 的进程,这个进程执行程序的路径为/system/bin/app_process64①,其后面的代码是要传给 app_process64 的参数。 class main 指的是 Zygote 的 classname 为main② ,后面会用到它。关于 Zygote 启动脚本会在本章的 2.2.2 节进行详细介绍 。 (此处标注的①、②,后续内容会引用到。〉
2.1.4、解析 Service 类型语句
init.rc 中的 Action 类型语句和 Service 类型语句都有相应的类来进行解析, Action 类型语句采用 ActionParser 来进行解析, Service 类型语句采用 ServiceParser 来进行解析,这里因为 主要分析 Zygote ,所以只介绍 ServiceParser 。 ServiceParser 的实现代码在system/core/init/service.cpp中,接下来我们来查看ServiceParser是如何解析上面提到的Service 类型语句的,会用到两个函数: 一个是 ParseSection ,它会解析 Service 的 rc 文件,比如上文讲到的 init.zygote64.rc,,ParseSection 函数主要用来搭建 Service 的架子; 另一个是ParseLineSection ,用于解析子项 。 代码如下所示:
system/core/init/service.cpp
bool ServiceParser::ParseSection(const std::vector<std::string>& args,std::string* err) {
if (args.size() < 3) { //判断 Service 是否有 name 与可执行程序
*err ="services must have a name and a program";
return false ;
}
canst std::string & name = args[l);
if (!IsValidName (name)) { //检查 Service 的 name 是否有效
*err= StringPrintf (" invalid service name '%s'", name.c_str());
return false ;
}
std::vector<std::string>str_args(args.begin() + 2, args.end());
service_ = std::make_unique<Service> (name, str_args); //1
return true ;
}
bool ServiceParser::ParseLineSection (const std::vector<std::string>& args,
const std::string & filename, int line, std: :string* err) const {
return service ? service_ -> ParseLine (args, err) : false ;
}
注释1处,根据参数,构造出一个 Service 对象,它的 classname 为 default。在解析完所有数据后,会调用 EndSection 函数 :
system/core/init/service.cpp
void ServiceParser::EndSection() {
if (service_) {
ServiceManager::Getinstance().AddService (std::move(service_));
}
}
End Section 函数中会调用 Serv i ceManager 的 AddService 函数,接着查看 AddService 函数做了什么 :
system/core/init/service.cpp
void ServiceManager::AddService(std::unique ptr<Service> service) {
Service* old_service = FindServiceByName(service->name());
if (old_service) {
LOG(ERROR) <"ignored duplicate definition of service'"<<
service -> name()<< "'";
return;
}
services_.emplace_back(std::move(service)) ; //1
}
注释 l 处的代码将 Service 对象加入 Service 链表中 。上面的 Service 解析过程总体来讲就是根据参数创建出 Service 对象,然后根据选项域的内容填充 Service 对象,最后将 Service对象加入 vector 类型的 Service 链表中。
2.1.5、init 启动 Zygote
讲完了解析 Service ,接下来该讲 init 是如何启动 Service 的,在这里主要讲解启动 Zygote这个 Service。在 Zygote 的启动脚本中,我们可知 Zygote 的 classname 为 main。在 init.rc中有如下配置代码:
system/core/rootdir/init.rc
...
on nonencrypted
exec - root / system/bin/update_verifier nonencrypted
class_start main //1
class_start late_start
...
其中 class_start 是一个 COMMAND ,对应的函数为 do_class_start。注释 1 处启动那些classname 为 main 的 Service ,从 2 .1.3 节末段的标注②处,我们知道 Zygote 的 classname就是 main ,因此 class_start main 是用来启动 Zygote 的 。do_class_start 函数在 builtins.cpp中定义,如下所示:
system/core/init/builtins.cpp
static int do_class_start(const std::vector<std::string>& args) {
ServiceManager::GetInstance().
ForEachServrIceinClass (args [1], [](Service* s) {s ->StartIfNotDisabled();});
return 0;
}
ForEachServicelnClass 函数会遍历 Service 链表,找到 classname 为 main 的 Zygote ,并执行 StartlfNotDisabled 函数 , 如下所示 :
system/core/init/service.cpp
bool Service::StartIfNotDisabled() {
if (!(flags & SVC DISABLED)) (//1
return Start() ;
} else {
flags_ | = SVC DISABLED START;
}
return true;
}
注释1处,如果 Service 没有在其对应的 re 文件中设置 disabled 选项,则会调用 Start函数启动该 Service, Zygote 对应的 init.zygote64.rc 中并没有设置 disabled 选项 ,因此我们接着来查看 Start 函数,如下所示 :
system/core/init/service.cpp
bool Service::Start () {
flags_ &= (~(SVC_DISABLED | SVC RESTARTING | SVC_RESET | SVC_RESTART |
SVC_DISABLED_START)) ;
time started_ = 0;
// 如果 Service 已经运行,则不启动
if (flags_ & SVC_RUNNING) {
return false;
}
bool needs_console = (flags_ & SVC_CONSOLE);
if (needs_console && !have_console) {
ERROR("service '%s' requires console\n", name_.c_str());
flags_ | = SVC DISABLED;
return false ;
}
//判断需要启动的 Service 的对应的执行文件是否存在,不存在则不启动该Service
struct stat sb;
if (stat(args_[0].c_str(), &sb) == -1) {
ERROR ("cannot find '%s' (%s) , disabling '%s' \n",
args_[0].c_str(), strerror (errno), name_.c_str());
flags_ |= SVC DISABLED;
return false ;
}
...
//如果子进程没有启动,则调用 fork 函数创建子进程
pid_t pid = fork();//l
//当前代码逻辑在子进程中运行
if (pid =+ 0) { //2
umask(077);
...
//调用 execve 函数,Service 子进程就会被启动
if (execve(args_[O] .c_str(), (char**) &strs[O], (char**) ENV) < 0) {//3
ERROR("cannotexecve('%s'):%s\n",args[0].c_str(), strerror(errno));
}
_exit(127);
}
...
return ture;
}
首先判断 Service 是否已经运行,如果运行则不再启动,直接返回 false。如果程序走到注释 1 处,说明子进程还没有被启动,就调用 fork 函数创建子进程,并返回 pid 值,注释2 处如果 pid 值为 0 ,则说明 当 前代码逻辑在子进程中运行。注释 3 处在子进程中 i周用 execve函数, Service 子进程就会被启动,并进入该 Service 的 main 函数中,如果该 Service 是 Zygote,从 2.1.3 节末段的标注①处我们可知 Zygote 执行程序的路径为/system/bin/app_process64,对应的文件为 app_main.cpp ,这样就会进入 app main.cpp 的 main 函数中,也就是在 Zygote的 main 函数中,代码如下:
frameworks/base/cmds/app_process/app_main. cpp
int main(int argc, char* const argv[]){
...
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit",args,zygote); //l
}else if(className){
runtime.start("com.android.internal.os.RuntimeInit",args,zygote);
}else{
fprintf(stderr,"Error: no class name or --zygote supplied.\n");
app usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
return 10 ;
}
}
从注释 l 处的代码可以得知调用 runtime 的 start 函数启动 Zygote ,至此 Zygote 就启动了。
2.1.6、属性服务
Windows 平台上有一个注册表管理器 , 注册表的 内容采用键值对的形式来记录用户、软件的一些使用信息。即使系统或者软件重启,其还是能够根据之前注册表中的记录,进行相应的初始化工作。 Android 也提供了一个类似的机制,叫作属性服务。
init 进程启动时会启动属性服务,并为其分配 内 存,用来存储这些属性, 如果需要这些属性直接读取就可以了, 在 2.1.2 节的开头部分,我们提到在 init.cpp 的 main 函数中与属性服务相关的代码有以下两行:
system/core/iniUinit.cpp
property_init();
start_property_service();
这两行代码用来初始化属性服务配置井启动属性服务。 首先我们来学习属’性服务配置的初始化和启动 。
2.1.6.1、属性服务初始化与启动
system/core/init/property_service.cpp
vroid property_init(){
if (_system_property_area_init()) {
LOG (ERROR) << "Failed to initialize property area";
exit(l) ;
}
}
_system_property_area_init 函数用来初始化属性内存区域。接下来查看 start_property_service 函数的具体代码:
system/core/init/prope时y_service . cpp
void start_property_service(){
property_set ("ro.property_service.version", "2");
property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC
| SOCK_NONBLOCK, 0666, 0, 0, NULL) ; //l
if (property_set_fd == -1) {
PLOG(ERROR)<<"start_property_service socket creation failed";
exit (1);
}
listen(property_set_fd, 8) ; //2
register_epoll_handler(property_set_fd , handle_property_set_fd) ; //3
}
在注释 1 处创建非阻塞的 Socket。 在注释 2 处调用 listen 函数对 property_set_fd进行监听,这样创建的 Socket 就成为 server,也就是属性服务; listen 函数的第二个参数设置为 8,意味着属性服务最多可以同时为 8 个试图设置属性的用户提供服务。注释 3 处的代码将property_set_fd放入了epoll 中,用 epoll 来监听 property_set_fd :当 property_set_fd 中有数据到来时, init 进程将调用 handle_property_set_fd 函数进行处理。
在 Linux 新的内核中, epoll 用来替换 select, epo11 是 Linux 内核为处理大批量文件描述符而做了改进的 poll ,是 Linux 下多路复用 I/O 接口 select/poll 的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统 CPU 利用率。 epoll 内部用于保存事件的数据类型是红黑树, 查找速度快, select 采用的数组保存信息,查找速度很慢,只有当等待少量文件描述符时, epoll 和 select 的效率才会差不多。
2.1.6.2、服务处理客户端请求
从上面我们得知, 属性服务接收到客户端的请求时,会调用 handle_property_set_fd函数进行处理:
system/core/init/property_service.cpp
static void handle_property_set_fd() {
...
Switch (cmd) {
case PROP MSG SETPROP : {
char prop_name[PROP_NAME_MAX];
char prop_value[PROP_VALUE_MAX];
//如果 Socket 读取不到属性数据则返回
if (!socket.RecvChars (prop_name, PROP_NAME_MAX, &timeout ms) ||
!socket.RecvChars(prop_value, PROP_VALUE_MAX , &timeout ms )) {
PLOG(ERROR) << "sys_prop (PROP_MSG_SETPROP):error while reading name/value
from the socket";
return ;
}
prop_name[PROP_NAME_MAX - 1] = 0 ;
prop_value[PROP_VALUE_MAX-1] = 0;
handle_property_set(socket , prop_value, prop_value, true);//l
break;
}
...
}
}
Android 7.0 中只用 handle_property_set_fd函数来处理客户端请求 , Android 8.0 的源码中则增加了注释 1 处的 handle_property_ set 函数做进一步封装处理, 如下所示:
system/core/init/property_seNice.cpp
static void handle_property_set(SocketConnection& socket,
const std::string& name,
const std::string& value ,
bool legacy_protocol) {
...
//如果属性名称以“ctl. ”开头,说明是控制属性
if (android::base::StartsWith(name, "ctl.")) {//1
//检查客户端权限
if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
//设置控制属性
handle_control_message(name.c_str() + 4, value.c_str ()); //2
if (!legacy_protocol){
socket.SendUint32(PROP_SUCCESS);
}
}else{
...
}
}else{
//该分支是普通属性
//检查客户端权限
if (check_mac_perms(name,source_ctx,&cr)) {
uint32_t result = property_set(name, value); //3
if ( !legacy protocol) {
socket . SendUint32 (result) ;
}
}else{
LOG(ERROR)<< sys_prop("<< cmd_name <<"): permission denied uid:" << cr.uid
<<"name:"<<name;
if (!legacy_protocol) {
socket.SendUint32 (PROP_ERROR_PERMISSION_DENIED);
}
}
}
freecon(source_ctx) ;
}
系统属性分为两种类型 : 一种是普通属性;还有一种是控制属性,控制属性用来执行一些命令 ,比如开机的动画就使用了这种属性。因此, handle_property_ set 函数分为了两个处理分支,一部分处理控制属性,另一部分用于处理普通属性,这里只分析处理普通属性。如果注释1处的属性名称以“ ctl.”开头,就说明是控制属性,如果客户端权限满足,则会调用 handle_control_message 函数来修改控制属性。如果是普通属性,则会在客户端权限满足的条件下调用注释 3 处的 property_set 函数来对普通属性进行修改,如下所示:
system/core/init/property_service . cpp
uint32_t property_set(const std::string& name , const std::string& value ) {
size_t valuelen = value . size() ;
//判断属性是否合法
if (!is_legal_property_name(name)) {
LOG(ERROR)<<"property_set(\""<< name <<"\", \""<< value <<"\") failed:
bad name";
return PROP_ERROR_INVALID_NAME;
}
...
//从属性存储空间查找该属性
prop_info* pi = (prop_info*) _system_property_find(name.c_str()); //1
//如果属性存在
if (pi != nullptr) {
//如果属性名称以 "ro." 开头,则表示只读,不能修改,直接返回
if (android::base::StartsWith(name, "ro.")) {
LOG(ERROR)<<"property_set(\""<< name << "\", \""<< value <<"\") failed:"
<< "property already set”;
return PROP_ERROR_READ_ONLY_PROPERTY;
}
//如果属性不存在,则添加该属性
int rc = _system_property_add(name.c_str(),name.size(),value.c_str(),
valuelen);
if (rc < 0){
LOG(ERROR) << "property_set (\""<<name<<"\",\""<< value < "\") failed: "
<<" _system_property_add_failed”;
return PROP_ERROR_SET_FAILED;
}
}
// 属性名称以 “persist. ”开头的处理部分
if (persistent_properties loaded && android::base::StartsWith(name,"persist.")) {
write_persistent_property(name.c_str(), value.c_str());
}
property_changed(name,value);
return PROP_SUCCESS;
}
property_set 函数主要对普通属性进行修改,首先要判断该属性是否合法,如果合法就在注释1处从属性存储控件中查找该属性,如果属性存在,就更新属性值,否则就添加该属性。另外,还对名称以“ro” “persist”开头的属性进行了相应的处理。
2.1.7、init进程启动总结
init 进程启动做了很多的工作,总的来说主要做了以下三件事:
- (1) 创建和挂载启动所需的文件目 录。
- (2 ) 初始化和启动属性服务。
- (3 ) 解析 init.rc 配置文件并启动 Zygote 进程。
2.2、Zygote进程启动过程
在 上一小节节中我们学习了 init 进程启动过程,在启动过程中主要做了三件事,其中一件就是创建了 Zygote 进程,本节接着学习 Zygote 进程启动过程, 首先我们要了解 Zygote 是什么。
2.2.1、Zygote概述
在 Android 系统中, DVM (Dalvik 虚拟机)和 ART 、 应用程序进程以及运行系统的关键服务的 SystemServer 进程都是由 Zygote 进程来创建的,我们也将它称为孵化器。它通过fock ( 复制进程)的形式来创建应用程序进程和 SystemServer 进程,由于 Zygote 进程在启动时会创建 DVM 或者 ART ,因此通过 fock 而创建的应用程序进程和 SystemServer 进程可以在内部获取一个 DVM 或者 ART 的实例副本 。
我们已经知道 Zygote 进程是在 init 进程启动时创建的,起初 Zygote 进程的名称并不是叫 “zygote”,而是叫"app_process”,这个名称是在 Android .mk 中定义的, Zygote 进程启动后, Linux 系统下的 pctrl 系统会调用 app_process ,将其名称换成了“ zygote”。
2.2.2、Zygote启动脚本
在 init.rc 文件中采用了 Import 类型语句来引人 Zygote 启动脚本,这些启动脚本都是由Android 初始化语言 ( Android Init Language )来编写的:
import /init.${ro.zygote}.rc
可以看出 init.rc 不会直接引人一个固定的文件,而是根据属性 ro.zygote 的内容来引人不同的文件 。
从 Android 5.0 开始, Android 开始支持 64 位程序, Zygote 也就有了 32 位和 64 位的区别,所以在这里用 ro.zygote 属性来控制使用不同的 Zygote 启动脚本,从而也就启动了不同版本的 Zygote 进程, ro.zygote 属性的取值有以下 4 种:
- init.zygote32.rc
- init.zygote32_64.rc
- init.zygote64.rc
- init.zygote64_32.rc
这些 Zygote 启动脚本都放在 system/core/rootdir 目录中,为了更好地分析这些 Zygote启动脚本,我们先来回顾一下 2.1. 3 节所提到的 Android 初始化语言的 Service 类型语句,它的格式如下所示 :
service <name> <pathname> [<argument>]* //<service 的名字〉〈执行程序路径〉〈传递参数〉
<option> //option 是 Service 的修饰词,影响什么时候、如何启动Service
<optiton>
...
了解了 Service 类型语句的格式,下面分别介绍这些 Zygote 启动脚本 。
2.2.2.1、init.zygote32.rc
表示支持纯 32 位程序, init.zygote32.rc 文件内容如下所示:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote
--start-system- server
class main
priority -20
user root
group root readproc
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
根据 Service 类型语句的格式,可以得知 Zygote 进程名称为zygote ,执行程序为app_process, classname 为 main ,如果 audioserver、 cameraserver、 media 等进程终止了,就需要进行 restart (重启 )。
2.2.2.2、init.zygote32_64.rc
表示既支持 32 位程序也支持 64 位程序, init.zygote32_ 64.rc 文件的内容如下所示:
service zygote /system/bin/app_process32 -Xzygote /system/bin
--zygote --start-system-server --socket-name=zygote
class main
priority -20
user root
group root readproc
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
...
service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin
--zygote --socket-name=zygote_secondary
class main
priority -20
user root
group root readproc
socket zygote_secondary stream 660 root sysytem
onrestart restart zygote
writepid /dev/cpuset/froeground/tasks
脚本中有两个 Service 类型语句,说明会启动两个 Zygote 进程, 一个名称为 zygote,执行程序为 app_process32 ,作为主模式 ;另一个名称为 zygote secondary,执行程序为app_process64 ,作为辅模式。
剩余的 init.zygote64.rc 和 init.zygote64_32.rc 与上面讲到的 Zygote 启动脚本类似,这里就不再赘述了,我们在 2.1.3 节分析的 Zygote 启动脚本就是支持 64 位程序的 init.zygote64.rc 。
2.2.3、Zygote进程启动过程介绍
从 2.1.5 节中可知 init 启动 Zygote 时主要是调用app-main.cpp 的 main 函数中的AppRuntime 的 start 方法来启动 Zygote 进程的,我们就先从 app_main .cpp 的 main 函数开始分析, Zygote 进程启动过程的时序图如图 2-1 所示。
frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[]){
...
while(i < argc){
const char* arg = argv[i++];
if (strcmp(arg, "一zygote") == 0) {//l
//如果当前运行在 Zygote 进程中,则将 zygote 设置为 true
zygote= true;//2
niceName = ZYGOTE_NICE_NAME ;
}else if(strcmp(arg,"--tart-system-server") == 0){//3
//如果当前运行在 SystemServer 进程中,则将 startSystemServer 设置为 true
startSystemServer= true;//4
}
...
}
...
if (!niceName.isEmpty()){
runtime.setArgv0(niceName.string(), true /* setProcName */);
}
//如果运行在 Zygote 进程中
if (zygote) {//5
runtime.start ("com.android.internal.os.Zygoteinit", args, zygote) ;//6
}else if(className) {
runtime.start ("com.android.internal.os.Runtimeinit", args, zygote);
}else{
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL ("app_process:no class name or --zygote supplied.");
}
}
Zygote 进程都是通过 fock 自身来创建子进程的,这样 Zygote 进程以及它的子进程都可以进入 app_ main.cpp 的 main 函数 , 因此 main 函数中为了 区分当前运行在哪个进程,会在注释 1 处判断参数 arg 中是否包含了 "--zygote” ,如果包含了则说明 main 函数是运行在Zygote 进程中的并在注释 2 处将 zygote 设置为 ture 。 同理在注释 3 处判断参数 arg 中是否包含了“--start-system-server”,如果包含 了则说明 main 函数是运行在 SystemServer 进程中的并在注释 4 处将 startSystemServer 设置为 true 。
在注释 5 处,如果zygote 为 true ,就说明当前运行在 Zygote 进程中,就会调用注释 6处的 AppRuntime 的 start 函数 ,如下所示 :
frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start (const char* className ,const Vector<String8>& options ,
bool zygote){
...
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
// 启动 Java 虚拟机
if (startVm(&mJavaVM, &env, zygote) != 0) {//l
return ;
}
onVmCreated (env);
// 为 Java 虚拟机注册 JNI 方法
if (startReg (env) < 0) {//2
ALOGE ("Unable to register all android natives\n");
return;
}
...
//从 app ma 工n 的 ma工口函数得知 className 为 com.android. 工nternal.os . Zygoteinit
classNarneStr = env->NewStringUTF(className) ; //3
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
for (size_ t i = 0; i < options.size(); ++i) {
jstring optionsStr = env -> NewStringUTF(options.itermAt(i).string()) ;
assert(optionsStr != NULL);
env - >SetObjectArrayElement(strArray, i + 1 , optionsStr);
}
//将 className 的 "."替换为"/"
char* slashClassNarne = toSlashClassNarne(className) ; //4
//找到 Zygoteinit
jclass startClass = env- >FindClass(slashClassNarne) ; //5
if (startClass == NULL) {
ALOGE ("JavaVM unable to locate class '%s\n', slashClassName) ;
} else {
//找到 ZygoteInit 的 main 方法
jmethodID startMeth = env->GetStaticMethodID(startClass,"main‘,
"([Ljava/lang/String;)V");//6
if (startMeth == NULL ) {
ALOGE(" JavaVM unable to find main() in '%s'\n", className) ;
/* keep going */
} else {
//通过 JNI 调用 Zygoteinit 的 main 方法
env->CallStaticVoidMethod(startClass, startMeth, strArray) ; //7
}
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
在注释1处调用 startVm 函数来创建 Java 虚拟机,在注释 2 处调用 startReg 函数为 Java虚拟机注册 JNI 方法。注释 3 处的 className 的值是传进来的参数 , 它的值为 com.android.intemal.os.Zygotelnit 。 在注释 4 处通过 toSlashClassName 函数 ,将 className 的"." 替换为"/",替换后的值为 com/android/intemal/os/Zygotelnit , 并赋值给 slashClassName ,接着在注释 5 处根据 slashClassName 找到 Zygotelnit,找到了 Zygotelnit 后顺理成章地在注释 6 处找到 Zygotelnit 的 main 方法。最终会在注释 7 处通过 JNI 调用 Zygotelnit 的 main 方法。 这里为何要使用JNI 呢?因为 Zygotelnit 的 main 方法是由 Java 语言编写的,当前的运行逻辑在 Native 中,这就需要通过 JNI 来调用 Java。这样 Zygote 就从 Native 层进入了 Java 框架层。
在我们通过 JNI 调用 Zygotelnit 的 main 方怯后, Zygote 便进入了 Java 框架层, 此前是没有任何代码进入 Java 框架层的,换句话说是 Zygote 开创了 Java 框架层。该 main 方法代码如下:
frameworks/base/core/java/com/android/internal/os/Zygotelnit.java
public stati c void main(String argv[]) {
...
try {
...
//创建一个 Server 端的 Socket , socketName 的值为"zygote"
zygoteServer.registerServerSocket(socketName);//1
if (!enableLazyPreload) {
bootTimingsTraceLog.traceBegin("ZygotePreload");
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
//预加载类和资源
preload(bootTimingsTraceLog);//2
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
bootTimingsTraceLog.traceEnd();
}else{
Zygote.resetNicePriority();
}
...
if (startSystemServer) {
//启动 SystemServer 进程
startSystemServer(abiList,socketName,zygoteServer);//3
}
Log.i(TAG, "Accepting command socket connections");
//等待 AMS 请求
zygoteServer.runSelectLoop(abiList);//4
zygoteServer . closeServerSocket();
}catch (Zygote.MethodAndArgsCaller caller) {
caller.run () ;
}catch (Throwable ex) {
Log.e(TAG,"System zygote died with exception", ex);
zygoteServer.closeServerSocket();
throw ex ;
}
}
在注释1处通过 registerServerSocket 方法来创建一个 Server 端的 Socket,这个 name为“ zygote”的 Socket 用于等待 ActivityManagerService 请求 Zygote 来创建新的应用程序进程,关于 AMS 将在第 6 章进行介绍。在注释 2 处预加载类和资源。在注释 3 处启动SystemServer 进程,这样系统的服务也会由 SystemServer 进程启动起来。在注释 4 处调用ZygoteServer 的 runSelectLoop 方法来等待 AMS 请求创建新的应用程序进程。由此得知,Zygotelnit 的 main 方毡主要做了 4 件事:
- (1)创建一个 Server 端的 Socket 。
- (2)预加载类和资掘。
- (3)启动 SystemServer 进程。
- (4)等待 AMS 请求创建新的应用程序进程。
其中第二件事预加载类和资源这里不做介绍,有兴趣的读者可以查看摞码,这里会对其他的主要事件进行分析。
2.2.3.1、registerZygoteSocket
首先我们来查看 ZygoteServer 的 registerZygoteSocket 方怯做了什么,如下所示:
frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
void registerServerSocket(String socketName) {
if (mServerSocket == null) {
int f ileDesc ;
//拼接 Socket 的名称
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName ; //l
try {
//得到 Socket 的环境变量的值
String env = System.getenv(fullSocketName); //2
//将 Socket 环境变量的值转换为文件描述符的参数
fileDesc = Integer.parseint (env); //3
} catch (RuntimeException ex ) {
throw new RuntimeException {fullSocketName + "unset or invalid", ex) ;
}
try {
//创建文件描述符
FileDescriptor fd = new FileDescriptor();//4
fd.setint$(fileDesc); //5
//创建服务器端 Socket
mServerSocket = new LocalServerSocket(fd);//6
}catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex );
}
}
在注释1处拼接 Socket 的名称,其中ANDROID_SOCKET_PREFIX的值为"ANDROID_SOCKET_”, socketName 的值是传进来的值,等于"zygote”,因此 fullSocketName 的值为“ ANDROID_SOCKET_zygote” 。在注释 2 处将 fullSocketName 转换为环境变量的值, 再在注释 3 处转换为文件描述符的参数。在注释 4 处创建文件描述符,并在注释 5 处传入此前转换的文件操作符参数。在注释 6 处创建 LocalServerSocket,也就是服务器端的 Socket,并将文件操作符作为参数传进去。在 Zygote 进程将 SystemServer 进程启动后,就会在这个服务器端的 Socket 上等待 AMS 请求 Zygote 进程来创建新的应用程序进程。
2.2.3.2、启动 SystemServer 进程
接下来查看 startSystemServer 函数 , 代码如下所示:
frameworks/base/core/java/com/android/internal/os/Zygotelnit.java
private static boolean startSystemServer (String abiList , String socketName ,
ZygoteServer zygoteServer)throws Zygote . MethodAndArgsCaller , RuntimeException {
...
//创建 args 数组,这个数组用来保存启动 SystemServer 的启动参数
/*l*/
String args[] = {
"--setuid=lO00",
"--setgid=lOO0",
"--setgroups=lOOl, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1018 ,
1021, 1023, 1032, 3001, 3002, 3003, 3006, 3007, 3009, 3010 ",
"--capabilities=" + capabilities + ”,” + capabilities ,
"--nice-name=system server",
"--runtirne-args",
" com.android.server.SystemServer"
};
ZygoteConnection.Arguments parsedArgs = null ;
int pid;
try{
parsedArgs = new ZygoteConnection.Arguments(args); //2
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
ZygoteConnection.applyinvokeWithSystemProperty(parsedArgs);
/*** 3 创建一个子进程,也就是 SystemServer 进程*/
pid = Zygote.forkSysternServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs . gids ,
parsedArgs . debugFlags ,
null ,
parsedArgs . permittedCapabilities ,
parsedArgs . effectiveCapabilities);
}catch (IllegalArgumentException ex) {
throw new RuntimeException (ex);
}
//当前代码逻辑运行在子进程中
if(pid == 0){
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
zygoteServer.closeServerSocket();
//处理 SystemServer 进程
handleSystemServerProcess(parsedArgs);//4
}
return true;
}
注释 l 处的代码用来创建 args 数组,这个数组用来保存启动 SystemServer 的启动参数,其中可以看出 SystemServer 进程的用户 id 和用户组 id 被设置为 1000 ,并且拥有用户组1001 ~ 1010 、 1018 、 1021 、 1032 、 3001 ~3010 的权限;进程名为 system_server ;启动的类名为 com.android.server.SystemServer。在注释 2 处将 args 数组封装成 Arguments 对象并供注
释 3 处的 forkSystemServer 函数调用 。在注释 3 处调用 Zygote 的 forkSystemServer 方法,其内部会调用 nativeForkSystemServer 这个 Native 方法,nativeForkSystemServer 方法最终会通过 fork 函数在 当前进程 创 建 一 个子进程,也就是 SystemServer 进程,如果forkSystemServer 方陆返回的 pid 的值为 0 ,就表示当前的代码运行在新创建的子进程中,则执行注释 4 处的 handleSystemServerProcess 来处理 SystemServer 进程,关于 SystemServer进程启动会在 2.3 节进行介绍 。
2.2.3.3、runSelectloop
启动 Systerr巾rver 进程后,会执行 ZygoteServer 的 runSelectLoop 方法,如下所示:
frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
void runSelectLoop(Sting abiList) throws Zygote.MethodAndArgsCaller {
ArrayList<FileDescriptor> fds =new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
fds.add(mSe凹erSocket.getFileDescriptor()) ;//1
peers.add(null);
//无限循环等待 AMS 的请求
while (true) {
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for(int i = 0; i < pollFds.length; ++i) {//2
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds .get(i) ;
pollFds[i].events = (short) POLLIN;
}
try {
Os.poll(pollFds, -1);
}catch(ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
for (int i = pollFds.length - 1; i >= 0; --i) {//3
continue ;
}
if(i == 0){
ZygoteConnection newPeer = acceptCommandPeer(abiList);//4
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
}else{
boolean done = peers.get(i).runOnce(this);//5
if (done){
peers.remove(i);
fds.remove(i);
}
}
}
}
注释 l 处的 mServerSocket 就是我们在 registerZygoteSocket 函数中创建的服务器端Socket ,调用 mServerSocket.getFileDescriptor()函数用来获得该 Socket 的创字段的值并添加到列表 fds 中。接下来无限循环用来等待 AMS 请求 Zygote 进程创建新的应用程序进程。在注释 2 处通过遍历将 fds 存储的信息转移到 pollFds 数组中。在注释 3 处对 pollFds 进行遍历,如果 i==0 ,说明服务器端 Socket 与客户端连接上了,换句话说就是,当前 Zygote进程与 AMS 建立了连接。在注释 4 处通过 acceptCommandPeer 方住得到 ZygoteConnection类并添加到 Socket 连接列表 peers 中,接着将该 ZygoteConnection 的fd 添加到 fd 列表 fds中,以便可以接收到 AMS 发送过来的请求。 如果 i 的值不等于 0 ,则说明 AMS 向 Zygote进程发送了一个创建应用进程的请求,则在注释 5 处调用 ZygoteConnection 的 runOnce 函数来创建一个新的应用程序进程,并在成功创建后将这个连接从 Socket 连接列表 peers 和 fd 列表 fds 中清除 。
2.2.4、Zygote进程启动总结
Zygote 进程启动共做了如下几件事 :
- 创建 AppRuntime 并调用其 start 方法,启动 Zygote 进程。
- 创建 Java 虚拟机并为 Java 虚拟机注册 JNI 方法 。
- 通过JNI调用 Zygotelnit 的 main 函数进入 Zygote 的 Java 框架 层。
- 通过 registerZygoteSocket 方法创建服务器端 Socket ,并通过 runSelectLoop 方法等待 AMS 的请求来创建新的应用程序进程。
- 启动 SystemServer 进程。