在Android系统中,普通应用程序进程以及运行系统的服务system_server进程都是有Zygote进程fork来的,所以也叫Zygote孵化器。它是通过linux的fork形式创建应用程序进程和system_server进程。由于zygote进程在启动的时候会创建java虚拟机环境,因此通过fork而创将的应用程序或者system_server都可以在内部获得java虚拟机环境。
1 Zygote的启动
在系统开机启动有内核启动的第一个进程就是init进程,init进程会通过init.rc 包括import导入的各个模块的rc,包括前面几篇文章讲解的 bootanimation.rc surfaceflinger.rc等,zygote就是这样启动的,init.rc中就有导入:
import /init.${ro.zygote}.rc
路径在:system/core/rootdir/init.zygote32.rc 等,目录下有多个zygote.rc 是由于android系统支持64位系统和32位系统的原因
我们直接看init.zygote32.rc即可:
service zygote 表示要启动的服务名zygote,那么服务从哪里启动呢?就是通过/system/bin/app_process启动,后面传递的就是参数:-Xzygote /system/bin --zygote --start-system-server
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary 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
那么/system/bin/app_process 是一个二进制的可执行程序,是通过系统编译出来的,那么源码在哪里呢?通过grep 命令查找app_process 直接学习过Android.mk 或 Android.bp 定义的模块名就是生成可执行程序的文件名,通过Android.mk或者Android.bp 就可以定义源码的位置
cd frameworks
grep "app_process" ./ -rn
查找的位置如下图所示:可以看到Android.mk中的LOCAL_MODULE定义的名字那么源码路径就在:
frameworks/base/cmds/app_process
就一个app_main.cpp文件,直接看main函数
main 函数里面代码非常多,只看重点即可:其实main函数主要解析了传递的参数,根据参数的信息调用了AppRunTime的方法:
int main(int argc, char* const argv[])
{
....
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
// Process command line arguments
// ignore argv[0]
argc--;
argv++;
....
bool zygote = false;
bool startSystemServer = false;
bool application = false;
String8 niceName;
String8 className;
while (i < argc) {
const char* arg = argv[i++];//这里根据参数 对上述的变量进行赋值 --zygote --start-system-server
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) {//这里在启动zygote的时候没有这个参数
application = true;
} else if (strncmp(arg, "--nice-name=", 12) == 0) {//这里在启动zygote的时候没有这个参数
niceName.setTo(arg + 12);
} else if (strncmp(arg, "--", 2) != 0) {//这里在启动zygote的时候没有这个参数
className.setTo(arg);
break;
} else {
--i;
break;
}
}
Vector<String8> args;//args作为参数 传递给runTime了
if (!className.isEmpty()) {//className是空的 走else逻辑
.......
}else {
......
if (startSystemServer) {
args.add(String8("start-system-server"));
}
......
}
if (!niceName.isEmpty()) {//设置线程名 当zygote为true的时候niceName = ZYGOTE_NICE_NAME
//static const char ZYGOTE_NICE_NAME[] = "zygote";
runtime.setArgv0(niceName.string(), true /* setProcName */);
}
......
if (zygote) {//zygote为true 走的这里的逻辑
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {//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类,setArgv0和start函数,如下代码所示:
这里会发现AppRunTime继承了AndroidRuntime,而这两个函数正是AndroidRuntime提供的
class AppRuntime : public AndroidRuntime
frameworks/base/core/jni/AndroidRuntime.cpp (放到了jni目录下面,可以猜测C++和Java通信?后面去验证猜想)
先看下setArgv0这个函数:代码很简单用到了pthread linux的线程,线程的使用可以看这篇文章Android Native层Thread 原理,setProcName = zygote 设置了以下线程名
void AndroidRuntime::setArgv0(const char* argv0, bool setProcName) {
if (setProcName) {
int len = strlen(argv0);
if (len < 15) {
pthread_setname_np(pthread_self(), argv0);
} else {
pthread_setname_np(pthread_self(), argv0 + len - 15);
}
}
memset(mArgBlockStart, 0, mArgBlockLength);
strlcpy(mArgBlockStart, argv0, mArgBlockLength);
}
再看start这个函数,通过Jni创建了Java虚拟机,并且调用到了java层:
- className:
com.android.internal.os.ZygoteInit
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
......
/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;//C++和Java互相通信
if (startVm(&mJavaVM, &env, zygote) != 0) {//JVM 启动了一个Java虚拟机
return;
}
onVmCreated(env);//jvm 创建完毕的回调方法
/*
* Register android functions. 注册Android的一些方法
*/
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
....
//toSlashClassName 把. 替换成 / -> com/android/internal/os/ZygoteInit
char* slashClassName = toSlashClassName(className != NULL ? className : "");
//通过签名找到class
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {//class != null
//判断是否存在main方法,如果存在则调用
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
//这里就调到了java层了
env->CallStaticVoidMethod(startClass, startMeth, strArray);
......
}
}
......
}
同时我们也可以在AndroidRuntime中找到注册JNI:
extern int register_com_android_internal_os_Zygote(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
- Init.rc 启动zygote
- zygote 创建AppRuntime实例
- 解析参数, —zygote 并执行setArgv0()设置线程名为:zygote
- 执行runtime.start()方法
- 初始化jni并创建和启动jvm-java虚拟机
- 通过传递进来的classname,反射获取到class,并调用java层的main方法,此时进入到Java层
2 zygote Java层的启动
通过上述的分析,最终会调用到ZygoteInit.main()方法
// Mark zygote start. This ensures that thread creation will throw
// an error. 这个时候不允许线程创建的,如果有线程创建则抛出异常
ZygoteHooks.startZygoteNoThreadCreation();
......
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;//这里会置为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]);
}
}
上述运行完后:
startSystemServer= trueenableLazyPreload= false
ZygoteHooks.startZygoteNoThreadCreation();这里有个疑问,按照注释描述是如果创建线程就会抛出异常,这是为什么呢?这里和后面的初始化系统级别的类和资源有关系
- Zygote 阶段:这是系统的初始化阶段,Zygote 会通过加载常用的系统类库和资源来加速后续的应用启动。这时为了避免
fork()时的多线程问题,限制了线程的创建。 - 应用进程阶段:一旦
fork()生成新的子进程,应用进程就进入了自己的运行阶段。在这个阶段,应用可以自由创建和管理自己的线程,比如AsyncTask、HandlerThread、ThreadPoolExecutor等,这些都是 Android 中常见的多线程操作。
Zygote 是 Android 中非常重要的进程,负责创建和初始化 Java 虚拟机 (JVM) 以及系统的基本服务。它是所有应用进程的父进程,采用的是“forking”机制来启动新的应用进程。当系统需要启动一个新的应用时,Zygote 进程会通过 fork() 复制自身来生成新的子进程。通过这种方式,新进程可以继承父进程的资源和状态,比如类加载器、JVM 状态等。
在 Unix 系统中,fork() 是通过复制进程的内存空间来生成子进程的。在单线程环境下,fork() 是相对简单和安全的,因为它只需要复制当前线程的上下文。但如果在多线程环境下执行 fork(),则可能会出现一些问题。具体来说:
- 线程状态不一致:
fork()只会复制调用它的线程,而其他线程不会被复制。如果 Zygote 进程在fork()时已经创建了多个线程,那么在子进程中只有调用fork()的线程会被保留,其他线程会丢失。这样可能会导致不一致的状态,甚至资源泄漏或死锁。 - 锁和资源的继承问题:多线程程序中,线程之间会共享很多资源(比如锁、文件描述符等)。如果在
fork()时多个线程正在竞争某些共享资源,子进程可能会继承一些不一致的锁状态,导致死锁或其他未定义的行为。
因此,在 Zygote 进程中,**特别是在它准备 **fork() 子进程的阶段,**禁止创建新的线程是为了避免这些多线程下 **fork() 的问题。
这里禁止创建线程会在zygote进程初始化和fork()之前是要禁止创建的,通常情况下,startZygoteNoThreadCreation() 被调用是在 Zygote 进程初始化和 fork() 之前。Zygote 进程在初始化时会加载系统级别的类、资源等。这些操作完成后,才会调用 fork() 来创建应用进程。在 fork() 之前限制线程的创建,可以避免多线程下的 fork() 问题。
我们来看后续代码:
从如下代码中可以看到调用了stopZygoteNoThreadCreation 也就是说zygote进程初始化完成了,这里就放开了禁止创建新的线程,enableLazyPreload = false 所以会执行preload() 这个方法是关键这里会初始化夹杂系统级别的类和资源等,也就是说当preload执行完毕了就可以创建新线程了,避免了多个线程竞争某些资源
final boolean isPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME);
......
// 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);//预加载一些资源
......
} else {
Zygote.resetNicePriority();
}
......
Zygote.initNativeState(isPrimaryZygote);
ZygoteHooks.stopZygoteNoThreadCreation();//zygote创建完了 停止这个阻止线程创建的方法
zygoteServer = new ZygoteServer(isPrimaryZygote);
可以看下preload方法如何预加载的:
static void preload(TimingsTraceLog bootTimingsTraceLog) {
beginPreload();
preloadClasses();//预加载class
preloadResources();//预加载资源文件
nativePreloadAppProcessHALs();
maybePreloadGraphicsDriver();
preloadSharedLibraries();
preloadTextResources();
WebViewFactory.prepareWebViewInZygote();
endPreload();
warmUpJcaProviders();
......
sPreloadComplete = true;
}
这里只看下preloadClasses()如何预加载class的:
可以看到通过inputStream加载文件然后读取文件的内容,按照行读取然后调用Class.forName预加载class
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;
}
String line;
while ((line = br.readLine()) != null) {
// Skip comments and blank lines.
line = line.trim();
if (line.startsWith("#") || line.equals("")) {
continue;
}
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, line);
try {
Class.forName(line, true, null);
}
.......
}
private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";源码目录在:frameworks/base/config/preloaded-classes 可以看到这里面的内容都是系统级别的类
继续看ZygoteInit.main函数后面的代码:
- 创建
ZygoteServer这里创建socket,使用socket进行进程间的通信 startSystemServer = true这里就会forkSystemServer进程zygoteServer.runSelectLoop会开启while(true)的循环,接收消息
Zygote 通信是非常简单的“请求-响应”模式:Zygote 等待启动请求,收到请求后 fork() 子进程,然后结束通信。因此,socket 通信能够有效支持这种一次性请求场景,不需要像 Binder 那样复杂的连接管理。Binder 复杂性和开销:相比之下,Binder 提供了更多的功能,如跨进程的直接方法调用、权限验证等,但这些功能在 Zygote 场景中并不需要,反而会增加系统的复杂性和性能开销,fork()**** 与多线程冲突:Binder 的多线程特性会在 Zygote 的 fork() 操作中引入复杂性,可能导致线程状态不一致等问题
zygoteServer = new ZygoteServer(isPrimaryZygote);
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
if (r != null) {
r.run();
return;
}
}
// The select loop returns early in the child process after a fork and
// loops forever in the zygote.
caller = zygoteServer.runSelectLoop(abiList);
总上所述,整体的zygote启动流程算是结束了 init → zygote → jvm 虚拟机 → java层 → ZygoteInit → preload → zygotesever → forkSystemServer → runSelectLoop