This article is the first part of the Android startup process, including the Linux kernel startup part and the Android Init process startup part.

Linux kernel startup

Why did I mention the Linux startup first? On the one hand, the Linux kernel is the foundation of the Android platform, and on the other hand, I’ve been exposed to some of the basics of Linux recently, so I’d like to write down what I’ve learned.

The function of the kernel is to control the hardware resources of the computer and provide the running environment of the program, such as: executing program, file operation, memory management and device driver, etc., and the interface provided by the kernel is also called system call.

Since the kernel is so important and provides all the services that programs need to run, you must boot it up before you start Android. To see how the kernel boots, let’s take a look at what happens when we press the power button.

Computer power after the first will go to ROM (read-only memory), which was solidified some initialization program, this program is also called BIOS, specific steps like the following:

Read BIOS (basic I/O system, in ROM) :

  • Hardware self-check, that is, check whether the computer hardware meets the basic conditions of operation;
  • This program to view the start order, of course, this can be adjusted, then according to the start order to find the next stage of the start procedure where;

Master boot record (giving control to first in the boot order in BIOS) :

  • Read the first 512 bytes of the first sector of the device, if it ends with a specific character, it indicates that the device can be used for startup, if not according to the startup order of the BIOS just give control to the next device, the first 512 bytes are also called the master boot record (MBR);
  • The 512 bytes in the MBR don’t fit much, so it mainly tells the computer where to find the operating system (on the hard disk);
  • At this time through the MBR partition table to find the corresponding location on the hard disk;

Start the OPERATING system using the Boot Loader:

  • Linux uses Grub2, which is the boot manager and loads various imgs in;
  • The operating system kernel is loaded into memory.
  • After that, the initial process (0/1/2) will be created, followed by the first process to load the rest of the user mode;

If you’re familiar with Linux, you’ll know that the entry function for Linux startup is start_kernel (in init/main.c), which does something important:

  • Idle process 0 is created.
  • System call initialization;
  • The memory management system is initialized.
  • Scheduling system initialization;
  • Other initializations:
    • Create process 1 (user mode).
    • Create process 2 (kernel mode);

The Android init process starts

Init () {system/core/init () {main.cpp ();}}

Int main(int argc, char** argv) {ueventd is used to create device nodesif(! strcmp(basename(argv[0]),"ueventd")) {
        return ueventd_main(argc, argv);
    }
    if(argc > 1) {// outline // selinux_setupif(! strcmp(argv[1],"selinux_setup")) {
            return SetupSelinux(argv);
        }
        // second_stage 
        if(! strcmp(argv[1],"second_stage")) {
            returnSecondStageMain(argc, argv); }}return FirstStageMain(argc, argv);
}
Copy the code

Based on the system/core/init/README. Md reading can know the main function is executed multiple times, startup sequence is such FirstStageMain – > SetupSelinux – > SecondStageMain.

So let’s take a look at what each of the three parts did:

FirstStageMain

CPP int FirstStageMain(int argc, char** argv) {//... // Mount a file system, create a directory, create a file, etc. // redirect standard input, standard output, standard error to /dev/null SetStdioToDevNull(argv); // redirect standard input to /dev/null SetStdioToDevNull(argv); // Initialize the current kernel log InitKernelLogging(argv); / /... // For example, get "/"stat// Initialize the device, create a logical partition, mount the partition DoFirstStageMount(); / /... // Start main again, except this time with selinux_setup const char* path ="/system/bin/init";
    const char* args[] = {path, "selinux_setup", nullptr};
    execv(path, const_cast<char**>(args));
}
Copy the code

The first stage is more about mounting the file system, creating directories and files, why mount them so you can use them, and then calling main again to enter the SetupSelinux stage.

SetupSelinux

/ / file location: the system/core/init/selinux CPP int SetupSelinux (char * * argv) {/ / initializes this phase the kernel log InitKernelLogging (argv); / / initialization SELinux, loading SELinux policy SelinuxSetupKernelLogging (); SelinuxInitialize(); // Call main again and pass second_stage to the second phase // And this startup has already run const char* path = in SELinux context"/system/bin/init";
    const char* args[] = {path, "second_stage", nullptr};
    execv(path, const_cast<char**>(args));
}
Copy the code

So what we’re doing here is basically initializing SELinux, so what is SELinux? In fact, security enhanced Linux, so that all processes can be well enforced access control, so that Android better protect and limit system services, control access to application data and system logs, reduce the impact of malware.

