Learn source code together (1) : system startup process

This is the first article in the “learn source code together” series, persistence is victory, mutual encouragement.

bootstrap

The boot program is a small program before the Android system runs, not part of the Android operating system, it needs to be specific to the motherboard and chip. The boot program is where the OEM or operator locks and restricts.

The kernel boot

The startup mode of the Android kernel is similar to that of desktop Linux. The main steps are as follows:

  1. Set the cache
  2. Protected memory
  3. Plans to list
  4. The load driver

init.rc

Init is the first Android process (PID = 0). It starts other SystemService processes such as Zygote, SystemService, and ServiceManager by parsing the init.rc script.

We say init. Rc is a script file, it has its own series of grammar rules, this article does not do more, the specification may refer to: / system/core/init/Readme. TXT, chestnut may refer to below init. Zygote64. Rc file.

Zygote

Zygote is started when init parses the init.rc script. The Android system can load the description of 32-bit or 64-bit machine zygote.rc on demand, depending on the specific value of ro.zygote.

# system/core/rootdir/init.rc
import /init.${ro.zygote}.rc
Copy the code

Init.zygote64. rc

#name Service name :zygote
# pathName Service path :/system/bin/app_process64
-xzygote /system/bin --zygote --start-system-serverService zygote /system/bin/ app_process64-xzygote /system/bin --zygote --start-system-server # OwningclassformainRather thancore
    class main
    priority-20 # Switch the service torootBy default, all users areroot
    user root
    group root readproc reserved_diskCreate a /dev/socket/zygotethesocketAnd put it onfdThe value is passed to the one that starts itinitprocesssocket zygote stream 660 root system
    socket usap_pool_primary stream 660 root systemWhen the service restarts, execute the following commandonrestart 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/tasks
Copy the code

According to Zygote’s path, the program zygote is in is called App_process64.

app_process

App_process source path in/frameworks/base/CMDS/app_pocess, main function app_main. CPP code below:

#if defined(__LP64__)
static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist64";
static const char ZYGOTE_NICE_NAME[] = "zygote64";

int main(int argc, char* const argv[])
{...#argv[] -xzygote /system/bin --zygote --start-system-server
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv)); argv++; .#a parameter parsing
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;
    ++i;
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") = =0) {
            The current process is used to host zygote
            zygote = true;
            #c Change process alias to zygote64
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") = =0) {
            #d Start system server
            startSystemServer = true;
        } else if (strcmp(arg, "--application") = =0) {
            #e Boot into standalone program mode
            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) {
        # runtime startup ZygoteInit
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else{... }}Copy the code

The runtime is an AppRuntime object.

AppRuntime

Source in the/frameworks/base/core/jni/AndroidRuntime CPP, part of the code below:

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{...#a Start the virtual machine and set the vm parameters according to the Android properties
    if(startVm(&mJavaVM, &env, zygote) ! =0) {
        return;
    }
    #b Callback after vm startup
    onVmCreated(env);
    #c JNI method registration
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return; }... jclass stringClass; jobjectArray strArray; jstring classNameStr; .# d className here that is app_process incoming "com. Android. Internal. OS. ZygoteInit"
    #e converts className to"com/android/internal/os/ZygoteInit"
    char* slashClassName = toSlashClassName(className ! =NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    
    # f found empty.#g reflection calls the main method of ZygoteInit, after which ZygoteInit runs on the virtual machine
    jmethodID startMeth = env->GetStaticMethodID(startClass, "main"."([Ljava/lang/String;)V");
    env->CallStaticVoidMethod(startClass, startMeth, strArray);
    #h Frees the memory space of the corresponding object
    free(slashClassName); . }Copy the code

ZygoteInit

Source in the/frameworks/base/core/Java/com/android/internal/OS/ZygoteInit. Java, part of the code below:

