Android Zygote

作者: Yink_Liu | 来源:发表于2022-03-04 11:36 被阅读0次

    一、前言

    本文主要讲解内容
    1、系统启动zygote、zygote的构造流程、主要做了什么
    2、如何创建一个新的进程
    3、systemserver的ams创建应用如何建立socket联系,以及如何收发消息

    先简单提一下开机流程

    init.rc -> zygote.rc -> app_main.cpp  ->  AndroidRuntime.cpp (启动虚拟机,注册jni,启动zygote) -> ZygoteInit.java ->  SystemServer进程
    

    其中init是我们系统启动的第一个进程,正是它通过linux的forck方法,创建我们系统的最重要的进程之一zygote.
    zygote的启动过程以及作用:启动dalvik虚拟机,加载系统必须的一些资源,启动framework的systemserver进程。最后等待app请求创建应用进程
    zygote在fork一个新的进程时会克隆出和之前zygote几乎一样的进程包含zygote的资源,新进程不需要进行初始化操作,只会修改一些必要参数
    由于源码过多,本文的代码都会精简要点,最好结合源码阅读,源码基于androidR,各安卓版本代码可能有小区别但整体不会变化很大

    二、启动流程

    2.1、init.rc启动的地方

    关于zygote的rc文件有几个地方:

    ./system/core/rootdir/init.zygote32_64.rc
    ./system/core/rootdir/init.zygote32.rc
    ./system/core/rootdir/init.zygote64_32.rc
    ./system/core/rootdir/init.zygote64.rc
    

    他们的32和64分别对应两个启动的地方app_process32和app_process64
    例如:zygote64_32.rc,那么它以app_process64为主,app_process32为辅,它两都会启动,也就是有两个zygote进程
    ./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 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 netd
        onrestart restart wificond
        writepid /dev/cpuset/foreground/tasks
    

    上面就是开始启动zygote的地方,不管是启动32/64,都比较类似,执行手机里的/system/bin/app_process它对应的地方

    2.2、app_main init主入口

    app_main.cpp    frameworks\base\cmds\app_process    11671   2022/2/23   241
    

    app_main.cpp的main函数就成了入口,它主要做了如下几个事情:

    //1、构建runtime
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    
    //2、解析运行参数
    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;
            }
        }
    
    //3、runtime start zygoteinit
    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.");
        }
    

    这里的runtime只是一个调用,实现在
    AndroidRuntime.cpp frameworks\base\core\jni

    void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
    {
        //这句日志实际输出02-21 11:29:59.487   496   496 D AndroidRuntime: >>>>>> START com.android.internal.os.ZygoteInit uid 0 <<<<<<
        //达标className = com.android.internal.os.ZygoteInit
        ALOGD(">>>>>> START %s uid %d <<<<<<\n",
                className != NULL ? className : "(unknown)", getuid());
        //打印关键日志boot_progress_start 表示开机过程上层开始
        for (size_t i = 0; i < options.size(); ++i) {
            if (options[i] == startSystemServer) {
                primary_zygote = true;
               /* track our progress through the boot sequence */
               const int LOG_BOOT_PROGRESS_START = 3000;
               LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,  ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
            }
        }
    
        1、启动虚拟机
         /* start the virtual machine */
        JniInvocation jni_invocation;
        jni_invocation.Init(NULL);
        JNIEnv* env;
        if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
            return;
        }
        onVmCreated(env);
    
        2、注册JNI
        /*
         * Register android functions.
         */
        if (startReg(env) < 0) {
            ALOGE("Unable to register all android natives\n");
            return;
        }
    
        3、调用ZygoteInit的main函数
        //这就是上面备注的className = ZygoteIni
        char* slashClassName = toSlashClassName(className != NULL ? className : "");
        jclass startClass = env->FindClass(slashClassName);
        if (startClass == NULL) {
            ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
            /* keep going */
        } else {
            jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
                "([Ljava/lang/String;)V");
    

    2.3、ZygoteInit.java#main zygote入口

    public static void main(String argv[]) {
            1、加载进程的资源和类(这里可以多线程加载class文件优化开机速度)
            preload(bootTimingsTraceLog);
                    
            2、创建zygoteServer
            zygoteServer = new ZygoteServer(isPrimaryZygote);
    
                if (startSystemServer) {
                    3、把sokeckname传进去systemserver中,并开始运行systemserver
                    Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
                    if (r != null) {
                        r.run();
                        return;
                    }
            4、最后开启loop等待消息
            caller = zygoteServer.runSelectLoop(abiList);
        }
    

    看一下forkSystemServer中关键的两个地方

    private static Runnable forkSystemServer(String abiList, String socketName,
                ZygoteServer zygoteServer) {
            1、zygote调用forkSystemServer这个native方法创建出系统进程
            pid = Zygote.forkSystemServer(
                        parsedArgs.mUid, parsedArgs.mGid,
                        parsedArgs.mGids,
                        parsedArgs.mRuntimeFlags,
                        null,
                        parsedArgs.mPermittedCapabilities,
                        parsedArgs.mEffectiveCapabilities);
    
            2、给应用创建新的进程,走handleSystemServerProcess逻辑
            /* For child process */
            if (pid == 0) {
                if (hasSecondZygote(abiList)) {
                    waitForSecondaryZygote(socketName);
                }
    
                zygoteServer.closeServerSocket();
                return handleSystemServerProcess(parsedArgs);
            }
    
    }
    

    这里下一步就开始启动我们熟悉的systemserver服务。关于Systemserver就不过多阐述了,它主要启动了我们整个android系统的各种service

    三、zygote创建子进程流程

    3.1、调用流程

    创建一个应用流程这里就不详述了,都大同小异,这里直接跟踪到关键点,当我们跟踪启动流程跟踪到
    ActivityStack.java frameworks\base\services\core\java\com\android\server\wm
    ActivityStack#resumeTopActivityInnerLocked

    mStackSupervisor.startSpecificActivity(next, true, false);
    

    startSpecificActivity主要判断当前应用是否进程已经在运行,会跟踪改到ActivityTaskManager#startProcessAsync方法
    final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::startProcess, mAmInternal, activity.processName, activity.info.applicationInfo, knownToBeDead, isTop, hostingType, activity.intent.getComponent());
    即来到ActivityManagerServie#startProcess

    public void startProcess(String processName, ApplicationInfo info, boolean knownToBeDead,
                    boolean isTop, String hostingType, ComponentName hostingName) {
    
            startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,
                    new HostingRecord(hostingType, hostingName, isTop),
                    ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE, false /* allowWhileBooting */,
                    false /* isolated */, true /* keepIfLarge */);
    

    即来到ActivityManagerServie#startProcessLocked - ProcessList#startProcessLocked -
    ProcessList.java frameworks\base\services\core\java\com\android\server\am

    private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
                ProcessRecord app, int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags,
                int mountExternal, String seInfo, String requiredAbi, String instructionSet,
                String invokeWith, long startTime) {
        
        Process.ProcessStartResult startResult;
                if (hostingRecord.usesWebviewZygote()) {
                    startResult = startWebView(entryPoint,
                            app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                            app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                            app.info.dataDir, null, app.info.packageName, app.mDisabledCompatChanges,
                            new String[]{PROC_START_SEQ_IDENT + app.startSeq});
                } else if (hostingRecord.usesAppZygote()) {
                    final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
    
                    // We can't isolate app data and storage data as parent zygote already did that.
                    startResult = appZygote.getProcess().start(entryPoint,
                            app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                            app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                            app.info.dataDir, null, app.info.packageName,
                            /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
                            app.mDisabledCompatChanges, pkgDataInfoMap, whitelistedAppDataInfoMap,
                            false, false,
                            new String[]{PROC_START_SEQ_IDENT + app.startSeq});
                } else {
                    //modify for prefork blank process begin
                    PreForkArgs preforkArgs = new PreForkArgs(entryPoint,
                            app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                            app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                            app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
                            isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap,
                            whitelistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
                            new String[] {PROC_START_SEQ_IDENT + app.startSeq});
                    startResult = mService.handlePreForkStartProcess(preforkArgs);
                    if (startResult == null) {
                        startResult = Process.start(entryPoint,
                                app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                                app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                                app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
                                isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap,
                                whitelistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
                                new String[]{PROC_START_SEQ_IDENT + app.startSeq});
                    }
                    //modify for prefork blank process end
                }
            
    }
    

    其中主要是appZygote.getProcess().start这一句,调用栈
    ZygoteProcess中start - startViaZygote -
    ygoteProces - zygoteSendArgsAndGetResult - attemptZygoteSendArgsAndGetResult

    3.2、连接socket发送数据

    1、sokeck建立的地方

    startViaZygote - openZygoteSocketIfNeeded - attemptConnectionToPrimaryZygote
    
     @GuardedBy("mLock")
        private void attemptConnectionToPrimaryZygote() throws IOException {
                primaryZygoteState =
                        ZygoteState.connect(mZygoteSocketAddress, mUsapPoolSocketAddress);
                //构建好消息这里发送
                maybeSetApiBlacklistExemptions(primaryZygoteState, false);
        }
    
    static ZygoteState connect(@NonNull LocalSocketAddress zygoteSocketAddress,
         @Nullable LocalSocketAddress usapSocketAddress) {
              DataInputStream zygoteInputStream;
                BufferedWriter zygoteOutputWriter;
                final LocalSocket zygoteSessionSocket = new LocalSocket();
    
                if (zygoteSocketAddress == null) {
                    throw new IllegalArgumentException("zygoteSocketAddress can't be null");
                }
    
                try {
                    //关键点,这里连接上了zygote的socket
                    zygoteSessionSocket.connect(zygoteSocketAddress);
                    zygoteInputStream = new DataInputStream(zygoteSessionSocket.getInputStream());
                    zygoteOutputWriter =
                            new BufferedWriter(
                                    new OutputStreamWriter(zygoteSessionSocket.getOutputStream()),
                                    Zygote.SOCKET_BUFFER_SIZE);
     }
    

    2、通过zygoteState的BufferedWriter,用socket发消息给zygote

     private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult(
                ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {
            try {
                final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
                final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;
    
                zygoteWriter.write(msgStr);
                zygoteWriter.flush();
    
                // Always read the entire result from the input stream to avoid leaving
                // bytes in the stream for future process starts to accidentally stumble
                // upon.
                Process.ProcessStartResult result = new Process.ProcessStartResult();
                result.pid = zygoteInputStream.readInt();
                result.usingWrapper = zygoteInputStream.readBoolean();
    
                if (result.pid < 0) {
                    throw new ZygoteStartFailedEx("fork() failed");
                }
    

    上面这段代码,通过zygoteWriter把消息发给zygote,即发送流程

    3.3、接收流程

    接着上面的第二部分的ZygoteInit#main函数中,new了一个ZygoteServer

    3.3.1 创建socket

    ZygoteServer(boolean isPrimaryZygote) {
            mUsapPoolEventFD = Zygote.getUsapPoolEventFD();
    
            if (isPrimaryZygote) {
                mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);
    
    static LocalServerSocket createManagedSocketFromInitSocket(String socketName) {
            int fileDesc;
            // fullSocketName = “ANDROID_SOCKET_” + “zygote";
            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);
            }
        }
    

    3.3.2 sokeck创建后消息发送到了哪里

    这里又接着上面的第二部分的ZygoteInit#main函数中第四步zygoteServer.runSelectLoop(abiList);
    ZygoteServer创建了socket,runSelectLoop会执行

    Runnable runSelectLoop(String abiList) {
        while(true) {
            try {
            ZygoteConnection connection = peers.get(pollIndex);
            // while循环到这句话会执行forck操作,进行新的进程的创建
            final Runnable command = connection.processOneCommand(this);
    
            // 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 processOneCommand hasn't called "exec".
                if (command == null) {
                    throw new IllegalStateException("command == null");
                }
    
                return command;
            }
        }
    }
    

    3.4 创建子进程

    ZygoteConnection#processOneCommand
    Runnable processOneCommand(ZygoteServer zygoteServer) {
         pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
                    parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
                    parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
                    parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
                    parsedArgs.mPkgDataInfoList, parsedArgs.mWhitelistedDataInfoList,
                    parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs);
        //穿件完后给ams回复
        return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);
    }
    
    //Zygote#forkAndSpecialize
    static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
                int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
                int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
                boolean isTopApp, String[] pkgDataInfoList, String[] whitelistedDataInfoList,
                boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) {
            ZygoteHooks.preFork();
    
            int pid = nativeForkAndSpecialize(
                    uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
                    fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
                    pkgDataInfoList, whitelistedDataInfoList, bindMountAppDataDirs,
                    bindMountAppStorageDirs);
    

    子进程的初始化操作就不详述了大概说下调用栈
    handleChildProc - ZygoteInit.zygoteInit - RuntimeInit.applicationInit - RuntimeInit.findStaticMain

    四、写在最后

    本文意在分析zygote的构建,以及如何通过socke构建新的进程。下面说两个比较常规的问题

    为啥系统其它进程都用binder而这里采用socket呢?
    首先binder是多线程的,zygote的fork函数是不允许多线程的,不然容易造成死锁(copy on write)

    为啥运行app不新建一个进程而采用zygote fork?
    因为应用是独立运行在dalvik中,他们的进程空间是分开的。如果每个应用都是新建进程,那么zygote加载的系统资源就会重复创建添加浪费系统资源。且zygote孵化也可节约创建的时间。

    相关文章

      网友评论

        本文标题:Android Zygote

        本文链接:https://www.haomeiwen.com/subject/pqesrrtx.html