Android Init for User Space

After the Linux kernel initializes the hardware and related kernel space, the first user-space process is started: /init.cpp.   Init.cpp is the big boss of the user space who should never dies.

———————————-init.cpp———————————-

Here we first have a look at the Android OS architecture [1].

architecture-android-operating-system

Figrue 1. Android OS architecture

All stuff between Linux Kernel and Applications will be prepared explicitly or implicitly by the init.cpp, who will do one critical thing:

*parses init.rc and init.<machine_name.rc> and runs what it finds there. Typically it will start a bunch of services and the “root” of the Java process, Zygote.

So it is time for some source code of init.cpp [2].

 int main(int argc, char** argv) {
 ...
 Parser& parser = Parser::GetInstance();
 parser.AddSectionParser("service",std::make_unique());
 parser.AddSectionParser("on", std::make_unique());
 parser.AddSectionParser("import", std::make_unique());
 parser.ParseConfig("/init.rc");
 ...
 while (true) {
        if (!waiting_for_exec) {
            am.ExecuteOneCommand();
            restart_processes();
        }
        ...
        epoll_event ev;
        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
        if (nr == -1) {
            ERROR("epoll_wait failed: %s\n", strerror(errno));
        } else if (nr == 1) {
            ((void (*)()) ev.data.ptr)();
        }
}

Now let’s divert to init.rc and pick up what we really care about.

———————————init.rc————————————

init.rc[3] should match with the init/readme.txt[4] to make one’s life easier. readme.txt gives detail about the syntax of init language. Below is the file of init.rc where I only state part of it.

import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc
...
on boot 
... 
   class_start core

on nonencrypted
    # A/B update verifier that marks a successful boot.
    exec - root -- /system/bin/update_verifier nonencrypted
    class_start main
    class_start late_start
...   

———————————init.zygote32.rc[5]————————–

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd
    writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks

From the zygote.rc file, we can tell that zygote is triggered as a service, and it belongs to class main.

on nonencrypted
    # A/B update verifier that marks a successful boot.
    exec - root -- /system/bin/update_verifier nonencrypted
    class_start main

Here, nonencrypted is the triggered condition, and class_start main is the command which means start the class belongs to main. once on is triggered, it will start the main class, and service zygote belongs to the main class. Here zygote (service name) has the path as: /system/bin/app_process. So now we will have a look at app_process/app_main.cpp’s main() method.

———————————app_main.cpp[6]—————————

int main(int argc, char* const argv[]) {
...
while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
            startSystemServer = true;
        }
...
if (zygote) {
        InitializeNativeLoader();
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } 
...
}

What matters most here is:

InitializeNativeLoader() and runtime.start(“com.android.internal.os.ZygoteInit”,…).

runtime is an object of class AppRuntime, which is inherited from AndroidRuntime.

We shall look at the above methods respectively.

———————————native_loader.cpp[7]————————

static constexpr const char* kPublicNativeLibrariesConfig = "/system/etc/public.libraries.txt";
void InitializeNativeLoader() {
#if defined(__ANDROID__)
  std::lock_guard guard(g_namespaces_mutex);
  g_namespaces->Initialize();
#endif
}
...
void Initialize() {
    // Read list of public native libraries from the config file.
    std::string file_content;
    LOG_ALWAYS_FATAL_IF(!base::ReadFileToString(kPublicNativeLibrariesConfig, &file_content),
                        "Error reading public native library list from \"%s\": %s",
                        kPublicNativeLibrariesConfig, strerror(errno));
    std::vector lines = base::Split(file_content, "\n");
    std::vector sonames;
    for (const auto& line : lines) {
      auto trimmed_line = base::Trim(line);
      if (trimmed_line[0] == '#' || trimmed_line.empty()) {
        continue;
      }
      sonames.push_back(trimmed_line);
    }
    public_libraries_ = base::Join(sonames, ':');
    // android_init_namespaces() expects all the public libraries
    // to be loaded so that they can be found by soname alone.
    for (const auto& soname : sonames) {
      dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE);
    }
  }

The content (shared libraries) inside the public.libraries.txt [8] is:

libandroid.so
libc.so
libdl.so
libEGL.so
libGLESv1_CM.so
libGLESv2.so
libGLESv3.so
libicui18n.so
libicuuc.so
libjnigraphics.so
liblog.so
libmediandk.so
libm.so
libOpenMAXAL.so
libOpenSLES.so
libRS.so
libstdc++.so
libwebviewchromium_plat_support.so
libz.so

We can tell these libraries is closely related to the Figure 1‘s libraries. Next we focus on AndroidRuntime.start(…) which will run the static main() of the class “com.android.internal.os.ZygoteInit”.

——————————-AndroidRuntime.cpp————————–

