忽如一夜春风来,千树万树梨花开
时光如白驹过隙,寥寥草草,今年的冬天又要到来了,怀念火炉打边的日子——

深入理解 AndroidFramework 之 Zygote 启动

Zygote 作为Android 第一个出道的进程被广大网友所熟知,那么该进程是如何被加载,如何运行的呢?在这里我们就尝试将她神秘的面纱一层层揭开。

1. Init 进程

Linux中PID为0的进程是所有其他进程的祖先, 也称作idle进程或swapper进程,在系统初始化时由kernel自身从无到有创建。进程0的数据成员大部分是静态定义的。

在Android系统中 0号进程会孵化出2个核心进程,一个进程号为2的名为kthreadd的进程,另一个则是进程号为1名为init的进程。kthreadd进程由idle通过kernel_thread创建,并始终运行在内核空间, 负责所有内核线程的调度和管理,所有的内核线程都是直接或者间接的以kthreadd为父进程。Init 进程是负责解析并执行 rc 文件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V2VfFhGP-1603424070132)(res/picture/ps.png)]

2. init.rc 启动zygote 服务

Zygote 进程是init 进程通过解析init.zygote64.rc 启动的(如果是32位系统就会解析init.zygote32.rc),接下来我们就来捋一捋zygote的前世今生。

  • 涉及的文件:
  1. system/core/rootdir/init.zygote64.rc
  2. frameworks/base/cmds/app_process/Android.bp
  3. frameworks/base/cmds/app_process/app_main.cpp
  4. frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
  • init.zygote64.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
    group root readproc reserved_disk
    socket zygote stream 660 root system \\创建socket
    socket usap_pool_primary stream 660 root system \\创建socket
    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

服务名称:zygote

服务路径名:/system/bin/app_process64

启动参数: ‘-Xzygote’、’/system/bin’、’–zygote’、’–start-system-server’

关于 rc 脚步具体可以参照 system/core/init/README.md

3. Zygote 进程的入口函数 —— main

3.1 Zygote的可执行库文件

从 zygot 的rc文件中可以看出,它的执行路径是“/system/bin/app_process64”,执行的库文件是“app_process64”, 我们通过查找编译文件找到了该执行库文件的Android.bp或Android.mk 所在。

代码路径 frameworks/base/cmds/app_process/Android.bp

