Introduction to the

Apps run in their own processes, and each process has an independent Dalvik VIRTUAL machine instance. Only with Dalvik can Android Java programs run. It can be understood that the process is running in an isolated user environment, so that none of the interference. Four commonly used components, to be able to run, first of all, the APP process has been ready.

The purpose of this article is to learn how processes are created.

Note: source version is 8.0, there are differences between versions, but does not affect the core design

Parameters required for starting a process are available

For example, during the startup of an Activity, when AMS finds that the application process on which the Activity depends has not been started, it requests Zygote to start the application process.

Portal: What happens when the Activity starts

The code set ActivityManagerService. AMS. StartProcessLocked ()

private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) { ...... try { ...... // process uid int uid = app.uid; // GIDS int[] gids = null; int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;if(! app.isolated){ ...... // Remember resource access permissionsif (ArrayUtils.isEmpty(permGids)) {
                    gids = new int[3];
                } else{ gids = new int[permGids.length + 3]; System.arraycopy(permGids, 0, gids, 3, permGids.length); } gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid)); gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid)); // shareUserId gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid)); }... // Specify the application entryif (entryPoint == null) entryPoint = "android.app.ActivityThread";
            if(...). {}else{// Give Zygote to incubate startResult = process. start(entryPoint, app.processName, uid, uid, gids, debugFlags, mountExternal, app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet, app.info.dataDir, invokeWith, entryPointArgs); }}}Copy the code

The total app of the code is ProcessRecord, which describes a process. The information related to the process is stored in ProcessRecord. AMS can also know the process condition through ProcessRecord.

UID

A UID identifies an application process. An application is assigned a UID when it is installed, and the UID remains the same while the application is alive (and not uninstalled). For normal applications, GID = UID. Through the command

adb shell ps | grap packageName

You can view information about running applications

As mentioned, the UID remains the same no matter how many times the APP process is killed and restarted.

GIDS

GIDS are related to the specific permission applied for by the Application. When the corresponding permission applied is granted, there are corresponding GIDS. Therefore, GIDS are about allowing or restricting the Application to access device resources. For example, you often apply for Internet access on Manifest

<uses-permission android:name="android.permission.INTERNET"/>
Copy the code

When allowed, groups have corresponding ids in GIDS

shareUserId

The shareUserId is also important in GIDS, so a brief understanding of the sandbox model is required.

Each application runs in its own environment (sandbox) and, by default, cannot interact with each other. Applications running in the sandbox cannot access the system or resources if they are not assigned permissions. Therefore, applications running the Dalvik VIRTUAL Machine can get the same security protection, and the programs confined in their sandboxes do not interfere with each other, so as to minimize the damage to other programs or by other programs. Under the sandbox mechanism, applications do not trust each other and run separately from each other.

The above two sources

Back to the main body, after the relevant data for starting the process is ready, hand over to the next node to continue the execution. Also note the assigned a value of “android. App. ActivityThread” variable entryPoint, follow-up will be switched by the variable ActivityThread. The main (), and also specifies the application of the entrance.

Start to prepare

Then call the link for the Process. The start () – > ZygoteProcess. The start () – > ZygoteProcess. StartViaZygote ()

private Process.ProcessStartResult startViaZygote(final String processClass, final String niceName,final int uid, final int gid,final int[] gids, int debugFlags, int mountExternal, int targetSdkVersion, String seInfo, String abi, String instructionSet, String appDataDir, String invokeWith, String[] extraArgs) throws ZygoteStartFailedEx {ArrayList<String> argsForZygote = new ArrayList<String>(); Argsforzygote.add ("--runtime-args");
        argsForZygote.add("--setuid=" + uid);
        argsForZygote.add("--setgid="+ gid); // Other parameters depend on specific conditions...... Synchronized (mLock) {// Obtain the LocalkSocket to connect to the Zygote process, and write parameters to the Zygote process through LocalSocket // for the Zygote process to createreturnzygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote); }}Copy the code

The Zygote process can write the request for the new process by preparing the parameters needed to start the process as necessary and then obtaining the ZygoteState used to connect to the process.

    private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
        Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");

        if(primaryZygoteState == null || primaryZygoteState.isClosed()) { try { primaryZygoteState = ZygoteState.connect(mSocket);  } catch (IOException ioe) { throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe); }}if (primaryZygoteState.matches(abi)) {
            return primaryZygoteState;
        }

        // The primary zygote didn't match. Try the secondary. if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) { try { secondaryZygoteState = ZygoteState.connect(mSecondarySocket); } catch (IOException ioe) { throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe); } } if (secondaryZygoteState.matches(abi)) { return secondaryZygoteState; } throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi); }Copy the code

Both primaryZygoteState and secondaryZygoteState are ZygoteState. Since Android 5.0, 64-bit compilation has been supported on Android, and the 32/64-bit difference has also been introduced in Zygote processes. As the name implies, primaryZygoteState is the primaryZygote and secondaryZygoteState is the secondaryZygote. Whether it is 32-bit or 64-bit depends on the configuration of the specific Zygote file. Using the ABI schema type as a starting point, find the right ZygoteSate.

This ZygoteState is obtained if the ZygoteState you are looking for is not created or the connection state is closed. ZYGOTE_SOCKET

        public static ZygoteState connect(String socketAddress) throws IOException {
            DataInputStream zygoteInputStream = null;
            BufferedWriter zygoteWriter = null;
            final LocalSocket zygoteSocket = new LocalSocket();

            try {
                zygoteSocket.connect(new LocalSocketAddress(socketAddress,
                        LocalSocketAddress.Namespace.RESERVED));

                zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());

                zygoteWriter = new BufferedWriter(new OutputStreamWriter(
                        zygoteSocket.getOutputStream()), 256);
            } catch (IOException ex) {
                try {
                    zygoteSocket.close();
                } catch (IOException ignore) {
                }

                throw ex;
            }

            String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
            Log.i("Zygote"."Process: zygote socket " + socketAddress + " opened, supported ABIS: "
                    + abiListString);

            return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
                    Arrays.asList(abiListString.split(",")));
        }
