Activity 启动流程(三)—— 应用程序进程启动阶段

418 阅读6分钟

前言

Activity 启动过程文章系列会按阶段拆开分析。上一篇已经停在 startProcessAsync() 把请求交给进程启动链;本文只聚焦 应用程序进程启动阶段:system_server 如何把启动请求转成一组 Zygote 参数,Zygote 如何接收这些参数并 fork 出新的应用进程。

启动流程梳理:

  1. Activity 启动流程(一)—— Launcher 阶段
  2. Activity 启动流程(二)—— AMS 处理阶段
  3. Activity 启动流程(三)—— 应用程序进程启动阶段
  4. Activity 启动流程(四)—— ActivityThread 初始化阶段
  5. Activity 启动流程(五)—— Activity 启动阶段

代码基于 android-14.0.0_r9

本文只讲 “进程怎么被创建出来”

1. 一句话总览

这一阶段的核心是:AMS 接到 ATMS 转来的进程启动请求后,先在 system_server 内准备 ProcessRecord 和启动参数,再通过 Process.start()ZygoteProcess 把参数发给 Zygote;Zygote 收到请求后执行 forkAndSpecialize() 创建子进程,并把新 pid 返回给 system_server。

主链路如下:

ActivityManagerInternal.startProcess()ActivityManagerService.startProcess()ActivityManagerService.startProcessLocked()ProcessList.startProcessLocked()Process.start()ZygoteProcess.start()startViaZygote()zygoteSendArgsAndGetResult()ZygoteServer.runSelectLoop()ZygoteConnection.processCommand()Zygote.forkAndSpecialize()ProcessStartResult

flowchart LR
    A[ActivityManagerInternal.startProcess]
    B[AMS.startProcess]
    C[ProcessList.startProcessLocked]
    D[Process.start]
    E[ZygoteProcess.startViaZygote]
    F[Socket 发送启动参数]
    G[ZygoteServer.runSelectLoop]
    H[ZygoteConnection.processCommand]
    I[forkAndSpecialize]
    J[返回 ProcessStartResult / pid]

    A --> B --> C --> D --> E --> F --> G --> H --> I --> J

这条链路里,system_server 负责“组织启动请求”,Zygote 负责“真正 fork 进程”。当 Zygote 将 ProcessStartResult / pid 通过 ZygoteProcess 回传后,本文这一阶段就结束了。

2. 应用程序进程启动阶段源码分析

2.1 请求落到 AMS:本地服务把进程启动交给 ActivityManagerService

上一篇最后停在 ActivityManagerInternal.startProcess(...) 这条本地服务边界。它在 AMS 里的实现如下:

// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public void startProcess(String processName, ApplicationInfo info, boolean knownToBeDead,
        boolean isTop, String hostingType, ComponentName hostingName) {
    try {
        synchronized (ActivityManagerService.this) {
            HostingRecord hostingRecord =
                    new HostingRecord(hostingType, hostingName, isTop);
            ProcessRecord rec = getProcessRecordLocked(processName, info.uid);
            ProcessRecord app = startProcessLocked(processName, info, knownToBeDead,
                    0 /* intentFlags */, hostingRecord,
                    ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE, false /* allowWhileBooting */,
                    false /* isolated */);
        }
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    }
}

这里做的事情并不复杂,但职责很明确:

  1. 接住 ATMS 交来的启动请求
  2. 构造 HostingRecord,把“这次进程为什么被拉起”保存下来。
  3. 进入 startProcessLocked(),正式切到 AMS 的进程管理链路。

也就是说,从这一刻起,问题已经不再是“这个 Activity 该不该启动”,而是“承载它的应用进程怎么创建出来”。

2.2 system_server 侧准备:ProcessList 组装启动参数并选择入口类

AMS 自身的 startProcessLocked() 只是转发到 ProcessList

// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
final ProcessRecord startProcessLocked(String processName,
        ApplicationInfo info, boolean knownToBeDead, int intentFlags,
        HostingRecord hostingRecord, int zygotePolicyFlags, boolean allowWhileBooting,
        boolean isolated) {
    return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
            hostingRecord, zygotePolicyFlags, allowWhileBooting, isolated, 0 /* isolatedUid */,
            false /* isSdkSandbox */, 0 /* sdkSandboxClientAppUid */,
            null /* sdkSandboxClientAppPackage */,
            null /* ABI override */, null /* entryPoint */,
            null /* entryPointArgs */, null /* crashHandler */);
}

真正准备进程参数的地方在 ProcessList.startProcessLocked()

// frameworks/base/services/core/java/com/android/server/am/ProcessList.java
boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
        int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks,
        String abiOverride) {
    ...
    int uid = app.uid;
    int[] gids = null;
    int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
    boolean externalStorageAccess = false;
    if (!app.isolated) {
        ...
        gids = computeGidsForProcess(mountExternal, uid, permGids, externalStorageAccess);
    }
    ...
    final String entryPoint = "android.app.ActivityThread";

    return startProcessLocked(hostingRecord, entryPoint, app, uid, gids,
            runtimeFlags, zygotePolicyFlags, mountExternal, seInfo, requiredAbi,
            instructionSet, invokeWith, startTime);
}

这里最重要的不是每个参数的细枝末节,而是它说明了两件事:

  • system_server 会先把 UID / GID / 挂载模式 / ABI / 运行时标志 / SELinux 信息 这些参数准备好;
  • 新进程的 Java 入口类已经确定为 android.app.ActivityThread

这意味着,到 ProcessList 这一层为止,AMS 已经把“要启动哪个进程、用什么身份启动、启动后先跑哪个入口”都确定下来了。下一步才是把这些信息发给 Zygote。

2.3 发给 Zygote:Process.start 只是桥接,真正通信由 ZygoteProcess 完成

ProcessList 继续调用 startProcess(...),最终落到 Process.start()

// frameworks/base/services/core/java/com/android/server/am/ProcessList.java
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) {
    ...
    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.getDisabledCompatChanges(), pkgDataInfoMap,
            allowlistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
            new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
    ...
}
// frameworks/base/core/java/android/os/Process.java
public static ProcessStartResult start(@NonNull final String processClass,
        @Nullable final String niceName, int uid, int gid, @Nullable int[] gids,
        int runtimeFlags, int mountExternal, int targetSdkVersion, @Nullable String seInfo,
        @NonNull String abi, @Nullable String instructionSet, @Nullable String appDataDir,
        @Nullable String invokeWith, @Nullable String packageName, int zygotePolicyFlags,
        boolean isTopApp, @Nullable long[] disabledCompatChanges,
        @Nullable Map<String, Pair<String, Long>> pkgDataInfoMap,
        @Nullable Map<String, Pair<String, Long>> whitelistedDataInfoMap,
        boolean bindMountAppsData, boolean bindMountAppStorageDirs,
        @Nullable String[] zygoteArgs) {
    return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
            runtimeFlags, mountExternal, targetSdkVersion, seInfo,
            abi, instructionSet, appDataDir, invokeWith, packageName,
            zygotePolicyFlags, isTopApp, disabledCompatChanges,
            pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
            bindMountAppStorageDirs, zygoteArgs);
}

Process.start() 本身并不创建进程,它只是把参数继续转交给 ZygoteProcess。真正和 Zygote 通信的关键点有两个:

// frameworks/base/core/java/android/os/ZygoteProcess.java
public final Process.ProcessStartResult start(...) {
    return startViaZygote(processClass, niceName, uid, gid, gids,
            runtimeFlags, mountExternal, targetSdkVersion, seInfo,
            abi, instructionSet, appDataDir, invokeWith, false,
            packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges,
            pkgDataInfoMap, allowlistedDataInfoList, bindMountAppsData,
            bindMountAppStorageDirs, zygoteArgs);
}

private Process.ProcessStartResult startViaZygote(...) throws ZygoteStartFailedEx {
    ArrayList<String> argsForZygote = new ArrayList<>();
    argsForZygote.add("--runtime-args");
    argsForZygote.add("--setuid=" + uid);
    argsForZygote.add("--setgid=" + gid);
    argsForZygote.add("--runtime-flags=" + runtimeFlags);
    ...
    synchronized (mLock) {
        return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
                zygotePolicyFlags, argsForZygote);
    }
}

