Android application process creation process

table of Contents
  • Preface
  • step
    • step1 Ams initiates a request to startProcessLocked
    • step2 Zygote received the request
    • step3 handleChildProc - Enter the world of child processes
    • step4 RuntimeInit.zygoteInit - child process environment preparation
      • nativeZygoteInit
      • applicationInit
    • step5 ActivityThread - the entrance to the app
  • to sum up

Preface

Oriental Health Network https://www.9559.org/cn/

We know that when startActivity, in addition to creating Activity-related entities, the system will create a process as needed. This process refers to the Linux-level process entity; as we will talk about later, the creation process is actually a fork, which means from the parent process. copy" a new process. This process must experience inter-process communication between initiators, ams, zygote, etc. This article mainly sorts out the important links of ams in this process, involving the inheritance of virtual machines, the Binder thread pool, how the message loop is established, etc., and the activity of The startup details will be discussed in other chapters.

step

step1 Ams initiates a request to startProcessLocked

ActivityManagerService.java

Start the analysis directly from the entrance of the request to zygote in Ams. Of course, the real start is that Ams receives Binder requests from other applications, so it should be in onTransact that is the real start. I will type a trace later. (Here is a small detail, there are many methods with Locked suffix in the source code, indicating that using this method requires locking because it is not thread-safe).

            // Start the process.  It will either succeed and return a result containing
            // the PID of the new process, or else throw a RuntimeException.
            boolean isActivityProcess = (entryPoint == null);
            if (entryPoint == null) entryPoint = "android.app.ActivityThread";
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
                    app.processName);
            checkTime(startTime, "startProcess: asking zygote to start proc");
            Process.ProcessStartResult startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
                    app.info.dataDir, entryPointArgs);
            checkTime(startTime, "startProcess: returned from zygote!");
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

The more important thing is that this paragraph calls the start method of the Process class and specifies the entry class as ActivityThread.

system    671   641   2348412 179992          0 0000000000 S Binder:641_1

641-671/system_process W:     at android.os.Process.zygoteSendArgsAndGetResult(Process.java:605)
641-671/system_process W:     at android.os.Process.startViaZygote(Process.java:738)
641-671/system_process W:     at android.os.Process.start(Process.java:521)
641-671/system_process W:     at com.android.server.am.ActivityManagerService.startProcessLocked(ActivityManagerService.java:4214)
641-671/system_process W:     at com.android.server.am.ActivityManagerService.startProcessLocked(ActivityManagerService.java:4069)
641-671/system_process W:     at com.android.server.am.ActivityManagerService.startProcessLocked(ActivityManagerService.java:3909)
641-671/system_process W:     at com.android.server.am.ActivityStackSupervisor.startSpecificActivityLocked(ActivityStackSupervisor.java:1441)
641-671/system_process W:     at com.android.server.am.ActivityStack.resumeTopActivityInnerLocked(ActivityStack.java:2741)
641-671/system_process W:     at com.android.server.am.ActivityStack.resumeTopActivityUncheckedLocked(ActivityStack.java:2213)
641-671/system_process W:     at com.android.server.am.ActivityStackSupervisor.resumeFocusedStackTopActivityLocked(ActivityStackSupervisor.java:1859)
641-671/system_process W:     at com.android.server.am.ActivityStack.completePauseLocked(ActivityStack.java:1393)
641-671/system_process W:     at com.android.server.am.ActivityStack.activityPausedLocked(ActivityStack.java:1237)
641-671/system_process W:     at com.android.server.am.ActivityManagerService.activityPaused(ActivityManagerService.java:7393)
641-671/system_process W:     at android.app.ActivityManagerNative.onTransact(ActivityManagerNative.java:571)
641-671/system_process W:     at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:3169)
641-671/system_process W:     at android.os.Binder.execTransact(Binder.java:565)

It can be directly seen from the above trace that after receiving the startActivity request, the pause operation of the current Activity is performed, and then a request is initiated to the Zygote process through the Process.java class. Note that by observing the thread number, you can see that all of this is operated in a binder thread.

Note that Ams and Zygote are connected through a socket, which encapsulates the zygoteState class to do communication-related tasks. It can be clearly seen from the occupancy of fd in the figure below that the zygote process does not use binder.

