这几天看了关于Zygote进程相关知识点,感觉对这块又有了新的认识了,接下来结合自己的理解将相关知识点记录下来,一来是好记性不如烂笔头,记录下来有助于后期查阅,二来是想通过写博客的方式加深对知识点的理解,三来是想将知识共享出来给需要的人。
我们学习一个知识点首先要问下自己问啥要学习这个?我学习Zygote相关知识点是因为很久之前就听说过它是用来孵化进程的,但是不知道它的工作原理是什么。因为万变不离其宗,只有懂得了内在原理才能更好的看清事物的本质。好了废话不多说了,开始进入正题。
Zygote无论你之前有没有听说过,我希望通过本篇博客的学习,你至少能够知道:
1)它是做什么的?
2)它跟init进程的关系是什么?
3)它跟我们app进程有什么关系?
4)它的工作原理是什么?
1.Zygote的作用
Zygote顾名思义就是孵化,它是用来孵化和管理Android系统中所有应用进程,而它自己也是一个进程。Zygote在Android系统启动时就会被启动,它会先预加载一些常用的类和资源,以便后续的应用程序进程能够快速启动。当一个应用程序需要启动时,Zygote会fork一个新进程,并把一些已经加载好的类和资源共享给这个新进程,以提高应用程序的启动速度。
看到上面这段解释我们脑海中可能会出现很多问号,Zygote是怎么启动的?它是如何预加载类和资源的,为什么要预加载?它是怎么fork进程的?等等。我们先不用考虑,等放到后面讲原理的时候再讲解。
2.Android系统启动流程

电源键按下后首先Boot ROM将引导程序BootLoader程序加载进内存中,接着BootLoader引导启动linux系统,系统起来后就开始启动0号进程idle,然后idle启动1号进程init,接着init进程会读取init.rc文件,这个文件实际上就是linux脚本,里面定义了各种需要启动的服务。Zygote进程就是init进程启动的进程之一,Zygote第一个fork出来的进程就是system_server进程,我们熟悉的AMS、PMS、WMS等Android框架层服务都是这个system_server进程启动的。在下一节的原理部分会详细介绍。

