Previous

  • The server is powered on and starts to initialize the kernel
  • The kernel starts the first user-mode app, init
  • Init collects actions and services by parsing init.rc and the various rc files it imports
  • Init uses the epoll loop mechanism to trigger various actions in the early-init, init, and late-init states in sequence, indirectly triggering associated services

The zygote service

Zygote, known as an incubator process for App developers, is the absolute leader in the App world. The Zygote service is also started by init as a service with the following service declaration (64-bit as an example) :

The file location is: the system/core/rootdir/init zygote64. Rc

init.rc

In init.rc, you can see the related startup action:

What really happened to Start Zygote? Let’s go back to init.

init

In the previous article, init parses a series of *.rc files and collects a large number of actions into the ActionManager.

When init parses the RC file line by line, the Action parses as shown above, triggering the Action’s AddCommand() method.

This function is the actual content that will be called later, which can be found by FindFunction() :

For actions, the general set of built-in support looks like this:

And start corresponds to:

Check out do_start:

Here you can do a simple comb:

While init resolves Actions to ActionManager, it also resolves Service to ServiceList.

To reinforce the overall understanding again:

  1. on XXXcalledActionAnd the Action is calledCommand. The complete set of Command can passsystem/core/init/builtins.cppIn **BuiltinFunctionMap::map()**
  2. service XXXcalledServiceThe thing under Service is calledOption. The complete set of options can passsystem/core/init/service.cppIn theService::OptionParserMap::map()To retrieve

Start zygote is an Action, and the Action is executed in the following path:

  • am.ExecuteOneCommand()
    • action->ExecuteOneCommand()
      • Action::ExecuteCommand()
        • command.InvokeFunc()
          • Function (builtin_arguments) // Pointer function call
            • do_start()
              • service.cpp#ExpandArgsAndExecv()

One thing to add first:

Look back at ActionParser to see how Command and Function are tied together. The Action::AddCommand method is always called during ActionParser parsing:

That is, use the map to find the function for the corresponding command and add it to Commands_.

Take a look at the ExpandArgsAndExecv ontology:

Two problems can be found:

  1. ExpandArgsAndExecv (ExpandArgsAndExecv, ExpandArgsAndExecv, ExpandArgsAndExecv, ExpandArgsAndExecv, ExpandArgsAndExecv, ExpandArgsAndExecv)
  2. The final command to execute is assembled through args, which is determined during Service parsing:

And the structure of the Service is through ServiceParser: : ParseSection () :

Specific rc file parsing interested students can view the system/core/init/parser. The CPP, here are analyzed in detail, don’t do is too boring.

The conclusion, combined with the previous Zygote service declaration, is that the final command to execute is:

/system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server

At this point, the init process’s career with Zygote is over.

app_process

Source: frameworks/base/ CMDS /app_process

From Android.mk you can confirm that the app_process64 executable was generated here (the new version has been changed to Android.bp) :

The following in-depth source code analysis.

app_main.cpp

To call C/C++ executables, we famously start with the main method:

This is a standard main entry method, worth noting for two reasons:

  1. AppRuntime Indicates the existence of this runtime
  2. Skip “app_process64” argv[0] by subtracting argc and shifting argv pointer

As for AndroidRuntime, this is a very important role. It is the real starting point of the Android runtime.

The argv pointer points to the complete string: -xzygote /system/bin –zygote –start-system-server

There is a pre-check before parsing:

It’s not hard to see that after this pre-check, I as a string pointer is changed to: /system/bin –zygote –start-system-server


  • /system/bin is useless
  • zygote = true
  • startSystemServer = true
  • niceName = “zygote64”

Now it’s time to start preparing the new ARGS for the actual launch of runtime:

After assembling the ARGS, the actual call is made:

Where setArgv0 is the setting name (pthread_setname_NP) for the current thread (the default thread of the fork process).

Args currently contains only two things:

  • start-system-server
  • –abi-list= arm64-v8A (or armeabi-v7a armeabi)

What other scenarios can be used for app_process, which will be expanded into a spin-off

AppRuntime

The AppRuntime class, which exists in app_main.cpp, is thin on its own. Its parent AndroidRuntime class is the synthesis behind, AppRuntime is more for some life cycle virtual functions exposed by its parent class to do the necessary implementation, such as:

The complete class diagram:

The detailed code for its parent class is available online: AndroidRuntime

structure

MClass is of type Jclass. NULL initialization is done here, and the other parent classes are passed in:

Zygote is not the first service or process started with init. As you can see from the source code, the first start of early-init in init.rc is actually ueventd (actually init), and even later in the late-init cycle binder starts earlier than Zygote

start

Let’s go back to the argument to call start:

runtime.start("com.android.internal.os.ZygoteInit", args, zygote);

  • className = com.android.internal.os.ZygoteInit
  • Options (same as arGS above)
    • start-system-server
    • –abi-list= arm64-v8A (or armeabi-v7a armeabi)
  • zygote = true

AndroidRuntime’s start process is a bit longer and is divided into four parts:

1. Start the ART VM

JniInvocation

Jni_invocation.Init(NULL) internally loads libart.so (via dlopen) and saves the operation handle in the Jni_Invocation handler_ :

Handle_ is used as a parameter in subsequent calls to DLSYM. At the same time, the ART virtual machine did three soul torture:

  1. JNI_GetDefaultJavaVMInitArgs Can you get default JVM initialization parameters? (From the source, this part has no content, returns -1, but method presence is called support)
  2. JNI_CreateJavaVM Can you create JVMS?
  3. JNI_GetCreatedJavaVMs Can YOU give me the JVM you just created?

If all three methods exist, this is a valid libart.so.

startVm()

Inside this method is a lot of configuration parameter assembly aimed at creating a JVM, which is the second problem from the previous step. The zygote parameter affects the JDWP configuration of the JVM. The other two values are assigned via the ART Runtime:

The JniInvocation is another so — libnativehelper.so method, why is it available in androidruntime.cpp? There is no place to see it before dlopen. This can be done by frameworks/base/core/jni Android. Bp to find the answer, AndroidRuntime. CPP belong here. In this (Makefile) file, you can see that libnativeHelper is used as its shared_libs, so it can be called directly.

  • JniInvocation.cpp#JNI_CreateJavaVM()
    • JniInvication::JNI_CreateJavaVM()
      • JniInvication::JNI_CreateJavaVM_ function pointer (pointing to the JNI_CreateJavaVM method of the ART vm)
        • java_vm_ext.cc#JNI_CreateJavaVM

At this point, you have an instance of the virtual machine, as well as Env for JNI calls.

onVmCreated()

AppRuntime overwrites the virtual function of the parent class:

In zygote’s case, you’re not going to do anything here.

At this point, the VM has been created and the basic JNI call environment is in place. End of step 1.

2. Android JNI

StartReg implementation:

GRegJNI is a very large JNI registry:

According to my source code, there are 155 ways to register JNI, all of which are related to the various apis of the Android system

Take RuntimeInit as an example:

The env parameter passed in startReg is passed as an entry to the Register * method, and the env->RegisterNatives method is eventually triggered (I won’t go into the virtual machine details).

The official explanation for LocalFrame is as follows:

Every “register” function calls one or more things that return a local reference (e.g. FindClass). Because we haven’t really started the VM yet, they’re all getting stored in the base frame and never released. Use Push/Pop to manage the storage.

We started the VM earlier, but we didn’t make the actual method calls in that VM. Each register function will be executed via env, and they will generate local references that will not be destroyed automatically if left unchecked, causing unwanted data to accumulate, so use LocalFrame to collect them and finally release them.

Summary: This step is to register the JNI of many Android system-related functions into the JVM for subsequent calls in the Java environment.

3. Parameter assembly

StrArray will be used to store the assembled parameters, as shown in the annotation. The parameters will read:

  • com.android.internal.os.ZygoteInit
  • start-system-server
  • –abi-list=arm64-v8a

4. Call the main

As stated in the official comment, the current thread will be the main thread of the Java world to be arrived at.

Current thread characteristics:

  • C + + program
  • App_process64 Executable program
  • Currently in user space relative to Linux kernel space
  • Is started by the first app in user space — init (pid=1)

Before initiating the JVM call, the various parameters are:

  • startClass = com/android/internal/os/ZygoteInit
  • StartMeth is a main method like this
  • (strArray in previous section)

Next, with a call to env->CallStaticVoidMethod(), the startup process moves into the Java realm starting with ZygoteInit.

Next

ZygoteInit.java