device:/proc/341/fd # ls -l
ls -l
total 0
lrwx------ 1 root root 64 2021-03-16 17:49 0 -> /dev/null
lrwx------ 1 root root 64 2021-03-16 17:49 1 -> /dev/null
lr-x------ 1 root root 64 2021-03-16 17:49 10 -> /system/framework/core-oj.jar
lr-x------ 1 root root 64 2021-03-16 17:49 11 -> /system/framework/core-libart.jar
lr-x------ 1 root root 64 2021-03-16 17:49 12 -> /system/framework/conscrypt.jar
lr-x------ 1 root root 64 2021-03-16 17:49 13 -> /system/framework/okhttp.jar
lr-x------ 1 root root 64 2021-03-16 17:49 14 -> /system/framework/core-junit.jar
lr-x------ 1 root root 64 2021-03-16 17:49 15 -> /system/framework/bouncycastle.jar
lr-x------ 1 root root 64 2021-03-16 17:49 16 -> /system/framework/ext.jar
lr-x------ 1 root root 64 2021-03-16 17:49 17 -> /system/framework/framework.jar
lr-x------ 1 root root 64 2021-03-16 17:49 18 -> /system/framework/telephony-common.jar
lr-x------ 1 root root 64 2021-03-16 17:49 19 -> /system/framework/voip-common.jar
lrwx------ 1 root root 64 2021-03-16 17:49 2 -> /dev/null
lr-x------ 1 root root 64 2021-03-16 17:49 20 -> /system/framework/ims-common.jar
lr-x------ 1 root root 64 2021-03-16 17:49 21 -> /system/framework/apache-xml.jar
lr-x------ 1 root root 64 2021-03-16 17:49 22 -> /system/framework/org.apache.http.legacy.boot.jar
lrwx------ 1 root root 64 2021-03-16 17:49 23 -> socket:[9842]
lr-x------ 1 root root 64 2021-03-16 17:49 24 -> /system/framework/framework-res.apk
lr-x------ 1 root root 64 2021-03-16 17:49 25 -> /dev/urandom
l-wx------ 1 root root 64 2021-03-16 17:49 5 -> /sys/kernel/debug/tracing/trace_marker
lrwx------ 1 root root 64 2021-03-16 17:49 6 -> /dev/null
lrwx------ 1 root root 64 2021-03-16 17:49 7 -> /dev/null
lrwx------ 1 root root 64 2021-03-16 17:49 8 -> /dev/null
lrwx------ 1 root root 64 2021-03-16 17:49 9 -> socket:[9289]

The call stack of Ams ends when it reaches the top, and then it enters the Zygote process.

step2 Zygote received the request

Suppose that after the initialization of the Zygote process is completed, there must be an infinite loop waiting for requests from other processes, and directly locate the code.

 private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();

        fds.add(sServerSocket.getFileDescriptor());
        peers.add(null);

        while (true) {
            StructPollfd[] pollFds = new StructPollfd[fds.size()];
            for (int i = 0; i < pollFds.length; ++i) {
                pollFds[i] = new StructPollfd();
                pollFds[i].fd = fds.get(i);
                pollFds[i].events = (short) POLLIN;
            }
            try {
                Os.poll(pollFds, -1);
            } catch (ErrnoException ex) {
                throw new RuntimeException("poll failed", ex);
            }
            for (int i = pollFds.length - 1; i >= 0; --i) {
                if ((pollFds[i].revents & POLLIN) == 0) {
                    continue;
                }
                if (i == 0) {
                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
                    peers.add(newPeer);
                    fds.add(newPeer.getFileDesciptor());
                } else {
                    boolean done = peers.get(i).runOnce();
                    if (done) {
                        peers.remove(i);
                        fds.remove(i);
                    }
                }
            }
        }
    }

As you can see, when the request is received, runOnce of the ZygoteConnection class is called to process it.

 boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {

        .......省略代码

        pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
                parsedArgs.appDataDir);

        ......省略代码

    try {
        /*子进程执行pid==0情况,父进程执行else情况*/
        if (pid == 0) {
            /*子进程*/
            // in child
            IoUtils.closeQuietly(serverPipeFd);
            serverPipeFd = null;
            handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);

            // should never get here, the child is expected to either
            // throw ZygoteInit.MethodAndArgsCaller or exec().
            return true;
        } else {
            // in parent...pid of < 0 means failure
            IoUtils.closeQuietly(childPipeFd);
            childPipeFd = null;
            /*Process中的io流监听的pid等信息都是通过下面的代码发出去的*/
            return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
        }
    } finally {
        IoUtils.closeQuietly(childPipeFd);
        IoUtils.closeQuietly(serverPipeFd);
    }
}

