brief introduction
Based on the source code analysis of Android 11.0, this paper analyzes how the Android process is created step by step
1, Overview
1.1 system_server process and Zygote process
system_server process: used to manage the entire Java framework layer, including various system services such as ActivityManager and PowerManager;
Zygote process: it is the first java process of Android system. Zygote is the parent process of all Java processes, including system_ The server process and all App processes are child processes of zygote. Note that this refers to child processes, not child threads.
1.2 process creation diagram
(1) App initiation process: when an application is launched from the desktop, the initiation process is the process of the Launcher; When a remote process is started from within an app, the sending process is the process of the app. The initiating process first sends a message to the system through the binder_ Server process
(2) system_server process: calling process The start () method sends a request to create a new process to the zygote process through the socket
(3) zygote process: executing zygoteinit After main(), it enters the runSelectLoop() loop. When there is a client connection, it will execute zygoteconnection RunOnce () method, and then fork out the new application process after layer by layer calling
(4) new process: executing handleChildProc method, and finally calling ActivityThread.main() method.
Next, from system_ The server process initiates a request to Zygote to create a process, and then to the operation of the new process.
2, system_server initiates the request
2.1 Process.start
public class Process { /** * State associated with the zygote process. * @hide */ public static final ZygoteProcess ZYGOTE_PROCESS = new ZygoteProcess(); 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); } }
/** * Maintains communication state with the zygote processes. This class is responsible * for the sockets opened to the zygotes and for starting processes on behalf of the * {@link android.os.Process} class. * * {@hide} */ public class ZygoteProcess { ... public final Process.ProcessStartResult start(@NonNull final String processClass, 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>> allowlistedDataInfoList, boolean bindMountAppsData, boolean bindMountAppStorageDirs, @Nullable String[] zygoteArgs) { ... try { return startViaZygote(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false, packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges, pkgDataInfoMap, allowlistedDataInfoList, bindMountAppsData, bindMountAppStorageDirs, zygoteArgs); } catch (ZygoteStartFailedEx ex) { ... } } private Process.ProcessStartResult startViaZygote(@NonNull final String processClass, @Nullable final String niceName, final int uid, final int gid, @Nullable final int[] gids, int runtimeFlags, int mountExternal, int targetSdkVersion, @Nullable String seInfo, @NonNull String abi, @Nullable String instructionSet, @Nullable String appDataDir, @Nullable String invokeWith, boolean startChildZygote, @Nullable String packageName, int zygotePolicyFlags, boolean isTopApp, @Nullable long[] disabledCompatChanges, @Nullable Map<String, Pair<String, Long>> pkgDataInfoMap, @Nullable Map<String, Pair<String, Long>> allowlistedDataInfoList, boolean bindMountAppsData, boolean bindMountAppStorageDirs, @Nullable String[] extraArgs) throws ZygoteStartFailedEx { ArrayList<String> argsForZygote = new ArrayList<>(); // --runtime-args, --setuid=, --setgid=, // and --setgroups= must go first argsForZygote.add("--runtime-args"); argsForZygote.add("--setuid=" + uid); argsForZygote.add("--setgid=" + gid); argsForZygote.add("--runtime-flags=" + runtimeFlags); if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) { argsForZygote.add("--mount-external-default"); } else if (mountExternal == Zygote.MOUNT_EXTERNAL_INSTALLER) { argsForZygote.add("--mount-external-installer"); } else if (mountExternal == Zygote.MOUNT_EXTERNAL_PASS_THROUGH) { argsForZygote.add("--mount-external-pass-through"); } else if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) { argsForZygote.add("--mount-external-android-writable"); } argsForZygote.add("--target-sdk-version=" + targetSdkVersion); // --setgroups is a comma-separated list if (gids != null && gids.length > 0) { final StringBuilder sb = new StringBuilder(); sb.append("--setgroups="); final int sz = gids.length; for (int i = 0; i < sz; i++) { if (i != 0) { sb.append(','); } sb.append(gids[i]); } argsForZygote.add(sb.toString()); } if (niceName != null) { argsForZygote.add("--nice-name=" + niceName); } if (seInfo != null) { argsForZygote.add("--seinfo=" + seInfo); } if (instructionSet != null) { argsForZygote.add("--instruction-set=" + instructionSet); } if (appDataDir != null) { argsForZygote.add("--app-data-dir=" + appDataDir); } if (invokeWith != null) { argsForZygote.add("--invoke-with"); argsForZygote.add(invokeWith); } if (startChildZygote) { argsForZygote.add("--start-child-zygote"); } if (packageName != null) { argsForZygote.add("--package-name=" + packageName); } if (isTopApp) { argsForZygote.add(Zygote.START_AS_TOP_APP_ARG); } if (pkgDataInfoMap != null && pkgDataInfoMap.size() > 0) { StringBuilder sb = new StringBuilder(); sb.append(Zygote.PKG_DATA_INFO_MAP); sb.append("="); boolean started = false; for (Map.Entry<String, Pair<String, Long>> entry : pkgDataInfoMap.entrySet()) { if (started) { sb.append(','); } started = true; sb.append(entry.getKey()); sb.append(','); sb.append(entry.getValue().first); sb.append(','); sb.append(entry.getValue().second); } argsForZygote.add(sb.toString()); } if (allowlistedDataInfoList != null && allowlistedDataInfoList.size() > 0) { StringBuilder sb = new StringBuilder(); sb.append(Zygote.ALLOWLISTED_DATA_INFO_MAP); sb.append("="); boolean started = false; for (Map.Entry<String, Pair<String, Long>> entry : allowlistedDataInfoList.entrySet()) { if (started) { sb.append(','); } started = true; sb.append(entry.getKey()); sb.append(','); sb.append(entry.getValue().first); sb.append(','); sb.append(entry.getValue().second); } argsForZygote.add(sb.toString()); } if (bindMountAppStorageDirs) { argsForZygote.add(Zygote.BIND_MOUNT_APP_STORAGE_DIRS); } if (bindMountAppsData) { argsForZygote.add(Zygote.BIND_MOUNT_APP_DATA_DIRS); } if (disabledCompatChanges != null && disabledCompatChanges.length > 0) { StringBuilder sb = new StringBuilder(); sb.append("--disabled-compat-changes="); int sz = disabledCompatChanges.length; for (int i = 0; i < sz; i++) { if (i != 0) { sb.append(','); } sb.append(disabledCompatChanges[i]); } argsForZygote.add(sb.toString()); } argsForZygote.add(processClass); if (extraArgs != null) { Collections.addAll(argsForZygote, extraArgs); } synchronized(mLock) { // The USAP pool can not be used if the application will not use the systems graphics // driver. If that driver is requested use the Zygote application start path. return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), zygotePolicyFlags, argsForZygote); } } /** * Sends an argument list to the zygote process, which starts a new child * and returns the child's pid. Please note: the present implementation * replaces newlines in the argument list with spaces. * * @throws ZygoteStartFailedEx if process start failed for any reason */ @GuardedBy("mLock") private Process.ProcessStartResult zygoteSendArgsAndGetResult( ZygoteState zygoteState, int zygotePolicyFlags, @NonNull ArrayList<String> args) throws ZygoteStartFailedEx { ... return attemptZygoteSendArgsAndGetResult(zygoteState, msgStr); } 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(); //Wait for the socket server (i.e. zygote) to return the newly created process pid; result.pid = zygoteInputStream.readInt(); result.usingWrapper = zygoteInputStream.readBoolean(); if (result.pid < 0) { throw new ZygoteStartFailedEx("fork() failed"); } return result; } catch (IOException ex) { ... } } }
The main function of the zygoteSendArgsAndGetResult method is to send a parameter list to the Zygote process through the socket channel, and then enter the blocking waiting state until the remote socket server sends back the newly created process pid.
2.2 ZygoteProcess.openZygoteSocketIfNeeded
public class ZygoteProcess { ... /** * Tries to open a session socket to a Zygote process with a compatible ABI if one is not * already open. If a compatible session socket is already open that session socket is returned. * This function may block and may have to try connecting to multiple Zygotes to find the * appropriate one. Requires that mLock be held. */ @GuardedBy("mLock") private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx { try { attemptConnectionToPrimaryZygote(); if (primaryZygoteState.matches(abi)) { return primaryZygoteState; } if (mZygoteSecondarySocketAddress != null) { // The primary zygote didn't match. Try the secondary. attemptConnectionToSecondaryZygote(); if (secondaryZygoteState.matches(abi)) { return secondaryZygoteState; } } } catch (IOException ioe) { throw new ZygoteStartFailedEx("Error connecting to zygote", ioe); } throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi); } /** * Creates a ZygoteState for the primary zygote if it doesn't exist or has been disconnected. */ @GuardedBy("mLock") private void attemptConnectionToPrimaryZygote() throws IOException { if (primaryZygoteState == null || primaryZygoteState.isClosed()) { //Initiate the connect() operation to the master zygote primaryZygoteState = ZygoteState.connect(mZygoteSocketAddress, mUsapPoolSocketAddress); maybeSetApiDenylistExemptions(primaryZygoteState, false); maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState); } } /** * Creates a ZygoteState for the secondary zygote if it doesn't exist or has been disconnected. */ @GuardedBy("mLock") private void attemptConnectionToSecondaryZygote() throws IOException { if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) { //When the primary zygote fails to match successfully, the second zygote is used to initiate the connect() operation secondaryZygoteState = ZygoteState.connect(mZygoteSecondarySocketAddress, mUsapPoolSecondarySocketAddress); maybeSetApiDenylistExemptions(secondaryZygoteState, false); maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState); } } }
The openzygotesocket ifneeded (ABI) method selects whether to communicate with zygote or zygote64 according to the current abi.
Since system_ The zygoteSendArgsAndGetResult() method of the server process sends a message to the Zygote process through the socket. At this time, the Zygote process will wake up to respond to the request of the socket client (i.e. system_server). The next operation is to create a process in Zygote.
3, Zygote create process
The Zygote process is created by the init process, and the ZygoteInit. is invoked after the process starts. The main () method creates a socket pipe, preloads resources, and then the process runSelectLoop() method.
3.1 ZygoteInit.main
/** * Startup class for the zygote process. * * Pre-initializes some classes, and then waits for commands on a UNIX domain socket. Based on these * commands, forks off child processes that inherit the initial state of the VM. * * Please see {@link ZygoteArguments} for documentation on the client protocol. * * @hide */ public class ZygoteInit { ... /** * This is the entry point for a Zygote process. It creates the Zygote server, loads resources, * and handles other tasks related to preparing the process for forking into applications. * * This process is started with a nice value of -20 (highest priority). All paths that flow * into new processes are required to either set the priority to the default value or terminate * before executing any non-system code. The native side of this occurs in SpecializeCommon, * while the Java Language priority is changed in ZygoteInit.handleSystemServerProcess, * ZygoteConnection.handleChildProc, and Zygote.usapMain. * * @param argv Command line arguments used to specify the Zygote's configuration. */ @UnsupportedAppUsage 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 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(); } } }
3.2 ZygoteServer.runSelectLoop
/** * Server socket class for zygote processes. * * Provides functions to wait for commands on a UNIX domain socket, and fork * off child processes that inherit the initial state of the VM.% * * Please see {@link ZygoteArguments} for documentation on the * client protocol. */ class ZygoteServer { ... /** * Runs the zygote process's select loop. Accepts new connections as * they happen, and reads commands from connections one spawn-request's * worth at a time. */ Runnable runSelectLoop(String abiList) { ArrayList<FileDescriptor> socketFDs = new ArrayList<>(); ArrayList<ZygoteConnection> peers = new ArrayList<>(); //mZygoteSocket is the server in socket communication, that is, zygote process, which is saved to socketFDs[0] socketFDs.add(mZygoteSocket.getFileDescriptor()); peers.add(null); ... while (true) { fetchUsapPoolPolicyPropsWithMinInterval(); mUsapPoolRefillAction = UsapPoolRefillAction.NONE; int[] usapPipeFDs = null; StructPollfd[] pollFDs; // Allocate enough space for the poll structs, taking into account // the state of the USAP pool for this Zygote (could be a // regular Zygote, a WebView Zygote, or an AppZygote). if (mUsapPoolEnabled) { usapPipeFDs = Zygote.getUsapPipeFDs(); pollFDs = new StructPollfd[socketFDs.size() + 1 + usapPipeFDs.length]; } else { pollFDs = new StructPollfd[socketFDs.size()]; } /* * For reasons of correctness the USAP pool pipe and event FDs * must be processed before the session and server sockets. This * is to ensure that the USAP pool accounting information is * accurate when handling other requests like API blacklist * exemptions. */ int pollIndex = 0; for (FileDescriptor socketFD : socketFDs) { pollFDs[pollIndex] = new StructPollfd(); pollFDs[pollIndex].fd = socketFD; pollFDs[pollIndex].events = (short) POLLIN; ++pollIndex; } ... int pollReturnValue; try { //Handle the polling status. When an event arrives in pollFds, execute it down, otherwise it will be blocked here pollReturnValue = Os.poll(pollFDs, pollTimeoutMs); } catch (ErrnoException ex) { throw new RuntimeException("poll failed", ex); } if (pollReturnValue == 0) { // The poll timeout has been exceeded. This only occurs when we have finished the // USAP pool refill delay period. mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP; mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED; } else { boolean usapPoolFDRead = false; while (--pollIndex >= 0) { if ((pollFDs[pollIndex].revents & POLLIN) == 0) { continue; } if (pollIndex == 0) { // Zygote server socket // The ZygoteConnection object is created and added to socketFDs. ZygoteConnection newPeer = acceptCommandPeer(abiList); peers.add(newPeer); socketFDs.add(newPeer.getFileDescriptor()); } else if (pollIndex < usapPoolEventFDIndex) { // Session socket accepted from the Zygote server socket try { //Pollindex > 0 means receiving data from the opposite end through socket and performing corresponding operations ZygoteConnection connection = peers.get(pollIndex); 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; } else { // We're in the server - we should never have any commands to run. if (command != null) { throw new IllegalStateException("command != null"); } // We don't know whether the remote side of the socket was closed or // not until we attempt to read from it from processOneCommand. This // shows up as a regular POLLIN event in our regular processing // loop. if (connection.isClosedByPeer()) { connection.closeSocket(); peers.remove(pollIndex); //After processing, remove the file descriptor from socketFDs socketFDs.remove(pollIndex); } } } catch (Exception e) { ... } finally { // Reset the child flag, in the event that the child process is a child- // zygote. The flag will not be consulted this loop pass after the // Runnable is returned. mIsForkChild = false; } } else { ... } } ... } } }
When there is no connection request, it will enter the sleep state. When there is a connection request to create a new process, wake up the Zygote process, create the Socket channel ZygoteConnection with the acceptCommandPeer method, and then execute the processOneCommand() method of ZygoteConnection.
3.3 ZygoteConnection.processOneCommand
/** * A connection that can make spawn requests. */ class ZygoteConnection { /** * Reads one start command from the command socket. If successful, a child is forked and a * {@code Runnable} that calls the childs main method (or equivalent) is returned in the child * process. {@code null} is always returned in the parent process (the zygote). * * If the client closes the socket, an {@code EOF} condition is set, which callers can test * for by calling {@code ZygoteConnection.isClosedByPeer}. */ Runnable processOneCommand(ZygoteServer zygoteServer) { String[] args; try { //Read the parameter list sent by the socket client args = Zygote.readArgumentList(mSocketReader); } catch (IOException ex) { throw new IllegalStateException("IOException on command socket", ex); } ... //Parse the parameters passed from the binder client into the ZygoteArguments object format ZygoteArguments parsedArgs = new ZygoteArguments(args); ... 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); try { if (pid == 0) { // in child zygoteServer.setForkChild(); zygoteServer.closeServerSocket(); IoUtils.closeQuietly(serverPipeFd); serverPipeFd = null; return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote); } else { // In the parent. A pid < 0 indicates a failure and will be handled in // handleParentProc. IoUtils.closeQuietly(childPipeFd); childPipeFd = null; handleParentProc(pid, serverPipeFd); return null; } } finally { IoUtils.closeQuietly(childPipeFd); IoUtils.closeQuietly(serverPipeFd); } } } public final class Zygote { ... 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); if (pid == 0) { // Note that this event ends at the end of handleChildProc, Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork"); // If no GIDs were specified, don't make any permissions changes based on groups. if (gids != null && gids.length > 0) { NetworkUtils.setAllowNetworkingForProcess(containsInetGid(gids)); } } // Set the Java Language thread priority to the default value for new apps. Thread.currentThread().setPriority(Thread.NORM_PRIORITY); //After fork ing the new process, start the Daemon thread of Zygote ZygoteHooks.postForkCommon(); return pid; } }
3.4 nativeForkAndSpecialize
Nativeforkendspecialize() finally calls the fork method through JNI
com_android_internal_os_Zygote.cpp static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( JNIEnv* env, jclass, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs) { ... pid_t pid = ForkCommon(env, false, fds_to_close, fds_to_ignore, true); ... return pid; } // Utility routine to fork a process from the zygote. static pid_t ForkCommon(JNIEnv* env, bool is_system_server, const std::vector<int>& fds_to_close, const std::vector<int>& fds_to_ignore, bool is_priority_fork) { SetSignalHandlers(); // Curry a failure function. auto fail_fn = std::bind(ZygoteFailure, env, is_system_server ? "system_server" : "zygote", nullptr, _1); // Temporarily block SIGCHLD during forks. The SIGCHLD handler might // log, which would result in the logging FDs we close being reopened. // This would cause failures because the FDs are not whitelisted. // // Note that the zygote process is single threaded at this point. BlockSignal(SIGCHLD, fail_fn); // Close any logging related FDs before we start evaluating the list of // file descriptors. __android_log_close(); AStatsSocket_close(); // If this is the first fork for this zygote, create the open FD table. If // it isn't, we just need to check whether the list of open files has changed // (and it shouldn't in the normal case). if (gOpenFdTable == nullptr) { gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore, fail_fn); } else { gOpenFdTable->Restat(fds_to_ignore, fail_fn); } android_fdsan_error_level fdsan_error_level = android_fdsan_get_error_level(); // Purge unused native memory in an attempt to reduce the amount of false // sharing with the child process. By reducing the size of the libc_malloc // region shared with the child process we reduce the number of pages that // transition to the private-dirty state when malloc adjusts the meta-data // on each of the pages it is managing after the fork. mallopt(M_PURGE, 0); //fork subprocess pid_t pid = fork(); ... return pid; }
The main work of fork() is to find the idle process number pid, and then copy the process information from the parent process, such as data segments and code segments, and the code to be executed by the child process after fork(). Zygote process is the parent of all Android processes, including system_server and App processes. Zygote uses the fork() method to generate A new process. For new process A, reuse the resources of zygote process itself, plus the resources related to new process A to form A new application process A.
The creation of this App process has been completed. Next, start the work of the newly created App process. In front of zygoteconnection In the processonecommand method, after the zygote process executes forkAndSpecialize(), the newly created App process enters the handleChildProc() method, and the following operations run in the App process.
4, New process running
4.1 ZygoteConnection.handleChildProc
In the processOneCommand() process, after calling the forkAndSpecialize() to create the new process, return the value pid=0 (that is, running in the subprocess) and continue to start executing the handleChildProc() method.
ZygoteConnection.java /** * Handles post-fork setup of child proc, closing sockets as appropriate, * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller * if successful or returning if failed. * * @param parsedArgs non-null; zygote args * @param pipeFd null-ok; pipe for communication back to Zygote. * @param isZygote whether this new child process is itself a new Zygote. */ private Runnable handleChildProc(ZygoteArguments parsedArgs, FileDescriptor pipeFd, boolean isZygote) { /* * By the time we get here, the native code has closed the two actual Zygote * socket connections, and substituted /dev/null in their place. The LocalSocket * objects still need to be closed properly. */ //Close the connection at both ends of Zygote's socket closeSocket(); //Set process name Zygote.setAppProcessName(parsedArgs, TAG); // End of the postFork event. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); if (parsedArgs.mInvokeWith != null) { WrapperInit.execApplication(parsedArgs.mInvokeWith, parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion, VMRuntime.getCurrentInstructionSet(), pipeFd, parsedArgs.mRemainingArgs); // Should not get here. throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned"); } else { if (!isZygote) { //Execute the main() method of the target class return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion, parsedArgs.mDisabledCompatChanges, parsedArgs.mRemainingArgs, null /* classLoader */); } else { return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion, parsedArgs.mRemainingArgs, null /* classLoader */); } } }
/** * Startup class for the zygote process. * * Pre-initializes some classes, and then waits for commands on a UNIX domain socket. Based on these * commands, forks off child processes that inherit the initial state of the VM. * * Please see {@link ZygoteArguments} for documentation on the client protocol. * * @hide */ public class ZygoteInit { /** * 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> [--] <start class name> <args> * </ul> * * @param targetSdkVersion target SDK version * @param disabledCompatChanges set of disabled compat changes for the process (all others * are enabled) * @param argv arg strings */ ublic static final Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges, String[] argv, ClassLoader classLoader) { if (RuntimeInit.DEBUG) { Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote"); } Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit"); RuntimeInit.redirectLogStreams(); // Some common initialization RuntimeInit.commonInit(); // zygote initialization ZygoteInit.nativeZygoteInit(); // Application initialization return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv, classLoader); } }
jni method corresponding to nativeZygoteInit()
AndroidRuntime.cpp static AndroidRuntime* gCurRuntime = NULL; static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz) { gCurRuntime->onZygoteInit(); } app_main.cpp virtual void onZygoteInit() { sp<ProcessState> proc = ProcessState::self(); ALOGV("App process: starting thread pool.\n"); //Start a new binder thread proc->startThreadPool(); }
ProcessState::self(): the main work is to call open() to open the / dev/binder driver device, then use mmap() to map the address space of the kernel, and assign fd of Binder driver to the variable mDriverFD in ProcessState object for interactive operation.
startThreadPool() is to create a new binder thread and continue talkWithDriver().
4.2 RuntimeInit.applicationInit
/** * Main entry point for runtime initialization. Not for * public consumption. * @hide */ public class RuntimeInit { 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 //Here args Startclass is "Android app. ActivityThread” 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. * * @param className Fully-qualified class name * @param argv Argument vector for main() * @param classLoader the classLoader to load {@className} with */ protected static Runnable findStaticMain(String className, String[] argv, ClassLoader classLoader) { Class<?> cl; try { cl = Class.forName(className, true, classLoader); } catch (ClassNotFoundException ex) { throw new RuntimeException( "Missing class when invoking static main " + className, ex); } Method m; try { m = cl.getMethod("main", new Class[] { String[].class }); } catch (NoSuchMethodException ex) { throw new RuntimeException( "Missing static main on " + className, ex); } catch (SecurityException ex) { throw new RuntimeException( "Problem getting static main on " + className, ex); } int modifiers = m.getModifiers(); if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) { throw new RuntimeException( "Main method is not public and static on " + className); } /* * 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 { //According to the passed parameters, activitythread. Is called here Main() method mMethod.invoke(null, new Object[] { mArgs }); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } catch (InvocationTargetException ex) { Throwable cause = ex.getCause(); if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } else if (cause instanceof Error) { throw (Error) cause; } throw new RuntimeException(ex); } } } }
Next, go to caller Run () method, that is, methodandargscaller run(). At this point, I finally entered the main() method of the ActivityThread class.
5, Summary
Process. The start () method is a blocking operation. Wait until the process creation is completed and the corresponding new process pid is returned. When the app is started for the first time or the remote Service is started, that is, androidmanifest When the process:remote attribute is defined in the XML file, you need to create a process. For example, when a user clicks an app icon on the desktop and the desktop itself is an app (i.e. Launcher App), the process in which the Launcher is located is the initiator of the new process, which sends a message to the system through the binder_ Server process, which carries the core services of the whole java framework. system_server process from process Start and execute the creation process. The flowchart (from the perspective of process) is as follows:
In the figure above, system_ The server processes communicate with the zygote process through the socket IPC channel. After the new fork process, zygote returns to the fork two times, which is called once in the zygote process, and returns once in the zygote process and sub process, so that it can enter the sub process to execute the code. The process of calling the flowchart:
(1) system_server process: through process The start () method initiates a request to create a new process. It will first collect various new process uid, gid, nice name and other related parameters, and then send them to the zygote process through the socket channel;
(2) zygote process: received system_ The parameters sent by the server process are encapsulated into Arguments objects. The forkAndSpecialize() method is the most core link in the process of process creation. Its specific work is to execute the following three methods in turn:
preFork(): first stop the operation of the four Daemon sub threads of Zygote (java heap memory collation thread, offline reference queue thread, destructor thread and monitoring thread) and initialize the gc heap;
nativeForkAndSpecialize(): call linux fork() to create a new process, create a thread pool for Java heap processing, reset gc performance data, set the signal processing function of the process, and start the JDWP thread;
postForkCommon(): four Daemon child threads suspended before startup.
(3) New process: enter the handleChildProc() method, set the process name, open the binder driver, and start a new binder thread; Then set the art virtual machine parameter, and then call the main () method of the target class, that is, activity main() method.
If the subsequent process is startActivity, it will enter the onCreate/onStart/onResume and other life cycles of the Activity; If it is a startService, it will enter the onCreate and other life cycles of the Service.
system_ The server process waits for zygote to return the process creation completion (ZygoteConnection.handleParentProc). Once zygote When the forkandspecialize () method is completed, go its separate ways and zygote tells the system_ The server process has been created, and the child process continues to perform subsequent handleChildProc operations
RuntimeInit. The Java method nativeZygoteInit() will call onZygoteInit(). In this process, startThreadPool() will create a Binder thread pool. That is, each process must contain at least one Binder thread whether it contains any activity and other components.