Zygote进程探索

一、前言

说到Zygote进程,就不可避免的提到init进程。init作为Linux的第一个进程,有着无与伦比的作用。它主要是启动系统的关键服务并且守护关键服务。这里的系统关键服务指的是Android系统的关键服务,比如媒体,通话等。即便关键进程被杀死,init进程也会把它复活出来。其中的关键之处就在于init进程会加载一个名为init.rc的文件。

我们想了解init进程就不可避免的去看源码,init的源码就位于/system/core/init/init.cpp 和 main.cpp中。这里推荐一个网站Android源码查看这是谷歌官方提供的源码查看网站。

我们可以在init.cpp中找到这样一个函数,它就会去解析init.rc里面的内容。

static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser = CreateParser(action_manager, service_list);
    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        parser.ParseConfig("/system/etc/init/hw/init.rc");
        if (!parser.ParseConfig("/system/etc/init")) {
            late_import_paths.emplace_back("/system/etc/init");
        }
        // late_import is available only in Q and earlier release. As we don't
        // have system_ext in those versions, skip late_import for system_ext.
        parser.ParseConfig("/system_ext/etc/init");
        if (!parser.ParseConfig("/vendor/etc/init")) {
            late_import_paths.emplace_back("/vendor/etc/init");
        }
        if (!parser.ParseConfig("/odm/etc/init")) {
            late_import_paths.emplace_back("/odm/etc/init");
        }
        if (!parser.ParseConfig("/product/etc/init")) {
            late_import_paths.emplace_back("/product/etc/init");
        }
    } else {
        parser.ParseConfig(bootscript);
    }
}
复制代码

那我们可以在哪里找到init.rc文件呢,我们可以在/system/core/rootdir/init.rc找到。下面浅看一下init.rc的内容吧。

# Copyright (C) 2012 The Android Open Source Project
#
# IMPORTANT: Do not create world writable files or directories.
# This is a common source of Android security bugs.
#
​
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
​
# Cgroups are mounted right before early-init using list from /etc/cgroups.json
on early-init
    # Disable sysrq from keyboard
    write /proc/sys/kernel/sysrq 0
​
    # Android doesn't need kernel module autoloading, and it causes SELinux
    # denials.  So disable it by setting modprobe to the empty string.  Note: to
    # explicitly set a sysctl to an empty string, a trailing newline is needed.
    write /proc/sys/kernel/modprobe \n
​
    # Set the security context of /adb_keys if present.
    restorecon /adb_keys
​
    # Set the security context of /postinstall if present.
    restorecon /postinstall
​
    mkdir /acct/uid
​
    # memory.pressure_level used by lmkd
    chown root system /dev/memcg/memory.pressure_level
    chmod 0040 /dev/memcg/memory.pressure_level
    # app mem cgroups, used by activity manager, lmkd and zygote
    mkdir /dev/memcg/apps/ 0755 system system
    # cgroup for system_server and surfaceflinger
    mkdir /dev/memcg/system 0550 system system
​
    # symlink the Android specific /dev/tun to Linux expected /dev/net/tun
    mkdir /dev/net 0755 root root
    symlink ../tun /dev/net/tun
    ...省略部分代码
复制代码

大家可以看到其实init.rc并不是一个程序代码文件,更像是一个脚本文件,其实也可以理解成脚本文件,它会运行Linux命令来启动相关服务。

我们需要注意的是import /system/etc/init/hw/init.${ro.zygote}.rc 这句话,它的意思是会根据系统去加载不同的init.zygote文件。我们可以在相同的目录下发现init.zygote64.rc和init.zygote32.rc两个文件。浅看一看内容,以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 reserved_disk
    socket zygote stream 660 root system
    socket usap_pool_primary stream 660 root system
    onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart media.tuner
    onrestart restart netd
    onrestart restart wificond
    task_profiles ProcessCapacityHigh
    critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
复制代码

它会通过service命令来启动一个位于/system/bin/app_process64的服务,而后面的则是带的一些参数。既然这里出现了zygote那么是不是和zygote进程有关呢。

二、Zygote进程的启动

前面说到init进程会加载init.rc文件并且通过命令启动app_process服务。那么我们的源码在哪呢。app_process的源码在frameworks/base/cmds/app_process/app_main.cpp这个路径下。既然这个是一个程序文件,那么程序的入口就是main函数了。

int main (int argc, char* const argv[]) 后面的就是argv就是携带的参数 --zygote --start-system-server

如果你们跟着源码在看的话,就会想到那么zygote进程到底是如何启动的呢。

其实我们的zygote进程已经开始执行了。

我们再看main函数中具体的代码。

 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;
        }
    }
​
复制代码

大家可以看到我们第一个参数就是--zygote所以zygote变量被赋值为true,第二次循环startSystemServer就会被赋值为true。当然niceName也被赋值了。

