Follow the previous: Startup process of the Android system (1) Startup process of the init process

Zygote means Zygote in English, and as the name suggests, the Zygote process is used to incubate other processes, such as SystemServer and other application processes. Zygote exists in the Android system in the form of services, is an important daemon of the Android system, the following we analyze the start process of the Zygote process through source code.

1. Parse the startup script of the Zygote service and start app_main

In the init process start, will parse Zygote service process start script and start Zygote process, for different bits of the operating system, Zygote is also corresponding to different start scripts, in the Android8.0 system source code has a total of 4 start scripts, Zygote32. rc (supports 32-bit systems), init.zygote64.rc (supports 64-bit systems), init.zygote32_64.rc (supports both 32-bit and 64-bit systems), Zygote64_32. rc (both 32-bit and 64-bit are supported, but the main 64-bit), we use init.zygote32.rc as an example to look at the Zygote service script source code:

Directory location: \ System \ Core \ RootDir \init.zygote32.rc

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main priority -20 user root group root readproc socket zygote stream 660 root system onrestart write /sys/android_power/request_state wake 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/tasksCopy the code

As you can see from the above code, the name of the service is zygote, the path is /system/bin/app_process, and the parameters include -xzygote /system/bin –zygote –start-system-server, The name of the class is main.

After parsing the above code, init will start the Zygote service. The script for starting zygote is as follows:

\ System \ Core \ Rootdir \init.rc

On zygote-start && property:ro.crypto. State =unencrypted // In the. Rc file, on represents a trigger and zygote-start is the name of the trigger. Exec_start update_verifier_nonencrypted start netd start zygote // Start zygote service start zygote_secondaryCopy the code

Start zygote (do_start) : zygote (do_start) : zygote (do_start)

Source path: system core init builtins.cpp

static int do_start(const std::vector<std::string>& args) {
    // 1. Locate the service based on its name
    Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);  
    if(! svc) {LOG(ERROR) << "do_start: Service " << args[1] < <" not found";
        return - 1;
    }
    if(! svc->Start())  //2. Invoke the Start method to Start the service
        return - 1;
    return 0;
}
Copy the code

Zygote = zygote = zygote = zygote = zygote = zygote = zygote = zygote = zygote

Source path: system core init service.cpp

bool Service::Start(a) {...pid_t pid = - 1;
    if (namespace_flags_) {
        pid = clone(nullptr.nullptr, namespace_flags_ | SIGCHLD, nullptr);
    } else {
        pid = fork();  // 1. Use fork to create zygote child process
    }

    if (pid == 0) {  // If pid is 0, the child process is in process.if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {  // 2. Call execve to execute the child process's code
            PLOG(ERROR) << "cannot execve('" << strs[0] < <"')";
        }

        _exit(127); }...return true;
}
Copy the code

In comment 1, a child thread is created by the fork function. Since fork is a self-copy of the parent process, fork returns both the parent and the child, with the id of the child in the parent and 0 in the child.

In comment 2, the code for the child process is executed by calling the execve function. As you can see from zygote’s startup script, the code for executing the service is located in/ system/bin/app_process and the corresponding file is app_main.cpp. This puts the program in the main method of app_main.

2. Run AppRuntime to start Zygote

App_main. CPP main method: app_main. CPP

Source path: \frameworks\base\ CMDS \app_process\app_main.cpp

int main(int argc, char* const argv[])
{...AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));// 1. Create an AppRuntime instance.while (i < argc) {  // 2. Loop through the parameters
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") = =0) {  // When the parameter is --zygote
            zygote = true;  // Change the zygote flag to true
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") = =0) { // When the parameter is --start-system-server
            startSystemServer = true;  // Change the startSystemServer parameter to 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; }}if (zygote) {
        // 3. If the zygote flag is true, the Runtime start method is executed
        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(a);LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); }}Copy the code

An instance of AppRuntime is created at comment 1.

In comment 2, loop through the parameters and set zygote to true if it contains “–zygote” and startSystemServer to true if it contains “–start-system-server”.

Note 3 executes the code in the ZygoteInit file by calling runtime’s start method and passing the ZygoteInit file path as a parameter to the start method, which is a Java file. AndroidRuntime is the parent of AppRuntime.

Source path: \frameworks\ Base \core\jni\AndroidRuntime.cpp

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{... JniInvocation jni_invocation; jni_invocation.Init(NULL);// Initialize jNI
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) ! =0) {  1. Start the Java VM
        return;
    }
    onVmCreated(env);

     // Mainly used to register JNI functions
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return; }.../* 2. Execute ZygoteInit's main method */ using jni
    char* slashClassName = toSlashClassName(className);  // Replace "." in classname with "/"
    jclass startClass = env->FindClass(slashClassName);// Load the Java class of ZygoteInit via JNI
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main"."([Ljava/lang/String;)V");  // Find ZygoteInit's main method
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);// Call the ZygoteInit mian method from jNI. }... }Copy the code

Since the ZygoteInit file is written in Java code, we need jNI methods to execute ZygoteInit’s main method.

In comment 1, the Java virtual machine is created using the startVm method.

In comment 2, you end up calling ZygoteInit’s main method by executing a series of JNI methods.