这里表达的是一个很清楚的模型:

system_server 不直接 fork; 它只是把启动参数编码成 Zygote 协议,再通过 socket 发给 Zygote。

2.4 Zygote 接单并 fork:父进程返回 pid,子进程进入下一阶段

ZygoteProcess 在本端把参数发出去以后,会同步等待结果:

// frameworks/base/core/java/android/os/ZygoteProcess.java
private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult(
        ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {
    final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
    final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;

    zygoteWriter.write(msgStr);
    zygoteWriter.flush();

    Process.ProcessStartResult result = new Process.ProcessStartResult();
    result.pid = zygoteInputStream.readInt();
    result.usingWrapper = zygoteInputStream.readBoolean();
    ...
    return result;
}

另一侧,Zygote 一直在 runSelectLoop() 里等请求:

// frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
Runnable runSelectLoop(String abiList) {
    ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
    ArrayList<ZygoteConnection> peers = new ArrayList<>();
    socketFDs.add(mZygoteSocket.getFileDescriptor());
    peers.add(null);

    while (true) {
        ...
        if (pollIndex == 0) {
            ZygoteConnection newPeer = acceptCommandPeer(abiList);
            peers.add(newPeer);
            socketFDs.add(newPeer.getFileDescriptor());
        } else if (pollIndex < usapPoolEventFDIndex) {
            ZygoteConnection connection = peers.get(pollIndex);
            final Runnable command = connection.processCommand(this, multipleForksOK);
            ...
        }
    }
}

收到请求后,ZygoteConnection.processCommand() 才会真正调用 forkAndSpecialize()

// frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
Runnable processCommand(ZygoteServer zygoteServer, boolean multipleOK) {
    ...
    pid = com.android.internal.os.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.mAllowlistedDataInfoList, parsedArgs.mBindMountAppDataDirs,
            parsedArgs.mBindMountAppStorageDirs,
            parsedArgs.mBindMountSyspropOverrides);

    if (pid == 0) {
        zygoteServer.setForkChild();
        zygoteServer.closeServerSocket();
        return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);
    } else {
        handleParentProc(pid, serverPipeFd);
        return null;
    }
}

这一段正好把边界切成父子两边:

  • 父进程(Zygote):拿到 pid > 0,继续通过 socket 把结果回给 system_server;
  • 子进程(新应用进程)pid == 0,进入 handleChildProc(...),后续会切到 ActivityThread.main()

本文只停在父进程把 pid 返回给 system_server 这一刻。因为从这里开始,问题已经从“怎么 fork 出新进程”切换成“新进程起来之后怎么初始化自己”,那是下一篇的主题。

3. 关键设计点

3.1 system_server 只描述启动请求,Zygote 才真正 fork

AMS / ProcessList 负责准备身份、ABI、入口类和运行参数;真正调用 forkAndSpecialize() 的是 Zygote。这样分层后,进程管理策略和进程创建机制就被干净拆开了。

3.2 Java 入口在 fork 前就已经确定为 ActivityThread

ProcessList.startProcessLocked() 里直接把 entryPoint 设成 android.app.ActivityThread。这说明应用进程被拉起来之后,并不是“再随机找个入口”,而是从进程创建阶段就已经约定好了后续 Java 世界的接管点。

3.3 socket 协议是这里的桥接层

startViaZygote() 把参数组织成 Zygote 能识别的参数列表,attemptZygoteSendArgsAndGetResult() 负责收发结果。本质上,这一层就是 system_server 到 Zygote 的协议桥接

4. 总结

应用程序进程启动阶段的本质,是 system_server 准备启动参数,Zygote 执行 fork,并把新进程 pid 回传给 system_server

ProcessStartResult 返回时,这个阶段就结束了。接下来新的应用进程会从 ActivityThread.main() 开始进入主线程初始化和 bindApplication 绑定流程,这正是下一篇要讲的内容。