Copy the code

The newly created LocalSocket is the ZygoteState member variable. The input and output streams used by ZygoteState are taken from LocalSocket. The socketAddress parameter creates the ZYGOTE_SOCKET passed by ZygoteProcess for Process, or “zygote”. So localsocket.connect () will connect to the Socket named “Zygote” in the Zygote process. Once you have a medium to communicate with the Zygote process, you can pass data to or from the Zygote process.

Create a process

Back – > ZygoteProcess. StartViaZygote () – > zygoteSendArgsAndGetResult ()

private static Process.ProcessStartResult zygoteSendArgsAndGetResult( ZygoteState zygoteState, ArrayList<String> args) throws ZygoteStartFailedEx { ...... Writer.write (integer.toString (args.size())); writer.newLine();for(int i = 0; i < sz; i++) { String arg = args.get(i); // Write the startup parameter writer.write(arg); writer.newLine(); } writer.flush(); // Should there be a timeout on this? Process.ProcessStartResult result = new Process.ProcessStartResult(); Result.pid = inputStream.readint (); result.usingWrapper = inputStream.readBoolean();if (result.pid < 0) {
                throw new ZygoteStartFailedEx("fork() failed");
            }
            return result;
        }
Copy the code

After starting data is written to the Zygote process through the output stream, the Zygote process is blocked until it receives data from the Zygote process. Getting the result of the creation means the process is finished.

The Zygote process

For a brief look at creating a Zygote process, see zygoteinit.main ().

Public static void main(String argv[]) {ZygoteServer ZygoteServer = new ZygoteServer(); . try{ ...... // Register the name"zygote的socket"zygoteServer.registerServerSocket(socketName); . . / / receiving request zygoteServer runSelectLoop (abiList); . }}Copy the code

When the Zygote process is created, a server Socket named “Zygote” is registered with the ZygoteServer. This Socket communicates with the new process request when it arrives. When the request arrives, the zygoteServer. RunSelectLoop () receives the request.

    void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
        ......
        boolean done = peers.get(i).runOnce(this);
    }
Copy the code

It is actually handled by the zygoteconnection.runonce () process

boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller { String args[]; Arguments parsedArgs = null; FileDescriptor[] descriptors; Try {// Get the startup parameter args = from LocalSocketreadArgumentList(); descriptors = mSocket.getAncillaryFileDescriptors(); }... Tyr {// parsedArgs = new Arguments(args); . // frok() pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo, parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet, parsedArgs.appDataDir); }... Try {// pid = 0 indicates that a process has been createdif (pid == 0) {
                // in child
                zygoteServer.closeServerSocket();
                IoUtils.closeQuietly(serverPipeFd);
                serverPipeFd = null;
                handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);

                return true; }}Copy the code

After receiving and parsing the startup parameters transmitted by LocalSocket, zygote.forkandSpecialize () actually forks () native out of the process. If PID = 0, the Zygote process has created a child process.

In the current case, the Zygote process is required to hatch the process. Zygote process, as the parent of Android process, contains process information and resource information required by various application processes. Since the child process from Zygote process fork() has Zygote process information such as data segment, code segment, etc., naturally, it also includes Dalvik VIRTUAL machine entity.

After process creation, by ZygoteConnection handleChildProc () – > ZygoteInit. ZygoteInit () to handle matters relating to further

    public static final void zygoteInit(int targetSdkVersion, String[] argv,
            ClassLoader classLoader) throws Zygote.MethodAndArgsCaller {
        if (RuntimeInit.DEBUG) {
            Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
        }

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit"); RuntimeInit.redirectLogStreams(); RuntimeInit.commonInit(); / / create a Binder thread pool ZygoteInit. NativeZygoteInit (); / / when AMS request the newly created thread, awaken ActivityThread. The main () RuntimeInit. ApplicationInit (targetSdkVersion, argv, this); }Copy the code

ApplicationInit () wakes up ActivityThread.main() by reflection, and entryPoint, which was invoked when AMS asked for a new process to start, is also passed to Zygote via LocalSocket as one of the arguments. So this wake-up message is known here.

When AMS requests to start a new process, it does not wait for the process to be created and delays sending the PROC_START_TIMEOUT_MSG message. If a process is successfully created within the PROC_START_TIMEOUT_MSG time, AMS considers that the process failed to start. Therefore, when attaching () after waking up activityThread.main (), AMS will be notified to cancel the PROC_START_TIMEOUT_MSG message to ensure that the subsequent process proceeds.

At this point, you know how the process time is created.

conclusion

reference

An analysis of the Android sandbox model

Android – System process Zygote startup analysis

An analysis of the Android ABI