Android Zygote

一、前言

本文主要讲解内容
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孵化也可节约创建的时间。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容