3. Start SystemServer and listen for application creation requests.

Zygote main method:

Source path: \frameworks\base\core\ Java \com\ Android \internal\ OS \ zygoteinit.java

public static void main(String argv[]) {
        ZygoteServer zygoteServer = new ZygoteServer();  // Create the ZygoteServer instance.try{... zygoteServer.registerServerSocket(socketName);// 1. Register ServerSocket.if (startSystemServer) {
                startSystemServer(abiList, socketName, zygoteServer);  2. Start SystemServer
            }

            Log.i(TAG, "Accepting command socket connections");
            zygoteServer.runSelectLoop(abiList);  // 3. Start an event loop to listen for new requests

            zygoteServer.closeServerSocket();
        } catch (Zygote.MethodAndArgsCaller caller) {
            caller.run();
        } catch (Throwable ex) {
            Log.e(TAG, "System zygote died with exception", ex);
            zygoteServer.closeServerSocket();
            throwex; }}Copy the code

In the main method, an instance of ZygoteServer is first created, and the ServerSocket is registered in comment 1 by calling ZygoteServer’s registerServerSocket method.

The SystemServer process is started by calling the startSystemServer method in comment 2.

Note 3 opens the event loop by calling zygoteServer’s runSelectLoop method so that the Zygote process can constantly listen for new application process creation requests.

Let’s take a look at the source of the registerServerSocket method:

Source path: \frameworks\base\core\ Java \com\ Android \internal\ OS \ zygoteserver.java

private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
private LocalServerSocket mServerSocket;

void registerServerSocket(String socketName) {
        if (mServerSocket == null) {
            int fileDesc;
            ANDROID_SOCKET_zygote = ANDROID_SOCKET_zygote = 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(fullSocketName + " unset or invalid", ex);
            }

            try {
                FileDescriptor fd = new FileDescriptor();// Create a file descriptor object
                fd.setInt$(fileDesc);
                mServerSocket = new LocalServerSocket(fd);// Create the ServerSocket object
            } catch (IOException ex) {
                throw new RuntimeException(
                        "Error binding to local socket '" + fileDesc + "'", ex); }}}Copy the code

In the registerServerSocket method, the name of the Socket is first obtained through string concatenation, and a file descriptor object is created based on that name. Based on the Linux idea that everything is a file, a socket is also considered a file, and the file descriptor is used to represent the socket.

The file descriptor is then used to create a LocalServerSocket object, which, by its name, is a socket running on the server to listen for new application process creation requests.

Let’s look at the runSelectLoop method again:

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

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

        /* 1. Start an infinite loop, constantly listening for new requests */
        while (true) {
            /* Move the data in FDS to the pollFds array */
            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) {// 2. When there are no outstanding tasks in the array
                    ZygoteConnection newPeer = acceptCommandPeer(abiList);// Keep listening and return when there is a new request

                    // Put the connection request into the array
                    peers.add(newPeer);
                    fds.add(newPeer.getFileDesciptor());
                } else {
                    boolean done = peers.get(i).runOnce(this);// 3. Fetch the join request from peers and execute it
                    if (done) {
                        // Remove from array after execution
                        peers.remove(i);
                        fds.remove(i);
                    }
                }
            }
        }
    }
Copy the code

In the runSelectLoop method, we first create an array of FileDescriptor and a ZygoteConnection array, which are used to store newly received requests. Note 1 opens an infinite loop to listen for new requests, so the Zygote process will continue to exist during the running of the Android system until the system is shut down.

If I ==0, all the requests in the array have been completed. Call the acceptCommandPeer method to fetch a new request. The acceptCommandPeer method is a blocking method. If there is no new connection request, the acceptCommandPeer method blocks until the new connection request is returned. Once a new request is obtained, it is put into an array of peers and FDS.

When I is not 0, indicating that there is an unexecuted request in the array, the request is fetched and the runOnce method is called to execute the request.

The acceptCommandPeer method is an acceptCommandPeer method.

private ZygoteConnection acceptCommandPeer(String abiList) {
        try {
            return createNewConnection(mServerSocket.accept(), abiList);// Call the Accept method to wait for a new request
        } catch (IOException ex) {
            throw new RuntimeException(
                    "IOException during accept()", ex); }}protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
            throws IOException {
        return new ZygoteConnection(socket, abiList);  // Create ZygoteConnection instance
    }
Copy the code

The acceptCommandPeer code is simply a call to the Accept method to wait for a new request. This method blocks the current thread until a new request arrives.

Let’s look at ZygoteConnection’s runOnce method again:

Source path: \frameworks\base\core\ Java \com\ Android \internal\ OS \ zygoteconnection.java

boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller {... pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo, parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet, parsedArgs.appDataDir); . }Copy the code

The runOnce method is a long code, so let’s just look at the most critical line of code, which is to create a new process by calling the ForkandWfriend method, which will eventually fork the new application process by calling the Native method.

As mentioned earlier, the Zygote process creates a Java virtual machine when it is started. Our application processes are generated by the Zygote process fork, which essentially copies the parent process. Therefore, all application child processes also get a copy of the Java Virtual machine. This eliminates the need to start the Java virtual machine separately in the application process.