Android Framework分析Zygote进程的启动过程

Android Framework分析Zygote进程的启动过程_第1张图片Zygote进程是Android系统中的一个重要进程,其主要作用是预热Java虚拟机和启动应用进程。本文将着重分析Zygote进程的启动过程,结合代码注释和示例代码,让读者更好地理解Zygote的内部工作原理。

Zygote进程的启动过程

Zygote进程的启动过程包含几个关键步骤,主要涉及Binder通信、双亲委派模型、系统属性加载和App启动等。下面我们将逐一进行讲解。

主入口函数

Zygote进程的主函数是ZygoteInit.main(),它的主要职责是启动Zygote进程并为其提供额外的参数。该函数主要由以下两部分组成:

public static void main(String[] args) {
    ...
    // 设置JAVA_OPTS环境变量,指定堆大小等Java虚拟机参数
    Os.setenv("JAVA_OPTS", "-Dfile.encoding=UTF-8 -Xmx64m", true);
    ...
    // 创建ZygoteInit对象,并调用其registerZygoteSocket()方法
    // 该方法会创建一个ServerSocket对象,用于监听应用进程的连接请求
    ZygoteInit zygoteInit = new ZygoteInit();
    zygoteInit.registerZygoteSocketIfNeeded(envVars);
    ...
    // 开始监听应用进程的连接请求
    zygoteInit.runSelectLoop(TextUtils.join(",", abis));
}

如上所示,ZygoteInit.main()中主要进行了环境变量、ZygoteInit对象创建以及注入参数等操作,其中registerZygoteSocketIfNeeded()是ZygoteInit对象中最关键的方法之一。

注册Zygote Socket

ZygoteInit.registerZygoteSocketIfNeeded()是ZygoteInit启动过程中最重要的方法之一,它主要用于创建一个ServerSocket对象并将其绑定到一个本地Unix域socket上,这样Zygote进程就可以监听来自应用进程的连接请求了。

@SuppressWarnings("UnusedReturnValue")
private boolean registerZygoteSocketIfNeeded(@NonNull String[] envVars)
        throws IllegalStateException, IOException {
    // 获取Zygote Socket的文件路径
    mZygoteSocketName = Zygote.getConfigurationProperty("ro.zygote", null);
    if (mZygoteSocketName == null) {
        Log.e(TAG, "Error starting Zygote: required property ro.zygote not set");
        throw new RuntimeException("Error starting Zygote: required property ro.zygote not set");
    }

    // 如果文件已存在,则删除相关的unix域socket
    File socketFile = new File(mZygoteSocketName);
    if (socketFile.exists()) {
        socketFile.delete();
    }

    // 创建ServerSocket对象,绑定到本地unix域socket上
    try {
        mZygoteServerSocket = new LocalServerSocket(mZygoteSocketName);
    } catch (IOException ex) {
        Log.e(TAG, "Error binding to local socket '" + mZygoteSocketName + "'", ex);
        throw ex;
    }

    // 获取androidboot.classpath和androidboot.libs属性的值
    String classPath = Zygote.getConfigurationProperty("androidboot.classpath", "");
    String libraryPath = Zygote.getConfigurationProperty("androidboot.libs", "");

    // 设置环境变量BOOTCLASSPATH和ANDROID_ROOT
    final String bootClassPath = !classPath.isEmpty() ? "-Xbootclasspath:" + classPath.replace(':', ',') : "";
    envVars = new String[] {
            computeZygoteHeapSize(),
            "BOOTCLASSPATH=" + bootClassPath,
            "ANDROID_ROOT=" + "/system",
            "ANDROID_DATA=" + "/data",
            "ANDROID_PROPERTY_WORKSPACE=" + "/data/local/tmp",
            "LD_LIBRARY_PATH=" + libraryPath};

    // 设置好环境变量之后关闭Socket,等待应用进程的连接
    mZygoteServerSocket.close();
    return true;
}