Aosp Q code:
cc_binary {
    name: "app_process",

    srcs: ["app_main.cpp"],

    multilib: {
        lib32: {
            version_script: ":art_sigchain_version_script32.txt",
            suffix: "32",//编译32位库
        },
        lib64: {
            version_script: ":art_sigchain_version_script64.txt",
            suffix: "64",//编译64位库
        },
    },

Aosp P code:

LOCAL_MODULE:= app_process
LOCAL_MULTILIB := both //32和64位库都会编译
LOCAL_MODULE_STEM_32 := app_process32 //编译32位库时命名
LOCAL_MODULE_STEM_64 := app_process64 //编译64位库时命名

3.2 main

main 函数的工作内容可以概括为2步:

  1. 解析执行命令参数。
  2. AndroidRuntime 启动进程。

在第二步中会通过jni方式执行 AndroidRuntime.cpp 中的 start 方法的第二个参数(“com.android.internal.os.ZygoteInit”)的main方法。

app_process64可执行库文件的 main 源代码如下:

int main(int argc, char* const argv[])
{
    ...

    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    // Process command line arguments
    // ignore argv[0]
    argc--;
    argv++;

    // Everything up to '--' or first non '-' arg goes to the vm.
    //
    // The first argument after the VM args is the "parent dir", which
    // is currently unused.
    //
    // After the parent dir, we expect one or more the following internal
    // arguments :
    //
    // --zygote : Start in zygote mode
    // --start-system-server : Start the system server.
    // --application : Start in application (stand alone, non zygote) mode.
    // --nice-name : The nice name for this process.
    //
    // For non zygote starts, these arguments will be followed by
    // the main class name. All remaining arguments are passed to
    // the main method of this class.
    //
    // For zygote starts, all remaining arguments are passed to the zygote.
    // main function.
    //
    // Note that we must copy argument string values since we will rewrite the
    // entire argument block when we apply the nice name to argv0.
    //
    // As an exception to the above rule, anything in "spaced commands"
    // goes to the vm even though it has a space in it.
    const char* spaced_commands[] = { "-cp", "-classpath" };
    // Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).
    bool known_command = false;

    int i;
    for (i = 0; i < argc; i++) {
        if (known_command == true) {
          runtime.addOption(strdup(argv[i]));
          // The static analyzer gets upset that we don't ever free the above
          // string. Since the allocation is from main, leaking it doesn't seem
          // problematic. NOLINTNEXTLINE
          ALOGV("app_process main add known option '%s'", argv[i]);
          known_command = false;
          continue;
        }

        for (int j = 0;
             j < static_cast<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0]));
             ++j) {
          if (strcmp(argv[i], spaced_commands[j]) == 0) {
            known_command = true;
            ALOGV("app_process main found known command '%s'", argv[i]);
          }
        }

        if (argv[i][0] != '-') {
            break;
        }
        if (argv[i][1] == '-' && argv[i][2] == 0) {
            ++i; // Skip --.
            break;
        }

        runtime.addOption(strdup(argv[i]));
        // The static analyzer gets upset that we don't ever free the above
        // string. Since the allocation is from main, leaking it doesn't seem
        // problematic. NOLINTNEXTLINE
        ALOGV("app_process main add option '%s'", argv[i]);
    }

    // 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;
        }
    }

    Vector<String8> args;
    if (!className.isEmpty()) {
        // We're not in zygote mode, the only argument we need to pass
        // to RuntimeInit is the application argument.
        //
        // The Remainder of args get passed to startup class main(). Make
        // copies of them before we overwrite them with the process name.
        args.add(application ? String8("application") : String8("tool"));
        runtime.setClassNameAndArgs(className, argc - i, argv + i);

        if (!LOG_NDEBUG) {
          String8 restOfArgs;
          char* const* argv_new = argv + i;
          int argc_new = argc - i;
          for (int k = 0; k < argc_new; ++k) {
            restOfArgs.append("\"");
            restOfArgs.append(argv_new[k]);
            restOfArgs.append("\" ");
          }
          ALOGV("Class name = %s, args = %s", className.string(), restOfArgs.string());
        }
    } else {
        // We're in zygote mode.
        maybeCreateDalvikCache();

        if (startSystemServer) {
            args.add(String8("start-system-server"));
        }

        char prop[PROP_VALUE_MAX];
        if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
            LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
                ABI_LIST_PROPERTY);
            return 11;
        }

        String8 abiFlag("--abi-list=");
        abiFlag.append(prop);
        args.add(abiFlag);

        // In zygote mode, pass all remaining arguments to the zygote
        // main() method.
        for (; i < argc; ++i) {
            args.add(String8(argv[i]));
        }
    }

    if (!niceName.isEmpty()) {
        runtime.setArgv0(niceName.string(), true /* setProcName */);
    }

    if (zygote) {
        // frameworks/base/core/jni/AndroidRuntime.cpp start()
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);//注意这里是去启动 ZygoteInit的main 函数
    } 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.");
    }
}

> Note: runtime 的 start方法里会去调用startReg()方法去动态注册Framework里的JNI函数。

4. Zygote 的第二个门户 —— ZygoteInit

根据 main 函数的执行过程,可以将main函数分为两部分:

  • zygote 启动的 Mark 阶段。
  • systemserver 的创建。

4.1 Zygote的 Mark 阶段

这里总结 Mark 阶段总共六个步骤:

  1. 通过 ZygoteHooks 的 startZygoteNoThreadCreation 与虚拟机建立连接,标记zygote启动。
  2. 通过Os.setpgid(0, 0)将Zygote加入自己的进程组,第一个0表示当前进程。
  3. Zygote Init 之前的准备工作,如激活Ddms RuntimeInit.preForkInit();
  4. 解析main 函数的参数。
  5. 通过gcAndFinalize()进行一次gc处理
  6. Zygote 的 Native 层的初始化——Zygote.initNativeState(isPrimaryZygote)。