SELinux is not initialized all at once, however. The next step is to call main again and enter the final SecondStageMain stage.

SecondStageMain

// File location: System/core/init/init. CPP / / less important place not post code int SecondStageMain (int arg c, Char ** argv) {// call SetStdioToDevNull(argv); // Initialize the current kernel log InitKernelLogging(argv); / /... Close (open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000)); // System property initializes property_init(); // System property Settings are related, and there are many places below the property_set //... // Set SELinux to phase 2 // create Epoll Epoll; InstallSignalFdHandler(&epoll); // Load the default system property property_load_boot_defaults(load_debug_prop); // StartPropertyService StartPropertyService(&epoll); // Am and sm are used to receive parsed data. // The action to be executed and the service to be started (am, sm) are used. // Add Action and Trigger to amwhile (true) {// Execute Action am.executeonecommand (); Auto next_process_action_time = HandleProcessActions(); }}Copy the code

This is the most important part of the startup phase, and I think there are four points that are important: property services, registration signal processing, init.rc parsing, and an infinite loop at the end of the method.

Property services

What is the attribute service? I think it is more like various system information about the phone, which is used by all our programs in the form of key/value. The following content is the attribute value obtained by my emulator after entering ADB shell.

generic_x86:/ $ getprop
...
[dalvik.vm.heapsize]: [512m]
...
[dalvik.vm.usejit]: [true]
[dalvik.vm.usejitprofiles]: [true]... [init.svc.adbd]: [running] ... [init.svc.gpu]: [running] ... [init.svc.surfaceflinger]: [running] ... [init.svc.zygote]: [running] ... [ro.product.brand]: [google] [ro.product.cpu.abi]: [x86] ... [ro.serialno]: [EMULATOR29X2X1X0] [ro.setupwizard.mode]: [DISABLED] [ro.system.build.date]: [Sat Sep 21 05:19:49 UTC 2019] ... [ro.zygote]: [zygote32] [ro.zygote.disable_gl_preload]: [1] [security.perf_harden]: [1] [selinux.restorecon_recursive]: [/data/misc_ce/0] ... [wifi.interface]: [wlan0]Copy the code

The property service code in the SecondStageMain phase basically does three things: create shared memory, load various property values, and create sockets for the property service. Here’s a snippet of those parts:

Property_init {// create directory /dev/__properties__ // will load and parse properties from somewhere else, // In the __system_property_area_init call chain trace, } property_load_boot_defaults {// many such codes load_properties_from_file("/system/build.prop", nullptr, &properties);
    load_properties_from_file("/vendor/default.prop", nullptr, &properties);
    load_properties_from_file("/vendor/build.prop", nullptr, &properties);
    load_properties_from_file("/product/build.prop", nullptr, &properties);
    load_properties_from_file("/product_services/build.prop", nullptr, &properties);
    load_properties_from_file("/factory/factory.prop"."ro.*", &properties); // StartPropertyService {// create Sockte // this Socket is used to handle system properties, All processes use it to modify system properties in shared memory property_set_fd = CreateSocket(...) ; Epoll ->RegisterHandler(property_set_fd, handle_property_set_fd); }Copy the code

It’s not that hard to understand in the code, but why use shared memory? What does the Socket do?

First of all, shared memory is an efficient way of inter-process communication. It only needs to have a copy of these attribute values in the memory, and each process does not need to copy a copy to its own space. Moreover, since it is shared, everyone can access it. But if anyone can at any time to read and write (in addition to the read-only attribute), that also will still be a problem, may appear with the problem of content, so everybody is not directly to the Shared memory, but through property service socket on the operation, thus avoiding the so process directly on that piece of Shared memory.

Registration signal processing

In the SecondStageMain phase, you are actually registering signal handlers so that you can respond to the underlying signals. The corresponding function is:

InstallSignalFdHandler { // ... Epoll ->RegisterHandler(signal_fd, HandleSignalFd); } HandleSignalFd { // ... / / ReapAnyOutstandingChildren will restart the process of die SIGCHLD - > ReapAnyOutstandingChildren SIGTERM - > HandleSigtermSignal default - > print log} / / the child abnormal exit to tag need to restart the ReapAnyOutstandingChildren {/ /... ReapOneProcess { // ... service.Reap { // ... Flags_ &= (~SVC_RESTART); // Set the flag bit to restart. flags_ |= SVC_RESTARTING; onrestart_.ExecuteAllCommands(); }}}Copy the code

Init. Rc parsing

What is init.rc? It is very important to the configuration file, and many in the rc file init. Rc is the main file, but I don’t speak the grammar of the rc file here, because the system/core/init/README. The md has written in clear, Init. rc is divided into different stages based on on, and is triggered by trigger in different stages. Each stage contains instructions to be executed, such as start followed by the service to be started, and mkdir is to create a directory.

Since there are multiple stages, let’s take a look at the trigger phase:

// The three stages are sequential, The sequence of early-init trigger early-fs is written in the SecondStageMain code: early-init -> init -> late-init // late-init Trigger fs trigger post-fs trigger late-fs trigger post-fs-data trigger load_persist_props_action // Zygote-start Trigger Zygote -start trigger firmware_mounts_complete trigger Early-boot Trigger bootCopy the code

So let’s see what init.rc parsing does in the SecondStageMain phase:

Int SecondStageMain(int argc, char** argv) {//... / / two containers used to store the ActionManager & am = ActionManager: : GetInstance (); ServiceList& sm = ServiceList::GetInstance(); // resolve init.rc LoadBootScripts(am, sm); / /... QueueEventTrigger() : QueueEventTrigger();"early-init"); / /... // add the init statement am.queueeventTrigger ("init"); / /... QueueBuiltinAction(InitBinder, QueueBuiltinAction, QueueBuiltinAction)"InitBinder"); / /... QueueEventTrigger(QueueEventTrigger())"late-init"); } LoadBootScripts(action_manager, service_list) { Parser parser = CreateParser(action_manager, service_list); Init_rc STD ::string bootscript = GetProperty("ro.boot.init_rc".""); Rc // the current directory is system/core/init/if(bootscript.empty()) {// ParseConfig is used to process parser.parseconfig ("/init.rc");
        //  ... 
    } else{ parser.ParseConfig(bootscript); }}Copy the code

In fact, the above code is mainly to parse the init.rc file and add the action to be performed.

An infinite loop at the end of a method

The main thing to do here is to perform the actions that you just entered into the ActionManager and see if there are any processes that need to be restarted.

while (true{/ /... // Execute the action am.executeonecommand () that you just added to ActionManager; / /... // HandleProcessActions is where the process is actually restarted auto next_process_action_time = HandleProcessActions(); } HandleProcessActions { // ... Auto result = s->Start(); auto result = s->Start(); }Copy the code

At this point, the three stages of starting the init process are basically clear.

However, since this is the first time I have read the AOSP source code, the content of this article is limited and there are many details that are not discussed, such as:

  • More details on the Linux startup process;
  • What exactly are those files mounted and what are their uses;
  • What is the complete read and write flow of the property service?
  • How to parse init.rc and execute it;
  • Zygote startup and so on;

But the rest, like Zygote, I’ll try to share after I read it next time.

Summary and Harvest

If you ask me what I learned from reading all of this, here are the three main things I learned:

  • In some cases (such as insufficient resources in the early stage or dependency), we can disassemble large tasks and reasonably allocate the execution order (including sequence, serial and parallel arrangement, etc.), so as to achieve an overall execution goal through the coordination of multi-stage tasks.
  • When the resource is shared, it’s better not to have everyone directly manipulate the resource, but to have the middleman, and everyone only interacts with the middleman, and the specific resource is interacted with by the middleman;
  • It’s important to have code running, but it’s also important to have a reasonable monitoring module that can detect and respond to problems when necessary.

Thanks and Reference

The above, in addition to the source code itself, also refer to the following links (in no particular order) :

How does the computer start?

Linux startup process

07 | from the BIOS to the bootloader: at the beginning of business, have their own work, the boss

08 | kernel initialization: it big in business have to start a company

Android Startup Process analysis (1)

Inside the Linux boot process

Linux process 0 prelife (init_task) This life (Idle)—-Linux process management and scheduling (5)

Security-enhanced Linux in Android

Android system startup -Init

Android10.0 system startup process (two) init process

Android P (9.0) Init process source analysis

Start the Init process of the Android system startup process

Android startup process — 1, 2, 3

The last

There is no ambiguity in the source itself, but because everyone is based on different, specific understanding may be some different, so what problems, please also give directions, more communication.

If you think my writing is good, give me feedback by liking, liking, and fucking liking. Thank you for your support.