static const char ZYGOTE_NICE_NAME[] = "zygote";
//....省略部分代码
if (!niceName.isEmpty()) {
      runtime.setArgv0(niceName.string(), true /* setProcName */);
  }
复制代码

由上述源码可知,在这段代码执行过后,当前的进程名字就被改为了zygote。我们一直期待的zygote进程就出现了。

 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.");
    }
    
复制代码

那么上述的一段代码又是作为什么使用的呢?

三、Zygote进程的作用

前面说到如果zygote这个变量为true的话,就会执行runtime.start("com.android.internal.os.ZygoteInit", args, zygote); 这句话回去执行相应包下的zygoteInit这个类。我们浅看一下。老样子还是找到它的main方法。

 public static void main(String[] argv) {
        ZygoteServer zygoteServer = null;
    // Mark zygote start. This ensures that thread creation will throw
    // an error.
    ZygoteHooks.startZygoteNoThreadCreation();
​
    // Zygote goes into its own process group.
    try {
        Os.setpgid(0, 0);
    } catch (ErrnoException ex) {
        throw new RuntimeException("Failed to setpgid(0,0)", ex);
    }
​
    Runnable caller;
    try {
        // Store now for StatsLogging later.
        final long startTime = SystemClock.elapsedRealtime();
        final boolean isRuntimeRestarted = "1".equals(
                SystemProperties.get("sys.boot_completed"));
​
        String bootTimeTag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing";
        TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,
                Trace.TRACE_TAG_DALVIK);
        bootTimingsTraceLog.traceBegin("ZygoteInit");
        RuntimeInit.preForkInit();
​
        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]);
            }
        }
​
        final boolean isPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME);
        if (!isRuntimeRestarted) {
            if (isPrimaryZygote) {
                FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
                        BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__ZYGOTE_INIT_START,
                        startTime);
            } else if (zygoteSocketName.equals(Zygote.SECONDARY_SOCKET_NAME)) {
                FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
                        BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__SECONDARY_ZYGOTE_INIT_START,
                        startTime);
            }
        }
​
        if (abiList == null) {
            throw new RuntimeException("No ABI list supplied.");
        }
​
        // In some configurations, we avoid preloading resources and classes eagerly.
        // In such cases, we will preload things prior to our first fork.
        if (!enableLazyPreload) {
            bootTimingsTraceLog.traceBegin("ZygotePreload");
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                    SystemClock.uptimeMillis());
            preload(bootTimingsTraceLog);
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                    SystemClock.uptimeMillis());
            bootTimingsTraceLog.traceEnd(); // ZygotePreload
        }
​
        // Do an initial gc to clean up after startup
        bootTimingsTraceLog.traceBegin("PostZygoteInitGC");
        gcAndFinalize();
        bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC
​
        bootTimingsTraceLog.traceEnd(); // ZygoteInit
​
        Zygote.initNativeState(isPrimaryZygote);
​
        ZygoteHooks.stopZygoteNoThreadCreation();
​
        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();
    }
}
复制代码

这么一大堆代码,看着是不是很让人头疼,但是我们可以有重点的去看这段代码。如main方法下面的第一行代码就说明zygote进程内部实例化了一个Server,这个Server是什么呢。我们点进ZygoteServer会发现里面有个LocalServerSocket变量,没错这个server就是地地道道的SocketServer。这个Socket有啥作用呢,我们等到后面再说。

 if (!enableLazyPreload) {
            bootTimingsTraceLog.traceBegin("ZygotePreload");
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                    SystemClock.uptimeMillis());
            preload(bootTimingsTraceLog);
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                    SystemClock.uptimeMillis());
            bootTimingsTraceLog.traceEnd(); // ZygotePreload
        }
复制代码

我们继续向下看会看到这样的一段代码,在经过之前的代码判断之后,会走到If里面 ,会调用到preload方法。再看看这个方法里面会做什么吧。

 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;
    }
复制代码

这个方法里面所作的就是加载Framework使用的类与资源,如preloadClass、preloadResources等。因为所有的app启动都会加载相应的类,但是每个app的activity类都是继承了Android的activity,那么不可能每次app加载的时候都会去重新加载一遍Android的类和资源。这时候zygote进程的提前预加载的好处就体现出来了,可以加快app的启动速度,因为app所需要的Android类已经加载好了。

让我们把目光接着往下移动。

 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;
            }
        }
复制代码

我们就会看到最重要的部分。zygote进程fork出了SystemServer进程。对Android源码有所了解的同学可能会知道,SystemServer进程会启动各种重要的服务,如AMS、PMS等。

当有新的APP需要启动时,AMS就会通过Socket告知zygote进程为APP创建一个新的进程。这就是zygote进程里面维护一个SocketServer的作用。

那么到此,zygote的进程就初步了解完成了。

猜你喜欢

转载自juejin.im/post/7106878753602535438