public static void main(String argv[]) {...try {
        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])) {
                // System Server needs to be started
                startSystemServer = true;
            } else if ("--enable-lazy-preload".equals(argv[i])) {
                // Lazily loading resources
                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 (abiList == null) {
            throw new RuntimeException("No ABI list supplied.");
        }
            
        if(! enableLazyPreload) { ...// If the app_process call argument argv[] does not have "-enable-lazy-preload"
            // This method is used to directly preload resources required by the VM runningpreload(bootTimingsTraceLog); . }else{ Zygote.resetNicePriority(); }.../ / GC
        bootTimingsTraceLog.traceBegin("PostZygoteInitGC"); gcAndFinalize(); .// Create a Socket interface that is controlled by a file descriptor
        zygoteServer = new ZygoteServer(isPrimaryZygote);
        if (startSystemServer) {
            // Create a new process to start various system services
            Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
            if(r ! =null) {
                r.run();
                return;
            }
        }
        Log.i(TAG, "Accepting command socket connections");
        // This is a while loop that acts as a daemon for Zygotecaller = zygoteServer.runSelectLoop(abiList); }... }Copy the code

The forkSystemServer method creates a new process to start various system services, as discussed later. Let’s start with another core method, runSelectLoop, which is a while loop that doesn’t break out until Zygote exits or an exception occurs. Part of the code is shown below:

Runnable runSelectLoop(String abiList) {
    ArrayList<FileDescriptor> socketFDs = new ArrayList<FileDescriptor>();
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
    // get the FileDescriptor for the Socket above and add it to the collection of ArrayList
      
    socketFDs.add(mZygoteSocket.getFileDescriptor());
    // Null is added to maintain the consistency of FDS and peers, corresponding to the listener Zygote Server Socket
    peers.add(null);
    while (true) {...boolean usapPoolFDRead = false;
        while (--pollIndex >= 0) {
            // When a connection request or data processing request is received from the client, proceed;
            // Otherwise enter continue to break the loop.
            if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
                continue;
            }
            if (pollIndex == 0) {
                // A new ZygoteConnection is generated by this method.
                // Peers and FDS are updated respectively
                ZygoteConnection newPeer = acceptCommandPeer(abiList);
                peers.add(newPeer);
                socketFDs.add(newPeer.getFileDescriptor());
            } else if (pollIndex < usapPoolEventFDIndex) {
                // Data sent from the client needs to be processed in the established connection
                try {
                    ZygoteConnection connection = peers.get(pollIndex);
                    / / see below
                    final Runnable command = connection.processOneCommand(this); . }... }... }}Copy the code

The core method processOneCommand generates its own separate process for each newly launched application through the forkAndSpecialize method inside the method and runs the application’s own code in the handleChildProc method:

Runnable processOneCommand(ZygoteServer zygoteServer) {...// Zygote generates its own separate process for each newly launched application
    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.mTargetSdkVersion);
    try {
       if (pid == 0) {
            // in child
            zygoteServer.setForkChild();
            zygoteServer.closeServerSocket();
            IoUtils.closeQuietly(serverPipeFd);
            serverPipeFd = null;
            // Enter the subprocess flow and run the application's own code through this method
            return handleChildProc(parsedArgs, descriptors, childPipeFd,
                    parsedArgs.mStartChildZygote);
        } else {
            // In the parent process, pid<0 indicates fault
            IoUtils.closeQuietly(childPipeFd);
            childPipeFd = null;
            handleParentProc(pid, descriptors, serverPipeFd);
            return null; }}finally{ IoUtils.closeQuietly(childPipeFd); IoUtils.closeQuietly(serverPipeFd); }}Copy the code

Let’s go back to the ZygoteInit main function. A new process is created using the forkSystemServer method within the forkSystemServer method, where “–setuid=1000” indicates the process ID and “–nice-name=system_server” indicates the process name. And this process will perform handleSystemServerProcess method, next to start the support System to run the System Server.

    private static Runnable forkSystemServer(String abiList, String socketName, ZygoteServer zygoteServer) {.../* Hardcoded command line to start the system server */
        String args[] = {
                "--setuid=1000"."--setgid=1000"."- setgroups = 1001100 2100 3100 4100 5100 6100 7100 8100 9101 0101 8102 1102 3,"
                        + "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010"."--capabilities=" + capabilities + "," + capabilities,
                "--nice-name=system_server"."--runtime-args"."--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
                "com.android.server.SystemServer"}; ZygoteArguments parsedArgs =null;
        int pid;
        try {
            // It is used to parse the parameters and generate the target format
            parsedArgs = new ZygoteArguments(args);
            Zygote.applyDebuggerSystemProperty(parsedArgs);
            Zygote.applyInvokeWithSystemProperty(parsedArgs);
            boolean profileSystemServer = SystemProperties.getBoolean(
                    "dalvik.vm.profilesystemserver".false);
            if (profileSystemServer) {
                parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
            }
            // fork child process to run system_server
            pid = Zygote.forkSystemServer(
                    parsedArgs.mUid, parsedArgs.mGid,
                    parsedArgs.mGids,
                    parsedArgs.mRuntimeFlags,
                    null,
                    parsedArgs.mPermittedCapabilities,
                    parsedArgs.mEffectiveCapabilities);
        } catch (IllegalArgumentException ex) {
            throw new RuntimeException(ex);
        }
        /* For child process */
        if (pid == 0) {
            if (hasSecondZygote(abiList)) {
                waitForSecondaryZygote(socketName);
            }
            // Close the zygote socket
            zygoteServer.closeServerSocket();
            return handleSystemServerProcess(parsedArgs);
        }
        return null;
    }
Copy the code

Within the handleSystemServerProcess method, when parsedArgs. MInvokeWith = = null, executes ZygoteInit. ZygoteInit method.

    private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {...if(parsedArgs.mInvokeWith ! =null) { String[] args = parsedArgs.mRemainingArgs; . WrapperInit.execApplication(parsedArgs.mInvokeWith, parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion, VMRuntime.getCurrentInstructionSet(),null, args);
            throw new IllegalStateException("Unexpected return from WrapperInit.execApplication");
        } else {
            createSystemServerClassLoader();
            ClassLoader cl = sCachedSystemServerClassLoader;
            if(cl ! =null) {
                Thread.currentThread().setContextClassLoader(cl);
            }
            /* * Pass the remaining arguments to SystemServer. */
            return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                    parsedArgs.mRemainingArgs, cl);
        }
        /* should never reach here */
    }