在初始化 zygote 的 Native 状态时,首先会去查找环境变量"ANDROID_SOCKET_zygote",并将找到的值通过atoi()转换成int类型的文件描述符,如果该环境变量为空,就会通过initUnsolSocketToSystemServer()函数去创建一个socket。最后就是检测selinux 权限并初始化selinux 上下文。

//文件路径: frameworks/base/core/java/com/android/internal/os/Zygote.java
    /**
     * Initialize the native state of the Zygote.  This inclues
     *   - Fetching socket FDs from the environment
     *   - Initializing security properties
     *   - Unmounting storage as appropriate
     *   - Loading necessary performance profile information
     *
     * @param isPrimary  True if this is the zygote process, false if it is zygote_secondary
     */
    static void initNativeState(boolean isPrimary) {
        nativeInitNativeState(isPrimary);
    }

//文件路径: frameworks/base/core/jni/com_android_internal_os_Zygote.cpp

/**
 * The prefix string for environmental variables storing socket FDs created by
 * init.
 */

static constexpr std::string_view ANDROID_SOCKET_PREFIX("ANDROID_SOCKET_");

static void com_android_internal_os_Zygote_nativeInitNativeState(JNIEnv* env, jclass,jboolean is_prima) {
  /*
   * Obtain file descriptors created by init from the environment.
   */

  std::string android_socket_prefix(ANDROID_SOCKET_PREFIX);
  std::string env_var_name = android_socket_prefix + (is_primary ? "zygote" : "zygote_secondary");
  char* env_var_val = getenv(env_var_name.c_str());

  if (env_var_val != nullptr) {
    gZygoteSocketFD = atoi(env_var_val);
    ALOGV("Zygote:zygoteSocketFD = %d", gZygoteSocketFD);
  } else {
    ALOGE("Unable to fetch Zygote socket file descriptor");
  }

  env_var_name = android_socket_prefix + (is_primary ? "usap_pool_primary" : "usap_pool_secondary");
  env_var_val = getenv(env_var_name.c_str());

  if (env_var_val != nullptr) {
    gUsapPoolSocketFD = atoi(env_var_val);
    ALOGV("Zygote:usapPoolSocketFD = %d", gUsapPoolSocketFD);
  } else {
    ALOGE("Unable to fetch USAP pool socket file descriptor");
  }

  initUnsolSocketToSystemServer();
  /*
   * Security Initialization
   */

  // security_getenforce is not allowed on app process. Initialize and cache
  // the value before zygote forks.
  gIsSecurityEnforced = security_getenforce();

  selinux_android_seapp_context_init();

  /*
   * Storage Initialization
   */

  UnmountStorageOnInit(env);

  /*
   * Performance Initialization
   */

  if (!SetTaskProfiles(0, {})) {
    ZygoteFailure(env, "zygote", nullptr, "Zygote SetTaskProfiles failed");
  }
}

// Create the socket which is going to be used to send unsolicited message
// to system_server, the socket will be closed post forking a child process.
// It's expected to be called at each zygote's initialization.
static void initUnsolSocketToSystemServer() {
    gSystemServerSocketFd = socket(AF_LOCAL, SOCK_DGRAM | SOCK_NONBLOCK, 0);
    if (gSystemServerSocketFd >= 0) {
        ALOGV("Zygote:systemServerSocketFD = %d", gSystemServerSocketFd);
    } else {
        ALOGE("Unable to create socket file descriptor to connect to system_server");
    }
}

4.2 SystemServer的创建

ZygoteInit 只是systemserve创建的发起者,创建systemserver启动的核心成员ZygoteServer和ZygoteArguments,然后由Zygote.java 的nativeForkSystemServer()去实施fork并返回子进程pid。

  1. ZygoteServer 初始化