如上所示,该方法主要实现了创建ServerSocket对象并接收应用进程的连接请求。在函数内部,最核心的部分是LocalServerSocket类的使用,它是一个继承自ServerSocket的本地socket类,因此它不需要使用网络中断和解封装技术,能够更快地处理传输数据。

接下来,该方法会获取androidboot.classpath和androidboot.libs属性的值,这些属性值是在启动Zygote进程时通过init.rc脚本获取的。

然后,该方法会设置BOOTCLASSPATH和ANDROID_ROOT等环境变量,并调用ZygoteInit中的computeZygoteHeapSize()方法计算出Zygote进程的堆大小。

最后,该方法会关闭ServerSocket对象并等待应用进程的连接请求。

应用进程的连接请求

应用进程的连接请求主要是通过ZygoteInit.runSelectLoop()方法实现的。在该方法中,Zygote进程会不断地监听ServerSocket对象是否有新的socket连接请求,并在接收到新的socket连接请求时,调用ZygoteInit.forkAndSpecialize()方法来创建新的应用进程,并通过socket文件描述符的方式将新进程启动起来。

void runSelectLoop(String abiList) {
    while (true) {
        ...
        // 接收新的连接请求
        ZygoteConnection newPeer = acceptCommandPeer(mZygoteServerSocket);

        // 创建新的进程
        ZygoteProcess zygoteProcess = ZygoteProcess.getZygoteProcess();

        // 通过Socket文件描述符的方式启动新进程
        socketDescriptor = newPeer.getFileDescriptor();
        zygoteProcess.start(socketDescriptor, newArgs, runtimeFlags, null,
                nativeDebuggable, newStdin, newStdout, newStderr, null,
                instructionSet, cdnEnabled, capabilities, permittedCapabilities, effectiveCapabilities);
    }
}

如上所示,ZygoteInit.runSelectLoop()方法主要是循环监听ServerSocket对象,并在接收到新的连接请求时,通过ZygoteProcess.start()方法启动新的App进程。

ZygoteProcess.start()方法是一个比较庞大的方法,其主要流程如下:

public int start(int socketFd, String[] argv, int runtimeFlags,
        String seInfo, boolean debuggerEnabled, FileDescriptor[] descriptors,
        String instructionSet, String invokeWith, long[] fdsToClose, int[] fdsToIgnore,
        boolean startChildZygote, String[] acceptedAbis) {
    ...
    // 创建App进程
    for (String abi : abis) {
        ...
        // 创建新进程,并创建对应的应用类加载器
        try {
            return doPreForkInit(newZygote, socketFd, contentType, argc,
                    argv, instructionSet, invokeWith, acceptedAbis, runtimeFlags, seInfo,
                    stdio_fds, fdsToClose, fdsToIgnore, isTopZygote);
        } catch (IOException e) {
            if (e.getMessage().contains("Too many open files")) {
                // If the kernel runs out of file descriptors, it may start throwing EIO on socket
                // accept(). Since we're likely to recover from an EIO, log it only at a
                // VERBOSE level.
                Log.v(TAG, "IOException on accept: ", e);
            } else {
                Log.e(TAG, "Exception creating new zygote: ", e);
            }
        }
    }
    ...
}

如上所示,doPreForkInit()方法是创建新进程的重要方法之一,它主要作用是创建应用进程的类加载器和执行系统属性文件。

系统属性文件的加载

在Zygote进程启动过程中,加载系统属性文件是一个非常重要的过程。系统属性文件中包含了许多Android系统运行所需的关键信息,例如堆大小、系统密度、语言环境等。

ZygoteInit.doPreload()方法是加载系统属性文件的核心方法。在该方法中,系统会首先扫描/system/build.prop文件和/prop目录中的所有*.prop文件,并将它们的属性信息读取到Properties对象中。然后,针对某些特殊的系统属性,比如ro.build.fingerprint,系统会调用SystemProperties.get()方法获取对应的属性值。