Copy the code

This is the logic of zygoteInit. RedirectLogStreams closes system.out and system.err and redirects to the Android log. CommonInit handles the initialization of the common part. NativeZygoteInit is a local initialization function that is responsible for starting local system services. In JNI, Native functions are declared at the Java layer and then implemented at the local layer. See system.loadLibrary (” android_Servers “) in the SystemServer().run() method below:

 public static final Runnable zygoteInit(int targetSdkVersion, 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();
        RuntimeInit.commonInit();
        ZygoteInit.nativeZygoteInit();
        return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
    }
Copy the code

In the applicationInit method, args.startClass is within the args of forkSystemServer when the server process is created “Com. Android. Server. SystemServer” findStaticMain method will reflect call args. StartClass main method.

    protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
            ClassLoader classLoader) {...// Remaining arguments are passed to the start class's static main
        return findStaticMain(args.startClass, args.startArgs, classLoader);
    }

    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);
    }
Copy the code

This is SystemServer’s main function, which in turn calls SystemServer().run() directly to start a Java layer system service.

    public static void main(String[] args) {
        new SystemServer().run();
    }
Copy the code

In the run method, prepare the body of the main loop, load the local service library into memory, initialize the local service, start various system servers, and enter the long loop through looper. loop. Binder services enabled by nativeZygoteInit accept and process external requests.

    private void run(a) {
        try{...if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {
                Slog.w(TAG, "System clock is before 1970; setting to 1970."); SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME); }... Looper.prepareMainLooper(); Looper.getMainLooper().setSlowLogThresholdMs( SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);// Initialize native services.
            System.loadLibrary("android_servers"); .// Initialize the system context.
            createSystemContext();
            // Create the system service manager.
            mSystemServiceManager = newSystemServiceManager(mSystemContext); mSystemServiceManager.setStartInfo(mRuntimeRestart, mRuntimeStartElapsedTime, mRuntimeStartUptime); LocalServices.addService(SystemServiceManager.class, mSystemServiceManager); }...// Start services.
        try {
            traceBeginAndSlog("StartServices"); startBootstrapServices(); startCoreServices(); startOtherServices(); SystemServerInitThreadPool.shutdown(); }...// Loop forever.
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

Copy the code

conclusion

Look at the sequence diagram and go over it in your mind. The intention is to be sequential, not verbose:

Well, that’s the end of this article. If this article is useful to you, give it a thumbs up. Everyone’s affirmation is also the motivation for Dumb I to keep writing.