ZygoteServer(boolean isPrimaryZygote) {
    mUsapPoolEventFD = Zygote.getUsapPoolEventFD();

    if (isPrimaryZygote) {
        //根据环境变量 ANDROID_SOCKET_zygote 获取socket文件句柄。
        mZygoteSocket =  Zygote.createManagedSocketFromInitSocket(
                         Zygote.PRIMARY_SOCKET_NAME);
        //根据环境变量 ANDROID_SOCKET_usap_pool_primary 获取usap_pool_primary句柄
        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();
}

/**
     * Creates a managed LocalServerSocket object using a file descriptor
     * created by an init.rc script.  The init scripts that specify the
     * sockets name can be found in system/core/rootdir.  The socket is bound
     * to the file system in the /dev/sockets/ directory, and the file
     * descriptor is shared via the ANDROID_SOCKET_<socketName> environment
     * variable.
     */
    static LocalServerSocket createManagedSocketFromInitSocket(String socketName) {
        int fileDesc;
        final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;

        try {
            String env = System.getenv(fullSocketName);
            fileDesc = Integer.parseInt(env);
        } catch (RuntimeException ex) {
            throw new RuntimeException("Socket unset or invalid: " + fullSocketName, ex);
        }

        try {
            FileDescriptor fd = new FileDescriptor();
            fd.setInt$(fileDesc);
            return new LocalServerSocket(fd);
        } catch (IOException ex) {
            throw new RuntimeException(
                "Error building socket from file descriptor: " + fileDesc, ex);
        }
    }
  1. ZygoteArguments 的创建
/* Hardcoded command line to start the system server */
String args[] = {
    "--setuid=1000",
    "--setgid=1000",
    "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
        + "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010,3011",
    "--capabilities=" + capabilities + "," + capabilities,
    "--nice-name=system_server",
    "--runtime-args",
    "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
    "com.android.server.SystemServer",
    };
  1. 委托 Zygote 创建进程并返回进程ID

父进程中返回的是子进程ID,子进程中返回pid = 0。

    public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
            int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
        ZygoteHooks.preFork();

        int pid = nativeForkSystemServer(
                uid, gid, gids, runtimeFlags, rlimits,
                permittedCapabilities, effectiveCapabilities);

        // Set the Java Language thread priority to the default value for new apps.
        Thread.currentThread().setPriority(Thread.NORM_PRIORITY);

        ZygoteHooks.postForkCommon();
        return pid;
    }
  1. 关闭子进程中的 socket 文件句柄。
        /* For child process */
        if (pid == 0) {
            if (hasSecondZygote(abiList)) {
                waitForSecondaryZygote(socketName);
            }

            zygoteServer.closeServerSocket();
            //子进程返回 runable 接口
            return handleSystemServerProcess(parsedArgs);
        }
        //父进程返回空。
        return null
  1. 子进程中的 handleSystemServerProcess

创建Binder线程,创建main 函数的反射runable 接口。

