Android zygote 进程的启动过程分析
Posted 严振杰
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android zygote 进程的启动过程分析相关的知识,希望对你有一定的参考价值。
版权声明:转载必须注明本文转自严振杰的博客:http://blog.yanzhenjie.com
zygote
进程在 android 开发中属于比较底层的内容,然而要透彻的理解 AMS、WMS 和 IPC 等,不可避免的要先理解zogyte
进程,因此本文即作为对自己的学习记录,也想分享出来和遇到同样问题的读者交流。
在正式内容开始之前,我建议读者朋友下载部分源代码备用,分别是:
platform/framework/base
platform/system/core
platform/packages/apps/Launcher3
我们可以到 Android 源码托管站下载:https://android.googlesource.com/,可以下载具体某个模块源代码,可以指定分支或者 tag,需要翻阅 GFW。
另外如果你没有翻阅 GFW 的条件的话,可以使用清华大学镜像源:
https://mirrors.tuna.tsinghua.edu.cn/help/AOSP/
例如,现在我们从 Terminal 进入~/Workspance/Android
目录下,然后分别下载上述三个模块oreo-release
分支的源代码:
$ git clone -b oreo-release https://aosp.tuna.tsinghua.edu.cn/platform/frameworks/base platform_framework_base
...
$ git clone -b oreo-release https://aosp.tuna.tsinghua.edu.cn/platform/system/core platform_system_core
...
$ git clone -b oreo-release https://aosp.tuna.tsinghua.edu.cn/platform/packages/apps/Launcher3 platform_packages_apps_Launcher3
特别声明:本文出现的源代码,为了更加简洁清晰,部分代码经过大量的精简,读者阅读本文时,如果有条件,最好配合源代码,以免产生误解。
另外,纠正一个无伤大雅的小问题,我们经常说的init
进程、zygote
进程和system_server
进程,它们的名字如上所写,不是大写,也不是驼峰式写法,比如这些写法是不正确的:
Init进程,正确的应该是:init进程
Zygote进程,正确的应该是:zygote进程
SystemServer进程,正确的应该是:system_server进程
本文内容
init.rc
配置文件的作用和运行流程init.zygote.rc
配置文件的含义和作用ZygoteInit.java
初始化zygote
进程的过程ZygoteInit.java
启动system_server
进程的过程SystemServer.java
初始化system_server
进程的过程zygote
进程等待 Socket 客户端的请求
init.rc 配置文件的作用和运行流程
Android 系统是基于 Linux 内核的,Linux 系统中的进程都是 init
进程的子进程,Android 的zygote
进程也是在系统启动的过程,由init
进程创建的。本小节要分析从系统启动后代码是如何执行到 zygote
相关逻辑块的,然后由此引出zygote
的启动过程分析。
系统启动时kernel/init/main.c
文件中的kernel_init()
函数会做一些硬件相关的基础工作,比如 CPU 初始化和内存初始化等,然后会执行platform/system/core
目录下的init
程序,该程序即 Android 的init
进程对应的程序。该程序的对应代码是platform/system/core/init.cpp
,打开该文件并找到它的入口函数:
// 注意这两行引入
#include "property_service.h"
#include "service.h"
int main(int argc, char** argv)
... // 一些必要的环境变量配置和启动其他Linux进程
// 执行在头部引入的property_service.h的start_property_service()函数
start_property_service();
// 解析配置文件
Parser& parser = Parser::GetInstance();
// 解析service
parser.AddSectionParser("service",std::make_unique<ServiceParser>());
parser.AddSectionParser("on", std::make_unique<ActionParser>());
parser.AddSectionParser("import", std::make_unique<ImportParser>());
parser.ParseConfig("/init.rc");
...
ActionManager& am = ActionManager::GetInstance();
... // 配置am命令参数
while (true)
// 使用epoll机制 + 死循环,使init进程一直运行,类似Looper#loop()
// 默认一直阻塞,直到有事情需要做被唤醒
int epoll_timeout_ms = -1;
if (...)
am.ExecuteOneCommand();
// 一些赋值逻辑,可能是0
epoll_timeout_ms = ...;
// 调用epoll_wait使init进程沉睡或者...
epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms);
return 0;
我们会发现该函数中解析了位于platform/system/core/rootdir/init.rc
的初始化配置文件,我摘抄出了与zygote
进程相关的部分代码:
import /init.$ro.zygote.rc
...
# 当电源键被按住的时候执行 property
on property:sys.boot_from_charger_mode=1
class_stop charger
trigger late-init
# 挂载文件系统并启动核心系统服务
on late-init
...
# 调用zygote-start
trigger zygote-start
...
trigger early-boot
trigger boot
# 在init.rc中调用zygote-start解除zygote的启动阻塞
on zygote-start && property:ro.crypto.state=unencrypted
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start netd
start zygote
上述配置的代码类似 C 语言风格,每一个on
和后边的定义我们暂且把它叫做函数,当手机处于关机状态时,我们摁住电源键开机,init
进程执行到这里时,会调用property
函数,该函数做一些准备工作后,会调用late-init
函数,late-init
函数会做一些启动初始化相关的工作,完事以后会调用zygote-start
去执行init.zygote*.rc
,我们发现该配置文件在init.rc
文件头部被引入:
import /init.$ro.zygote.rc
该文件被引入时有个ro.zygote
变量,它可以被理解为一个环境变量,该变量的值和设备的 CPU 平台相关,同样在./rootdir
目录下的可选项有:
init.zygote32.rc
init.zygote32_64.rc
init.zygote64.rc
init.zygote64_32.rc
为了一目了然简单清晰,我们选择init.zygote32.rc
来分析,init
进程执行到这里,根据init.zygote32.rc
的描述,会 fork zygote
进程,具体细节请看下一节。
init.zygote.rc 配置文件的含义和作用
打开init.zygote32.rc
文件后我们发现它的代码很简洁清晰:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
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
,在platform/core/init/README.md
中有对init
语法的描述,service
的语法是这样的:
service <name> <pathname> [ <argument> ]*
<option>
<option>
其中对service
的解释是:service
是init
退出时启动和重新启动的程序。我认为在这里,init
会先 fork 一个进程,会把该进程命名为zygote
,然后在该进程中执行/system/bin/app_process
程序,并且带上参数--zygote -- start-system-server
。
后面的socket
是为zygote
进程创建一个名称为"zygote"的socket
资源,在系统启动后,我们可以在/dev/socket
目录下发现一个名为“zygote”的文件。在本文中,该配置被执行完后我们会做这个 Socket 资源的验证。
接下来,我们把目光集中在zygote
进程接下来要执行的/system/bin/app_process
程序上,system/bin
一般用来存放系统命令类的程序,通过分析和查找,它对应的代码在platform/frameworks/base/cmds/app_process/app_main.cpp
文件中,接下来我们详细分析该文件。
ZygoteInit.java 初始化 zygote 进程的过程
上面讲到init
进程解析init.zygote.rc
后,会 forkzygote
进程,然后在zygote
进程中执行/system/bin/app_process
程序,执行该程序的目的是初始化zygote
进程。
接下来我们打开platform/frameworks/base/cmds/app_process/app_main.cpp
文件,来分析初始化zygote
进程时做了哪些事情,目光定位在main()
函数上:
int main(int argc, char* const argv[])
// 创建 AppRuntime的成员变量
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
...
// 解析运行时参数,赋值zygote和startSystemServer
bool zygote = false;
bool startSystemServer = false;
bool application = false;
String8 niceName;
String8 className;
++i; // Skip unused "parent dir" argument.
while (i < argc)
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0)
zygote = true;
niceName = ZYGOTE_NICE_NAME;
else if (strcmp(arg, "--start-system-server") == 0)
startSystemServer = true;
else if (strcmp(arg, "--application") == 0)
application = true;
else if (strncmp(arg, "--nice-name=", 12) == 0)
niceName.setTo(arg + 12);
else if (strncmp(arg, "--", 2) != 0)
className.setTo(arg);
break;
else
--i;
break;
if (!className.isEmpty())
...
else
...
if (startSystemServer)
args.add(String8("start-system-server"));
if (zygote)
// 调用 ZygoteInit#main()
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
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.");
上述代码,首先创建了一个AppRuntime
的局部成员变量,然后对入参进行了解析,根据对init.zygote32.rc
传入参数的分析,这里的zygote
变量和startSystemServer
的值均为true
,因此在最后调用AppRuntime#start()
函数时走了:
if (zygote)
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
AppRuntime
类也定义在platform/frameworks/base/cmds/app_process/app_main.cpp
中:
class AppRuntime : public AndroidRuntime
...
;
发现AppRuntime
类继承于AndroidRuntime
,而AppRuntime
类中是没有start()
函数的,因此回到上方的main()
函数中,实际上当执行AppRuntime#start()
时实际上是执行了AndroidRuntime#start()
,下面我们来看看AndroidRuntime
类,它位于platform/frameworks/base/core/jni/AndroidRuntime.cpp
:
void AndroidRuntime::start(const char* className, ..., bool zygote)
static const String8 startSystemServer("start-system-server");
...
/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
startVm(&mJavaVM, &env, zygote);
onVmCreated(env);
/*
* Register android functions.
*/
startReg(env);
/*
* We want to call main() with a String array with arguments in it.
*/
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
stringClass = env->FindClass("java/lang/String");
strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
char* slashClassName = toSlashClassName(className);
jclass startClass = env->FindClass(slashClassName);
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
env->CallStaticVoidMethod(startClass, startMeth, strArray);
...
根据AndroidRuntime
名称和上述代码可以分析得出,该函数启动了 Android 系统运行时库,其中它主要做了三件事:
- 调用
startVmVM()
函数启动虚拟机 - 调用
startReg()
函数注册 Android 的 Java 方法,其实在这里就是 JNI 方法 - 反射调用 Java 类
com.android.internal.os.ZygoteInit#main()
方法,并把 forkzygote
进程时的参数传入用来辅助初始化zygote
进程
此时,我们回到启动zygote
进程的地方回顾一下:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
根据上述分析,此时调用句柄已经执行到com.android.internal.os.ZygoteInit#main()
,该类的 Java 文件位于platform/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
:
public static void main(String argv[])
ZygoteServer zygoteServer = new ZygoteServer();
// 标记Zygote开始初始化,并Hook确保不创建其他线程
ZygoteHooks.startZygoteNoThreadCreation();
// Zygote进入自己的进程组
try
Os.setpgid(0, 0);
catch (ErrnoException ex)
throw new RuntimeException("Failed to setpgid(0,0)", ex);
...
boolean startSystemServer = false;
String socketName = "zygote";
// 解析参数,初始化局部变量
for (int i = 1; i < argv.length; i++)
if ("start-system-server".equals(argv[i]))
startSystemServer = true;
...
// 注册一个名为zygote的ServerSocket,
zygoteServer.registerServerSocket(socketName);
// 第一次启动时会预加载资源
if (!enableLazyPreload)
...
preload(bootTimingsTraceLog);
...
// 停止Hook不创建其他线程,也就是说允许创建其他线程
ZygoteHooks.stopZygoteNoThreadCreation();
// 这里一定是true,启动SystemServer
if (startSystemServer)
startSystemServer(abiList, socketName, zygoteServer);
// 运行zygote进程的select循环
zygoteServer.runSelectLoop(abiList);
zygoteServer.closeServerSocket();
在这个初始化过程中,做了不少准备工作,更加详细的过程请阅读上述代码注释和源码,我们这里总结以下我们需要着重关注的几件事:
- 调用
ZygoteServer#registerServerSocket()
方法创建一个名为zygote
的 Socket 资源 - 调用
ZygoteInit#starSystemServer()
方法启动system_server
进程 - 调用
ZygoteInit#runSelectLoop()
方法循环接受 Socket 连接,在这里开始和 AMS 搭上边,其实这里就是循环等待 AMS 的连接和命令,更加详细的过程在下文中分析。
ZygoteServer#registerServerSocket()创建 Java 层的 ServerSocket 对象
上面说到ZygoteServer#registerServerSocket()
创建了一个名为zygote
的 Socket 资源,我们知道,Socket 是 IPC 通信的一种方式,该 Socket 资源在后期被用在和 AMS 等服务进行进程间通信。下面我们来看ZygoteServer#registerServerSocket()
的具体实现,ZygoteServer.java
位于com.android.internal.os.ZygoteServer.java
:
class ZygoteServer
private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
private LocalServerSocket mServerSocket;
ZygoteServer()
void registerServerSocket(String socketName)
if (mServerSocket == null)
int fileDesc;
// fullSocketname的值是ANDROID_SOCKET_zygote
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
// 获取Socket名称对应的环境变量
String env = System.getenv(fullSocketName);
fileDesc = Integer.parseInt(env);
// 根据环境变量创建文件描述符
FileDescriptor fd = new FileDescriptor();
fd.setInt$(fileDesc);
// 根据文件描述符创建Socket
mServerSocket = new LocalServerSocket(fd);
请读者先看完上述代码的注释,上述代码中其他的因素都是确定的,只有 Socket 名称对应的环境变量是不确定的,这个环境变量指向的文件描述符的值是在文章开头的platform/system/init.cpp
创建的,在文章开头其实我已经标识出来了,但是大多数人应该不知道是干嘛用的,我们回到init.cpp
:
// 注意这两行引入
#include "property_service.h"
#include "service.h"
int main(int argc, char** argv)
... // 一些必要的环境变量配置和启动其他Linux进程
// 执行在头部引入的property_service.h的start_property_service()函数
start_property_service();
// 解析配置文件
Parser& parser = Parser::GetInstance();
// 解析service
parser.AddSectionParser("service",std::make_unique<ServiceParser>());
...
该函数中start_property_service()
会去创建 Socket 资源,下面我们一步步看这些相关的文件。
platform/system/core/init/property_service.cpp
中调用util#create_socket()
函数:
#include "util.h"
void start_property_service()
property_set("ro.property_service.version", "2");
// 创建Socket连接
property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | ...);
...
register_epoll_handler(property_set_fd, handle_property_set_fd);
platform/system/core/init/util.cpp
的create_socket()
函数会在ANDROID_SOCKET_DIR
常量指定的目录创建一个 Unix 的 Socket 资源,并通过ANDROID_SOCKET_ENV_PREFIX
常量加[name]
指定名称的环境变量向其他位置共享该文件描述符的值:
#include <socket.h>
/*
* 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, ...)
...
android::base::unique_fd fd(socket(PF_UNIX, type, 0));
...
return fd.release();
上述两个常量定义在platform/system/core/include/cutils/sockets.h
中:
#define ANDROID*SOCKET_ENV_PREFIX "ANDROID_SOCKET*"
#define ANDROID_SOCKET_DIR "/dev/socket"
如果你手里有个 Android 系统的设备,我们可以查看一下该文件:
Harrys-MacBook-Pro:Visit Harry$ adb shell
shell@cancro:/ $ cd /dev/socket
shell@cancro:/dev/socket $ ls
adbd
...
zygote
shell@cancro:/dev/socket $
在上述com.android.internal.os.ZygoteServer.java
中也能看到 Socket 资源的前缀常量,因此它在这里就获取到了名为ANDROID_SOCKET_zygote
的文件描述符,并在 Java 层创建了LocalServerSocket
的服务端 Socket 对象。
ZygoteInit.java 启动 system_server 进程的过程
zygote
进程启动了 Java 层的LocalServerSocket
后,接着调用了ZygoteInit#starSystemServer()
启动system_server
进程。
/**
* Prepare the arguments and fork for the system server process.
*/
private static boolean startSystemServer(String socketName,
ZygoteServer zygoteServer)
/* Hardcoded command line to start the system server */
String args[] =
...
"--nice-name=system_server", // 指定进程名字 system_server
"--runtime-args",
"com.android.server.SystemServer", // 指定进程fork后要执行的程序
;
ZygoteConnection.Arguments parsedArgs = new ZygoteConnection.Arguments(args);
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
// 从zygote进程fork system_server进程
int pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
parsedArgs.debugFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
return true;
这段代码中指定了 fork 新进程的名称为system_server
,当进程被 fork 后会调用com.android.server.SystemServer#main()
方法。
先简单的看一下 fork 新进程的方法:
/**
* Fork system_server进程,并返回该进程的pid
*/
public static int forkSystemServer(int uid, int gid, ...)
VM_HOOKS.preFork();
//
int pid = nativeForkSystemServer(uid, gid, ...);
VM_HOOKS.postForkCommon();
return pid;
native private static int nativeForkSystemServer(int uid, int gid, ...);
上述代码也比较清晰明了,实际上是调用了native
方法 fork 了一个新的进程。
SystemServer.java 初始化 system_server 进程的过程
接着会执行com.android.server.SystemServer#main()
方法,我们看一下该方法:
public final class SystemServer
/**
* The main entry point from zygote.
*/
public static void main(String<以上是关于Android zygote 进程的启动过程分析的主要内容,如果未能解决你的问题,请参考以下文章
Android5 Zygote 与 SystemServer 启动流程分析