The main mystery is the forkAndSpecialize method, which can fork out the native child process, and then return to the calling place; but it will return twice, once for the child process, once for the parent process, this is judged by the return value, and Linux fork The method is similar; if it is a child process, then execute handleChildProc, pay attention to the comments below, which means that the return statement will not be executed, which is the upper selectloop (because the child process does not need to worry about the zygote receiving command loop, and directly executes the app process Task); and the parent process will call handleParentProc and return.

There is a trick to the return of the child process, which is achieved by throwing an exception, so where did it return? Let's look at the main method of ZygoteInit.java.

    public static void main(String argv[]) {
        // Mark zygote start. This ensures that thread creation will throw
        // an error.
        ...
        try {
            Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygoteInit");
            RuntimeInit.enableDdms();
            // Start profiling the zygote initialization.
            SamplingProfilerIntegration.start();
            registerZygoteSocket(socketName);
            preload();
            gcAndFinalize();
            // Zygote process unmounts root storage spaces.
            Zygote.nativeUnmountStorageOnInit();
            ...
            Log.i(TAG, "Accepting command socket connections");
            /*这儿调用的runSelectLoop开启等待循环的*/
            runSelectLoop(abiList);
            
            closeServerSocket();
        } catch (MethodAndArgsCaller caller) {
            caller.run();
        } catch (Throwable ex) {
            Log.e(TAG, "Zygote died with exception", ex);
            closeServerSocket();
            throw ex;
        }
    }

Did you see the familiar figure runSelectLoop, so after Zygote entered the java world, it first entered the main method to make some preparations, and then opened selectLoop to wait for the loop, and after receiving the MethodAndArgsCaller exception thrown by the child process, it will execute caller.run , This is the beginning of the life of the child process that we can see from the call stack. The reason for this is that we can also clear the previous call stack by throwing an exception, removing some forks, settings, etc. It seems that the life of the app is more refreshing. The flow to onCreate is as shown in the figure below.

3199-3199/com.android.myapplication W:     at com.android.myapplication.MainActivity.onCreate(MainActivity.java:42)
3199-3199/com.android.myapplication W:     at android.app.Activity.performCreate(Activity.java:6709)
3199-3199/com.android.myapplication W:     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
3199-3199/com.android.myapplication W:     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2624)
3199-3199/com.android.myapplication W:     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2732)
3199-3199/com.android.myapplication W:     at android.app.ActivityThread.-wrap12(ActivityThread.java)
3199-3199/com.android.myapplication W:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1483)
3199-3199/com.android.myapplication W:     at android.os.Handler.dispatchMessage(Handler.java:102)
3199-3199/com.android.myapplication W:     at android.os.Looper.loop(Looper.java:154)
3199-3199/com.android.myapplication W:     at android.app.ActivityThread.main(ActivityThread.java:6141)
3199-3199/com.android.myapplication W:     at java.lang.reflect.Method.invoke(Native Method)
3199-3199/com.android.myapplication W:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:912)
3199-3199/com.android.myapplication W:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:802)

Haha, we're far from it, let's continue to see what the handleChildProc called by the child process did.

step3 handleChildProc - Enter the world of child processes

.frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java

By convention, look at the call stack first.

private void handleChildProc(Arguments parsedArgs,
        FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
        throws ZygoteInit.MethodAndArgsCaller {
        
        closeSocket();
        ZygoteInit.closeServerSocket();
    
        if (descriptors != null) {
            try {
                Os.dup2(descriptors[0], STDIN_FILENO);
                Os.dup2(descriptors[1], STDOUT_FILENO);
                Os.dup2(descriptors[2], STDERR_FILENO);

                for (FileDescriptor fd: descriptors) {
                    IoUtils.closeQuietly(fd);
                }
                newStderr = System.err;
            } catch (ErrnoException ex) {
                Log.e(TAG, "Error reopening stdio", ex);
            }
        }
     ......省略代码

     if (parsedArgs.runtimeInit) {
        if (parsedArgs.invokeWith != null) {
            WrapperInit.execApplication(parsedArgs.invokeWith,
                    parsedArgs.niceName, parsedArgs.targetSdkVersion,
                    pipeFd, parsedArgs.remainingArgs);
        } else {
            RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
                    parsedArgs.remainingArgs, null /* classLoader */);
        }
    } else {
       ......省略代码
    }
}