以上是执行adb shell后再执行ps -A看到的进程详情,PPID那一列是父进程号,PID列是当前进程号。可以看到audioservice、logcat、installd等服务都是由init进程启动的,这些进程是杀不掉的,当我们用kill -9 xxx来杀掉进程后,再次ps -A后会发现刚被杀掉的进程又会起来。init进程的主要作用就是 :1)启动系统关键服务。2)守护关键服务。
然后再看下关键进程zygote64,它的进程号是317(这个进程号是随机的),所有PPID是317的进程都是它来负责启动的,比如system_server进程。
看到这里有人会问:说了这么多你还没告诉我zygote到底有啥用?你上面说的system_server进程是它fork出来的,system_server又是干啥的?它的作用就是只启动system_server进程吗?别急,接下来希望通过下面的讲解,慢慢揭开zygote神秘的面纱。
3.原理讲解
(1)先看下system/core/rootdir/init.rc文件
import /init.environ.rc
import /system/etc/init/hw/init.usb.rc
import /init.${ro.hardware}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /system/etc/init/hw/init.usb.configfs.rc
import /system/etc/init/hw/init.${ro.zygote}.rc
......
引入了其他rc文件,再看下system/core/rootdir/init.zygote64.rc文件
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
可以看到rc文件实际上是linux脚本文件,最终会通过service命令来启动zygote服务。那app_process又是什么?
zygote是通过app_process启动,看下frameworks/base/cmds/app_process/app_main.cpp
/*
* Main entry of app process.
*
* Starts the interpreted runtime, then starts up the application.
*
*/
int main(int argc, char* const argv[])
{
if (!LOG_NDEBUG) {
String8 argv_String;
for (int i = 0; i < argc; ++i) {
argv_String.append("\"");
argv_String.append(argv[i]);
argv_String.append("\" ");
}
ALOGV("app_process main with argv: %s", argv_String.string());
}
......//省略
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
// Parse runtime arguments. Stop at first unrecognized option.
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 (!niceName.isEmpty()) {
runtime.setArgv0(niceName.string(), true /* setProcName */);
}
if (zygote) {
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.");
}
}
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server 这段命令启动的服务最终会传入main函数中的argv参数,然后解析参数,具体代码从上面代码30行开始。
runtime.setArgv0(niceName.string(), true /* setProcName */);实际上是给app_process重新设置了另外一个名字zygote64。
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));启动虚拟机。
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);在虚拟机中运行ZygoteInit,ZygoteInit是java写的,即这一步Zygote就从native世界进入到了Java世界。
Zygote的java世界入口就是ZygoteInit类的main方法,看下frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String[] argv) {
ZygoteServer zygoteServer = null;
...... //省略
Runnable caller;
try {
...... //省略
boolean startSystemServer = false;
String zygoteSocketName = "zygote";
String abiList = null;
boolean enableLazyPreload = false;
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
} else if ("--enable-lazy-preload".equals(argv[i])) {
enableLazyPreload = true;
} else if (argv[i].startsWith(ABI_LIST_ARG)) {
abiList = argv[i].substring(ABI_LIST_ARG.length());
} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command line argument: " + argv[i]);
}
}
...... //省略
// In some configurations, we avoid preloading resources and classes eagerly.
// In such cases, we will preload things prior to our first fork.
if (!enableLazyPreload) {
......
preload(bootTimingsTraceLog);
......
}
......
zygoteServer = new ZygoteServer(isPrimaryZygote);
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
// {@code r == null} in the parent (zygote) process, and {@code r != null} in the
// child (system_server) process.
if (r != null) {
r.run();
return;
}
}
Log.i(TAG, "Accepting command socket connections");
// The select loop returns early in the child process after a fork and
// loops forever in the zygote.
caller = zygoteServer.runSelectLoop(abiList);
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with fatal exception", ex);
throw ex;
} finally {
if (zygoteServer != null) {
zygoteServer.closeServerSocket();
}
}
// We're in the child process and have exited the select loop. Proceed to execute the
// command.
if (caller != null) {
caller.run();
}
}
ZygoteInit的main()方法是Android启动中的第一个Java进程的主方法。它主要完成几件事情,首先看下preload:
private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";
static void preload(TimingsTraceLog bootTimingsTraceLog) {
Log.d(TAG, "begin preload");
bootTimingsTraceLog.traceBegin("BeginPreload");
beginPreload();
bootTimingsTraceLog.traceEnd(); // BeginPreload
bootTimingsTraceLog.traceBegin("PreloadClasses");
preloadClasses();
bootTimingsTraceLog.traceEnd(); // PreloadClasses
bootTimingsTraceLog.traceBegin("CacheNonBootClasspathClassLoaders");
cacheNonBootClasspathClassLoaders();
bootTimingsTraceLog.traceEnd(); // CacheNonBootClasspathClassLoaders
bootTimingsTraceLog.traceBegin("PreloadResources");
preloadResources();
bootTimingsTraceLog.traceEnd(); // PreloadResources
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadAppProcessHALs");
nativePreloadAppProcessHALs();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadGraphicsDriver");
maybePreloadGraphicsDriver();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
preloadSharedLibraries();
preloadTextResources();
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
// for memory sharing purposes.
WebViewFactory.prepareWebViewInZygote();
endPreload();
warmUpJcaProviders();
Log.d(TAG, "end preload");
sPreloadComplete = true;
}
private static void preloadClasses() {
final VMRuntime runtime = VMRuntime.getRuntime();
InputStream is;
try {
is = new FileInputStream(PRELOADED_CLASSES);
} catch (FileNotFoundException e) {
Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
return;
}
......
}
该方法预加载了虚拟机运行时所需的各类资源,比如上面的preloadClasses,就预加载了各种系统类。因为我们每个android应用程序都会用到系统资源,预加载后所有由zygote fork出来的应用进程都能共用一块内存。借用网上的图:

通过上图可以很容易理解在Zygote进程预加载系统资源后,然后通过它孵化出其他的虚拟机进程,进而共享虚拟机内存和框架层资源,这样大幅度提高应用程序的启动和运行速度。
zygoteServer = new ZygoteServer(isPrimaryZygote);创建一个socket服务端,用于等待AMS请求Zygote创建新的应用程序进程。forkSystemServer启动system_server服务。
class ZygoteServer {
ZygoteServer(boolean isPrimaryZygote) {
mUsapPoolEventFD = Zygote.getUsapPoolEventFD();
if (isPrimaryZygote) {
mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);
mUsapPoolSocket =
Zygote.createManagedSocketFromInitSocket(
Zygote.USAP_POOL_PRIMARY_SOCKET_NAME);
} else {
mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.SECONDARY_SOCKET_NAME);
mUsapPoolSocket =
Zygote.createManagedSocketFromInitSocket(
Zygote.USAP_POOL_SECONDARY_SOCKET_NAME);
}
mUsapPoolSupported = true;
fetchUsapPoolPolicyProps();
}
private ZygoteConnection acceptCommandPeer(String abiList) {
try {
return createNewConnection(mZygoteSocket.accept(), abiList);
} catch (IOException ex) {
throw new RuntimeException(
"IOException during accept()", ex);
}
}
protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
throws IOException {
return new ZygoteConnection(socket, abiList);
}
}
重点看下runSelectLoop方法
/**
* Runs the zygote process's select loop. Accepts new connections as
* they happen, and reads commands from connections one spawn-request's
* worth at a time.
* @param abiList list of ABIs supported by this zygote.
*/
Runnable runSelectLoop(String abiList) {
ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
ArrayList<ZygoteConnection> peers = new ArrayList<>();
// 将 zygote socket 的 fd加入数组
socketFDs.add(mZygoteSocket.getFileDescriptor());
peers.add(null);
mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
// 这里使用到了linux的poll机制
while (true) {
fetchUsapPoolPolicyPropsWithMinInterval();
mUsapPoolRefillAction = UsapPoolRefillAction.NONE;
int[] usapPipeFDs = null;
StructPollfd[] pollFDs;
// Allocate enough space for the poll structs, taking into account
// the state of the USAP pool for this Zygote (could be a
// regular Zygote, a WebView Zygote, or an AppZygote).
if (mUsapPoolEnabled) {
usapPipeFDs = Zygote.getUsapPipeFDs();
pollFDs = new StructPollfd[socketFDs.size() + 1 + usapPipeFDs.length];
} else {
pollFDs = new StructPollfd[socketFDs.size()];
}
/*
* For reasons of correctness the USAP pool pipe and event FDs
* must be processed before the session and server sockets. This
* is to ensure that the USAP pool accounting information is
* accurate when handling other requests like API deny list
* exemptions.
*/
int pollIndex = 0;
for (FileDescriptor socketFD : socketFDs) {
pollFDs[pollIndex] = new StructPollfd();
pollFDs[pollIndex].fd = socketFD;
pollFDs[pollIndex].events = (short) POLLIN;
++pollIndex;
}
final int usapPoolEventFDIndex = pollIndex;
if (mUsapPoolEnabled) {
pollFDs[pollIndex] = new StructPollfd();
pollFDs[pollIndex].fd = mUsapPoolEventFD;
pollFDs[pollIndex].events = (short) POLLIN;
++pollIndex;
// The usapPipeFDs array will always be filled in if the USAP Pool is enabled.
assert usapPipeFDs != null;
for (int usapPipeFD : usapPipeFDs) {
FileDescriptor managedFd = new FileDescriptor();
managedFd.setInt$(usapPipeFD);
pollFDs[pollIndex] = new StructPollfd();
pollFDs[pollIndex].fd = managedFd;
pollFDs[pollIndex].events = (short) POLLIN;
++pollIndex;
}
}
int pollTimeoutMs;
if (mUsapPoolRefillTriggerTimestamp == INVALID_TIMESTAMP) {
pollTimeoutMs = -1;
} else {
long elapsedTimeMs = System.currentTimeMillis() - mUsapPoolRefillTriggerTimestamp;
if (elapsedTimeMs >= mUsapPoolRefillDelayMs) {
// The refill delay has elapsed during the period between poll invocations.
// We will now check for any currently ready file descriptors before refilling
// the USAP pool.
pollTimeoutMs = 0;
mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
} else if (elapsedTimeMs <= 0) {
// This can occur if the clock used by currentTimeMillis is reset, which is
// possible because it is not guaranteed to be monotonic. Because we can't tell
// how far back the clock was set the best way to recover is to simply re-start
// the respawn delay countdown.
pollTimeoutMs = mUsapPoolRefillDelayMs;
} else {
pollTimeoutMs = (int) (mUsapPoolRefillDelayMs - elapsedTimeMs);
}
}
int pollReturnValue;
try {
pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
if (pollReturnValue == 0) {
// The poll returned zero results either when the timeout value has been exceeded
// or when a non-blocking poll is issued and no FDs are ready. In either case it
// is time to refill the pool. This will result in a duplicate assignment when
// the non-blocking poll returns zero results, but it avoids an additional
// conditional in the else branch.
mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
} else {
boolean usapPoolFDRead = false;
while (--pollIndex >= 0) {
//采用I/O多路复用机制,当客户端发出连接请求或者数据处理请求时,跳过continue,执行后面的代码
if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
continue;
}
if (pollIndex == 0) {
//表示zygote socket中有连接到来,那么就要创建客户端连接
// 实际上就是执行socket accept操作
// 然后把创建的这个连接也加入到监听数组中,索引肯定大于0了
// 那么一旦这个连接中有数据来了,pollIndex肯定大于0
// Zygote server socket
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
socketFDs.add(newPeer.getFileDescriptor());
} else if (pollIndex < usapPoolEventFDIndex) {
// Session socket accepted from the Zygote server socket
try {
ZygoteConnection connection = peers.get(pollIndex);
boolean multipleForksOK = !isUsapPoolEnabled()
&& ZygoteHooks.isIndefiniteThreadSuspensionSafe();
final Runnable command =
connection.processCommand(this, multipleForksOK);
// TODO (chriswailes): Is this extra check necessary?
if (mIsForkChild) {
// We're in the child. We should always have a command to run at
// this stage if processCommand hasn't called "exec".
if (command == null) {
throw new IllegalStateException("command == null");
}
return command;
} else {
// We're in the server - we should never have any commands to run.
if (command != null) {
throw new IllegalStateException("command != null");
}
// We don't know whether the remote side of the socket was closed or
// not until we attempt to read from it from processCommand. This
// shows up as a regular POLLIN event in our regular processing
// loop.
if (connection.isClosedByPeer()) {
connection.closeSocket();
peers.remove(pollIndex);
socketFDs.remove(pollIndex);
}
}
} catch (Exception e) {
...
}
} else {
...
}
}
...
}
...
}
}
ZygoteServer.runSelectLoop()方法实际上是一个死循环,是一个不断循环执行命令的方法。执行runSelectLoop()方法是为了等待消息去创建应用进程。
runSelectLoop()方法主要做两件事:
①每次循环都重新构建监听文件列表,主要是ZygoteServer的socket文件(ZygoteServer的socket和其他应用进程连接过来的socket)和usap文件节点。
②监听文件列表,并从中获取命令执行。
最后借用一幅图来总结下整个流程:

通过以上介绍,再回过头看看第1节介绍的zygote的作用,相信大家应该有稍微深刻点的认识了。总结下,init进程属于linux进程,init进程启动zygote进程,它属于java进程,zygote负责创建android应用进程和预加载应用程序启动必要的资源,其中比较重要的systemserver进程负责启动android中重要的服务,比如AMS、PMS、WMS等等。下一章中将介绍PMS相关知识点。
这篇文章写的也很不错:https://blog.csdn.net/zenmela2011/article/details/125330521