/**
     * Finish remaining work for the newly forked system server process.
     */
    private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
        // set umask to 0077 so new files and directories will default to owner-only permissions.
        Os.umask(S_IRWXG | S_IRWXO);

        if (parsedArgs.mNiceName != null) {
            Process.setArgV0(parsedArgs.mNiceName);
        }

        final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
        ...

        if (parsedArgs.mInvokeWith != null) {
            ...
        } else {
            ClassLoader cl = null;
            if (systemServerClasspath != null) {
                cl = createPathClassLoader(systemServerClasspath, parsedArgs.mTargetSdkVersion);

                Thread.currentThread().setContextClassLoader(cl);
            }
            /*
             * Pass the remaining arguments to SystemServer.
             */
            return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                    parsedArgs.mDisabledCompatChanges,
                    parsedArgs.mRemainingArgs, cl);
        }
        /* should never reach here */
    }

    /**
     * The main function called when started through the zygote process. This could be unified with
     * main(), if the native code in nativeFinishInit() were rationalized with Zygote startup.<p>
     *
     * Current recognized args:
     * <ul>
     * <li> <code> [--] &lt;start class name&gt;  &lt;args&gt;
     * </ul>
     */
    public static final Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges, String[] argv, ClassLoader classLoader) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
        RuntimeInit.redirectLogStreams();

        RuntimeInit.commonInit();
        ZygoteInit.nativeZygoteInit();//创建Binder线程池,用于Binder通信
        return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv, classLoader);
    }

    protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges, String[] argv, ClassLoader classLoader) {
        // If the application calls System.exit(), terminate the process
        // immediately without running any shutdown hooks.  It is not possible to
        // shutdown an Android application gracefully.  Among other things, the
        // Android runtime shutdown hooks close the Binder driver, which can cause
        // leftover running threads to crash before the process actually exits.
        nativeSetExitWithoutCleanup(true);

        VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
        VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);

        final Arguments args = new Arguments(argv);

        // The end of of the RuntimeInit event (see #zygoteInit).
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

        // Remaining arguments are passed to the start class's static main
        return findStaticMain(args.startClass, args.startArgs, classLoader);
    }

    /**
     * Invokes a static "main(argv[]) method on class "className".
     * Converts various failing exceptions into RuntimeExceptions, with
     * the assumption that they will then cause the VM instance to exit.
     */
    protected static Runnable findStaticMain(String className, String[] argv,
            ClassLoader classLoader) {
        Class<?> cl;
        try {
            cl = Class.forName(className, true, classLoader);
        } catch (ClassNotFoundException ex) {}

        Method m;
        try {
            m = cl.getMethod("main", new Class[] { String[].class });
        } catch (NoSuchMethodException ex) {}
        int modifiers = m.getModifiers();
        /*
         * This throw gets caught in ZygoteInit.main(), which responds
         * by invoking the exception's run() method. This arrangement
         * clears up all the stack frames that were required in setting
         * up the process.
         */
        return new MethodAndArgsCaller(m, argv);
    }

/**
     * Helper class which holds a method and arguments and can call them. This is used as part of
     * a trampoline to get rid of the initial process setup stack frames.
     */
    static class MethodAndArgsCaller implements Runnable {
        /** method to call */
        private final Method mMethod;

        /** argument array */
        private final String[] mArgs;

        public MethodAndArgsCaller(Method method, String[] args) {
            mMethod = method;
            mArgs = args;
        }

        public void run() {
            try {
                mMethod.invoke(null, new Object[] { mArgs });
            } catch (IllegalAccessException ex) {}
        }
    }
  1. zygote循环监听socket文件变化

创建出systemserver 子进程后,由于systemserver 子进程返回的是非空runable,父进程zygote返回是null,所以子进程会执行run()方法然后return结束。父进程会执行zygoteServer.runSelectLoop(abiList);

public static void main(String argv[]) {

    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);
    // We're in the child process and have exited the select loop. Proceed to execute the
    // command.
    if (caller != null) {
        caller.run();
    }
}

runSelectLoop 会去监听两个socket(init.zygote64.rc中的socket)文件句柄的变化,通过acceptCommandPeer()创建socket的接收端ZygoteConnection,一旦文件句柄发生变化就会由ZygoteConnection的processOneCommand(this)来处理。

在processOneCommand方法中通过Zygote.forkAndSpecialize()去处理子进程的创建,通过handleChildProc -> ZygoteInit.zygoteInit 构建main 函数的反射函数的runable 接口,一路返回到ZygoteInit的man函数,由于父进程得到的返回是null,子进程不为空,才会去执行的runable的run 方法,calss的main函数就会被执行到,例如应用创建时的ActivityThread.main()。

5. 总结

ZygoteInit 做初始化
Zygote 用于native层交流
ZygoteServer 永来管理socket
ZygoteConnection 用来接收socket消息并处理。

用了一张图片来总结

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KGMpBEeq-1603424070135)(res/picture/zygote启动.jpg)]

参考原创传送门

posted on 2020-10-23 11:37  寒风凛凛  阅读(312)  评论(0编辑  收藏  举报