At the beginning, there were some resource recovery work, such as closing sockets, etc., because the child process was not used, so as to avoid occupying fd. The three operations of dup2 are to replace the original standard input, standard output and error output (fd are 0, 1, and 2 respectively) with descriptors passed in by parameters. In fact, what is passed is "/dev/null", which can be entered at will The fd of an app can see that 0, 1, and 2 must be /dev/null. Next, the parameters passed by Ams have "--runtime-init" and invokeWith is ActivityThead, and then execute the RuntimeInit.zygoteInit method.

step4 RuntimeInit.zygoteInit - child process environment preparation

Look at the code first

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

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");
        redirectLogStreams();
        /*设置时区、log、http代理等一些信息*/
        commonInit();
        nativeZygoteInit();
        applicationInit(targetSdkVersion, argv, classLoader);
    }

CommonInit is some general initialization, don't need to go deeper, let's focus on nativeZygoteInit (the more important methods are generally done in native) and applicationInit.

nativeZygoteInit

Through a series of inheritance and rewriting techniques, in short, it was finally called into app_main.cpp.frameworks
/base/cmds/app_process/app_main.cpp

virtual void onZygoteInit()
    {
        sp<ProcessState> proc = ProcessState::self();
        ALOGV("App process: starting thread pool.
");
        proc->startThreadPool();
    }

If you are familiar with the writing of the native binder process, you must be familiar with these two sentences, and you have seen the familiar routine. These two sentences helped open /dev/binder before the app started, and started the binder thread pool at the same time. After the app uses the binder, there is no need to care about the binder driver and thread pool related matters.

applicationInit

private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
        throws ZygoteInit.MethodAndArgsCaller {
    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.
     */
    throw new ZygoteInit.MethodAndArgsCaller(m, argv);
}

The program is relatively simple. The main method is to find the main method of ActivityThread by reflection, and throw an exception to pass the method as a parameter to the main method of zygote (the main method of zygote was mentioned earlier in addition to opening selectLoop, but also caught the exception of MethodAndArgsCaller ).

    /**
     * Helper exception 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.
     */
    public static class MethodAndArgsCaller extends Exception
            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 {
                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);
            }
        }
    }

The run method of MethodAndArgsCaller is very simple, in fact, it is to execute the main method of ActivityThread. Now let's take a look, zygote spared such a big circle, just to fork the child process, do some preparations for the binder thread pool, etc., and finally execute the main method of ActivityThread.
Summarize the process of creating life by the child process through the call stack

1896-1896/com.android.myapplication W:     at com.android.internal.os.RuntimeInit.invokeStaticMain(RuntimeInit.java:237)
1896-1896/com.android.myapplication W:     at com.android.internal.os.RuntimeInit.applicationInit(RuntimeInit.java:338)
1896-1896/com.android.myapplication W:     at com.android.internal.os.RuntimeInit.zygoteInit(RuntimeInit.java:290)
1896-1896/com.android.myapplication W:     at com.android.internal.os.ZygoteConnection.handleChildProc(ZygoteConnection.java:757)
1896-1896/com.android.myapplication W:     at com.android.internal.os.ZygoteConnection.runOnce(ZygoteConnection.java:243)
1896-1896/com.android.myapplication W:     at com.android.internal.os.ZygoteInit.runSelectLoop(ZygoteInit.java:876)
1896-1896/com.android.myapplication W:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:798)

Note that although the pid is the pid number of the child process, it inherits the call stack of the parent process. Starting from handleChildProc, it is the child process, and it was the parent process zygote before.

step5 ActivityThread - the entrance to the app

.frameworks/base/core/java/android/app/ActivityThread.java

This time we mainly look at the static main method of ActivityThread, because the entire file has more than 6000 lines, which is related to the various life cycles and management of Activity instances, as well as the creation of viewroot, and so on.

    public static void main(String[] args) {
        ...
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

Mainly call Looper's static method to create a message loop, then create an ActivityThread instance, and finally enter the message loop.

to sum up

Through this article, the general process of creating an app process from Ams is roughly summarized. From the perspective of the execution process entity, it is actually divided into three parts, one is in ams, one is in the parent process of zygote, and the other is in the child process. With this general context, according to the call stack, it is actually quite clear. ams is responsible for receiving the binder request and contacting zygote. After the zygote parent process receives the socket message, it forks the child process. The child process is responsible for preparing the binder environment and message loop, and then starts the activity life cycle.

Guess you like

Origin blog.csdn.net/nidongla/article/details/115134389