void AndroidRuntime::start(const char* className, const Vector& options, bool zygote)
{
...
/* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);
...
env->CallStaticVoidMethod(startClass, startMeth, strArray);

...
}

Two tasks have implemented here: start a Dalvik Virtual Machine, and also run the the static method of what passes to the start(const char* className…). Here we encounter one important design philosophy of Android Application: Each android App runs inside a VM (Virtual Machine) environment, that means, the App’s code will be handled to a VM (not in the form of machine code, but format of .def, and the VM will transfer the code into the machine code which is suitable for the CPU to consume.) If you don’t buy this, think of VM as an adapter (a very powerful one).You only have to care about the input for the adapter , you need not worry about how the adapter transfer  your input into a variable output. The adapter will has the knowledge to achieve this by itself. This is the beauty of Java language: write once, run anywhere. You can interpret it as: the Java application only needs to write once, and the VM will make sure your code can run on any platform.

So it seems quite logic that we first start a VM and then call the application code.

Now we won’t dig into the code about VM, instead we will focus on com.android.internal.os.ZygoteInit which is a java file. See, we finally come to the magic world of java, thanks to the VM.

————————————–ZygoteInit.java————————————-

If you want to make a steak, do you purchase the meat from the food market? Or are you going to kill a cow and take one piece of what you need? Or even raise the cattle first? If our goal is clear enough which is just to make a meal with the steak, the most cost-effective way is to get the meat from the food market (you have better luck if you got one from the fridge).

Zygote here is to elevate efficiency: it prepares the common requirement for all java processes. So when a new Java process starts, Zygote will give it a copy of himself. In this way, the newly created process is not started from 0. And that is the concept between hot-start and cold-start. Bearing this in mind, we can examine the source code of ZygoteInit.java:

public static void main(String argv[]) {
...
registerZygoteSocket(socketName);
...
startSystemServer(abiList, socketName);
...
runSelectLoop(abiList);
...
}

From the name of listed methods, the main() accomplishes three tasks:

*register a ZygoteSocket;

*start the SystemServer;

*run the Select Loop forever if it doesn’t crash or get killed.

There much be some reasons for why and how, which is a little bit outside of my current understanding. But it all relates to communication, communication and communication between processes.

Why register a zygote socket?  First, socket is a communication channel in the scope of operating system; Second, zygote is the generator for new processes. Then we come to the conclusion: in order to tell zygote that a new process needed to be launched, we have to have a zygote socket to conduct the communication, which means we tell zygote through this socket.

Why we start a system server? We start a bunch of system services through the system server. Then next question is: What services are run by system server? We can find the answer from the source code: SystemServer.java and have a code snippet.

    
private void run() {
            ...
            startBootstrapServices();
            startCoreServices();
            startOtherServices();
            ...
}

You can see the services are categories into three types. If you spend some time look into aforementioned methods, there are more than 20 services totally. Figure 2 may help.

System Server.PNG

Figure 2. Part of the Services run by System Server

Two more things mention: 1, once startSystemServer(…) is called, zygote forks the system server process; 2, once system server dies, zygote will be killed.

Next we have a look at runSelectLoop(abiList) and see how zygote does its work.

——————————-ZygoteInit.cpp—————————————-

private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
    ...
    while(true) {
        ...
        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);
                    }
                }
        ...
    }
}

Each ZygoteConnection will runOnce() and ZygoteInit will loop here forever.

——————————-ZygoteConnection.java—————————————

    /**
     * Reads one start command from the command socket. If successful,
     * a child is forked and a {@link ZygoteInit.MethodAndArgsCaller}
     * exception is thrown in that child while in the parent process,
     * the method returns normally. On failure, the child is not
     * spawned and messages are printed to the log and stderr. Returns
     * a boolean status value indicating whether an end-of-file on the command
     * socket has been encountered.
     *
     * @return false if command socket should continue to be read from, or
     * true if an end-of-file has been encountered.
     * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main()
     * method in child process
     */
    boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
     ...
     pid = Zygote.forkAndSpecialize(...).
     ...
     if (pid == 0) {
                // in child
                IoUtils.closeQuietly(serverPipeFd);
                serverPipeFd = null;
                handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
    }

So a new process will have the following start-up procedure: sends a start command to zygote socket; and once zygote socket receives it, zygote will fork and then the newly forked process will be executed by handleChildProce(…).

 

 

And I will continue on this topic when myself get a full picture of communication mechanism about Android.

———————————————————————————–

Reference:

1, http://cecs.wright.edu/~pmateti/Courses/4900/Lectures/Internals/Embedded-Android-228-247-pm.pdf

2, More about Zygote can reference to the articles [10] and [11].

3, http://blog.csdn.net/zhgxhuaa/article/details/24744601

4, http://allenlsy.com/android-kernel-3/

 

 

One thought on “Android Init for User Space

Leave a comment