private static void doPreload() {
    // Get a set of default properties from /system/build.prop and /prop files.
    Properties preloadedProps = new Properties();
    preloadedProps.load(new FileInputStream(new File("/system/build.prop")));

    File[] propFiles = new File("/").listFiles(new FilenameFilter() {
        @Override
        public boolean accept(File dir, String name) {
            return name.endsWith(".prop");
        }
    });

    if (propFiles != null) {
        for (File file : propFiles) {
            try {
                FileInputStream stream = new FileInputStream(file);
                preloadedProps.load(stream);
                stream.close();
            } catch (IOException e) {
                Log.w(TAG, "Unable to read " + file.getName() + ".prop: " + e.getMessage());
            }
        }
    }

    // Look for non-native debuggability override only if we haven't already gotten the value
    // through a native property.
    String nonDebuggable =
            SystemProperties.get(PROP_NONDEBUGGABLE_APPS);
    if (nonDebuggable == null) {
        nonDebuggable = preloadedProps.getProperty(PROP_NONDEBUGGABLE_APPS);
    }

    // Look for a non-sdk graylist override only if we haven't already gotten the value
    // through a native property.
    String nonSdkGreylist =
            SystemProperties.get(PROP_NON_SDK_GREYLIST_APPS);
    if (nonSdkGreylist == null) {
        nonSdkGreylist = preloadedProps.getProperty(PROP_NON_SDK_GREYLIST_APPS);
    }

    ...
}

如上所示,doPreload()方法是ZygoteInit中一个比较重要的方法,它主要是将/system/build.prop和/prop/*.prop中的属性读取到preloadedProps这个Properties对象中。同时,该方法也会调用SystemProperties.get()方法获取ro.build.fingerprint等特殊属性的值。

接下来,将介绍应用进程类加载器的创建过程。

应用进程类加载器的创建

ZygoteProcess.getZygoteProcess().start()方法的主要作用是创建新的应用进程和相应的应用类加载器。ZygoteProcess通过调用NativeStart.main()的方式来启动新进程,并将当前ClassLoader传递给新进程。新进程中调用的 ClassLoader.getSystemClassLoader() 方法就会返回当前的ClassLoader,也就是Zygote进程的应用进程类加载器。

public static int loadZygoteChild(@NonNull ZygoteArguments parsedArgs,
        @Nullable FileDescriptor[] descriptors, ZygoteServer zygoteServer) {
    ...
    // 创建新的进程,调用的是Object.clone()方法
    Process child = Zygote.fork();

    if (child == null) {
        return 1;
    } else if (child == 0) {
        // This is the child process.
        setArgv0(parsedArgs.processName);
        Os.setpgid(0, 0);
        try {
            // 通过类加载器和服务器端Socket描述符的方式启动新应用进程。
            Runnable r = forkAndSpecialize(parsedArgs, Zygote.getForkServerSocket(),
                    Zygote.getUsapPoolSocket(), descriptors, runtimeFlags, gidList);
            r.run();
        } finally {
            Zygote.nativeCloseServerSocket();
            Zygote.nativeCloseUsapPoolSocket();
        }
    }
    return handleChildProc(child, parsedArgs, zygoteServer);
}

如上所示,新应用进程中会通过forkAndSpecialize()方法创建一个应用类加载器,在运行时期,Zygote进程将通过Socket描述符的方式将应用进程类加载器传递给应用进程,并启动应用进程。

应用进程中通过ActivityThread类的main()方法来启动应用,它会在应用进程中启动ActivityThread主线程。

总结

本文介绍了Zygote进程的启动过程,包括Binder通信、双亲委派模型、系统属性加载和App启动等多个方面,通过代码和示例的方式,详细地讲解了Zygote进程启动的内部原理。在Zygote进程启动过程中,系统会加载系统属性文件,创建应用进程类加载器,并传递给应用进程。通过深入研究Zygote进程的启动过程,可以更好地理解Android系统的内部工作原理,并对Android应用的开发、优化和调试等方面有所帮助。

参考资料:

  1. Android Framework源码:https://cs.android.com/android/platform/superproject/

  2. Android Developers官方文档:https://developer.android.com/docs

你可能感兴趣的:(Android,android,zygote,java)