Android init process startup related analysis

Table of contents

1 Overview:

2. Architecture

2.1 How is the Init process started?

2.2 What did the Init process do after it started?

3. Kernel start init process source code analysis

3.1  kernel_init

3.2  do_basic_setup

4. Init process startup source code analysis

4.1 Init process entry

4.2 ueventd_main

4.3 The init process starts the first stage

4.3.1 FirstStageMain

4.4 Load SELinux rules

4.4.1 SetupSelinux

4.4.2 SelinuxInitialize()

4.5 The init process starts the second phase

4.5.1 SecondStageMain

5. Signal processing

5.1 InstallSignalFdHandler

5.2 RegisterHandler

5.3 HandleSignalFd

5.4 ReapOneProcess

6. Attribute service

6.1 property_init

6.2 StartPropertyService

6.3 handle_property_set_fd

7. The third stage init.rc

7.1 Action

7.2 Command

7.3 Service

7.4 Options

7.5 import

7.6 init.rc parsing process

7.6.1 LoadBootScripts

7.6.2 Executing Actions

7.6.3 Zygote startup

8. Summary


1 Overview:
 

The init process is the first process in the user space in the Linux system, and the process number is 1.

  • Boot ROM: When the phone is turned off, press and hold the Power button to turn it on, the boot chip starts to execute from the preset code solidified in ROM, and then loads the boot program to RAM;
  • Boot Loader: This is the boot program before starting the Android system, mainly to check RAM, initialize hardware parameters and other functions.

When the bootloader starts, start the kernel. After the kernel starts, start the init process in the user space, and then read the relevant configuration in init.rc through the init process, so as to start other related processes and other operations.

The init process is given a lot of important work. The init process startup is mainly divided into two stages:

The first phase completes the following:

ueventd/watchdogd jump and environment variable setting
Mount file system and create directory
Initialize log output, mount partition device
Enable SELinux security policy
Start the preparations before the second stage

The second phase completes the following:

Initialize the attribute system
Execute the second stage of SELinux and restore some file security contexts
Create a new epoll and initialize the child process termination signal processing function
Set other system attributes and enable attribute services

2. Architecture


2.1 How is the Init process started?


The Init process is the first user space process started after the Kernel starts, with PID 1.

After kernel_init starts, complete some init initialization operations, and then go to the system root directory to find the applications set by ramdisk_execute_command and execute_command in turn. If these two directories cannot be found, go to the root directory to find /sbin/init, / etc/init, /bin/init,/bin/sh these four applications are started, as long as one of these applications is started, the others will not be started.

The Android system generally puts an init executable file in the root directory, that is to say, the init process of the Linux system directly executes the init file after the kernel initialization is completed.

2.2 What did the Init process do after it started?


After the Init process starts, first mount the file system, then mount the corresponding partition, start the SELinux security policy, start the attribute service, parse the rc file, and start the corresponding attribute service process, initialize epoll, and set the signal, property, and keychord in sequence. The corresponding callback function when a fd is readable. Enter the wireless loop, used to respond to the changes and rebuilds of each process.

3. Kernel start init process source code analysis
 

3.1  kernel_init


kernel/msm-4.19/init/main.c

kernel/msm-4.19/init/main.c
kernel_init()
  |
run_init_process(ramdisk_execute_command) //run the executable file, start the init process
static int __ref kernel_init(void *unused)
{     kernel_init_freeable(); //do some init process Initialization operation     /* need to finish all async __init code before freeing the memory */     async_synchronize_full();// Wait for all asynchronous calls to complete, before freeing memory, all asynchronous __init codes must be completed     free_initmem();// Release all memory in the init.* segment     mark_rodata_ro(); // arm64 empty implementation     system_state = SYSTEM_RUNNING;// set the system state to the running state     numa_default_policy(); // set the default memory access policy of the NUMA system     flush_delayed_fput(); / / Release all delayed struct file structures     if (ramdisk_execute_command) { //The value of ramdisk_execute_command is "/init"







 

 

        if (!run_init_process(ramdisk_execute_command)) //run the init program in the root directory
            return 0;
        pr_err("Failed to execute %s\n", ramdisk_execute_command);
    }
 
    /*
     * We try each of these until one succeeds.
     *
     * The Bourne shell can be used instead of init if we are
     * trying to recover a really broken machine.
     */
    if (execute_command) { //If the value of execute_command is defined, go to the root directory to find the corresponding application, and then start
        if (!run_init_process(execute_command))
            return 0;
        pr_err("Failed to execute %s. Attempting defaults...\n",
            execute_command);
    }
    if (!run_init_process("/sbin/init")
    || ,/bin/sh These four applications are started
 
        !run_init_process("/etc/init") ||
        !run_init_process("/bin/init") ||
        !run_init_process("/bin/sh"))
        return 0;
 
    panic("No init found. Try passing init= option to kernel. "
          "See Linux Documentation/init.txt for guidance.");
}


3.2  do_basic_setup


kernel_init_freeable()
|
do_basic_setup()
 

static void __init do_basic_setup(void)
{     cpuset_init_smp();//For SMP systems, initialize the cpuset subsystem of the kernel control group.     usermodehelper_init();// Create a khelper single-threaded work queue to assist in creating and running user space programs     shmem_init();//Initialize shared memory     driver_init();//Initialize device drivers     init_irq_proc();//Create /proc/ irq directory, and initialize the subdirectories corresponding to all interrupts in the system     do_ctors();// execute the kernel constructor     usermodehelper_enable();// enable usermodehelper     do_initcalls();// traverse the initcall_levels array, call the initcall function inside, here mainly It is to initialize the device, driver, and file system,     //all the functions are encapsulated into an array for traversal, mainly for the purpose of extending     random_int_secret_init();//initialize the random number generation pool }









 


 

4. Init process startup source code analysis


We mainly analyze the init code of Android Q(10.0).

Source code files involved:

platform/system/core/init/main.cpp
platform/system/core/init/init.cpp
platform/system/core/init/ueventd.cpp
platform/system/core/init/selinux.cpp
platform/system/core/init/subcontext.cpp
platform/system/core/base/logging.cpp
platform/system/core/init/first_stage_init.cpp
platform/system/core/init/first_stage_main.cpp
platform/system/core/init/first_stage_mount.cpp
platform/system/core/init/keyutils.h
platform/system/core/init/property_service.cpp
platform/external/selinux/libselinux/src/label.c
platform/system/core/init/signal_handler.cpp
platform/system/core/init/service.cpp

4.1 Init process entry


The init process has already been started through kernel_init. The init process belongs to a daemon process. To be precise, it is the first process controlled by the user in the Linux system, and its process number is 1. Its life cycle runs through the entire Linux kernel. The common originator of all other processes in Android is the init process.

You can check the process number of init through the "adb shell ps |grep init" command.

The init entry function of Android Q (10.0) is adjusted from the original init.cpp to main.cpp, which separates the operations of each stage and makes the code more concise. Next, we will start learning from the main function.

 [system/core/init/main.cpp]

/*
 * 1. The first parameter argc indicates the number of parameters, the second parameter is the parameter list, that is, the specific parameters *
 2. The main function has four parameter entries,
 * one is ueventd in the parameter, enter ueventd_main
 * The second is that there is subcontext in the parameter, enter InitLogging and SubcontextMain
 * The third is that there is selinux_setup in the parameter, enter SetupSelinux
 * The fourth is that there is second_stage in the parameter, enter SecondStageMain

 *3. The execution sequence of main is as follows:
   * (1) The ueventd_main init process creates the child process ueventd,
   * and entrusts the work of creating the device node file to ueventd, and ueventd creates the device node file in two ways
   * (2) FirstStageMain starts the first Phase 1
   * (3)SetupSelinux loads selinux rules, sets selinux logs, and completes SELinux related work
   * (4)SecondStageMain starts the second phase
 */
int main(int argc, char** argv) {     //When argv[0] When the content is ueventd, the value of strcmp is 0,! strcmp is 1     //1 means true, and ueventd_main is executed, ueventd is mainly responsible for the creation of device nodes, permission setting and other work     if (!strcmp(basename(argv[0]), "ueventd")) {         return ueventd_main(argc, argv);     }    //When the number of parameters passed in is greater than 1, perform the following operations     if (argc > 1) {         //The parameter is subcontext, initialize the log system,         if (!strcmp(argv [1], "subcontext")) {





 




            android::base::InitLogging(argv, &android::base::KernelLogger);
            const BuiltinFunctionMap function_map;
            return SubcontextMain(argc, argv, &function_map);
        }
 
      //The parameter is "selinux_setup", start the Selinux security policy
        if (!strcmp (argv[1], "selinux_setup")) {             return SetupSelinux(argv);         }        //The parameter is "second_stage", start the second stage of the init process         if (!strcmp(argv[1], "second_stage")) {             return SecondStageMain(argc, argv);         }     }  // Start the first stage of the init process by default     return FirstStageMain(argc, argv); }










4.2 ueventd_main


Code path: platform/system/core/init/ueventd.cpp

The "/dev" directory does not exist in the image of the Android root file system. This directory is dynamically created after the init process starts.

Therefore, the heavy task of establishing the device node file in Android also falls on the init process. To this end, the init process creates a child process ueventd, and entrusts ueventd with the work of creating device node files.

ueventd creates device node files in two ways.

The first method corresponds to "Cold Plug", that is, based on the predefined device information, when ueventd is started, the device node file is created uniformly. This type of device node file is also called a static node file.

The second method corresponds to "Hot Plug", that is, when the system is running, when a device is inserted into the USB port, ueventd will receive this event and dynamically create a device node file for the inserted device. This type of device node file is also called a dynamic node file.

int ueventd_main(int argc, char** argv) {     //Set the default value of the new file, which is opposite to chmod, here is equivalent to the permission after the new file is 666     umask(000);      //Initialize the kernel log, located at node/ dev/kmsg, at this time the logd and logcat processes have not started yet,     //use the kernel log system, open the device node /dev/kmsg, then you can get the kernel log through cat /dev/kmsg.     android::base::InitLogging(argv, &android::base::KernelLogger);     //Register the selinux-related callback function for printing logs     SelinuxSetupKernelLogging();      SelabelInitialize();     //Parse xml and obtain it according to different SOC manufacturers Different hardware rc files     auto ueventd_configuration = ParseConfig({"/ueventd.rc", "/vendor/ueventd.rc",                                               "/odm/ueventd.rc", "/ueventd." + hardware + ".rc"}) ;     //Cold boot     if (access(COLDBOOT_DONE,


 



 



 



 


        ColdBoot cold_boot(uevent_listener, uevent_handlers);
        cold_boot.Run();
    }
    for (auto& uevent_handler : uevent_handlers) {         uevent_handler->ColdbootDone();     }     //Ignore the child process termination signal     signal(SIGCHLD, SIG_IGN);     // Reap and pending children that exited between the last call to waitpid() and setting SIG_IGN //     for        SIGCHLD above     . (-1, nullptr, WNOHANG) > 0) {     }     //Listen to the uevent from the driver and perform "hot swap" processing     uevent_listener.Poll([&uevent_handlers](const Uevent& uevent) {         for (auto& uevent_handler : uevent_handlers) {


 







 



            uevent_handler->HandleUevent(uevent); //Hot start, create device
        }
        return ListenerAction::kContinue;
    });
    return 0;
}


4.3 The init process starts the first stage


Code path: platform\system\core\init\first_stage_init.cpp

The main work of the first stage of the init process is to mount partitions, create device nodes and some key directories, initialize the log output system, and enable SELinux security policies

The first phase completes the following:

/* 01. Create a file system directory and mount the related file system */

/* 02. Shield standard input and output/initialize kernel log system*/

4.3.1 FirstStageMain

int FirstStageMain(int argc, char** argv) {     //Restart the bootloader when init crash     //This function is mainly used to set the behavior of various semaphores, such as SIGABRT, SIGBUS, etc. to SA_RESTART, once these signals are monitored Execute reboot system     if (REBOOT_BOOTLOADER_ON_PANIC) {         InstallRebootSignalHandlers();     }     //clear file permission     umask(0);     CHECKCALL(clearenv());     CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));     //on RAM memory Get the basic file system, the rest is used by rc files     CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));     CHECKCALL(mkdir("/dev/pts", 0755));     CHECKCALL(mkdir("/dev/socket", 0755));     CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0,NULL)); #define MAKE_STR(x) __STRING(x)







 


 






    CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
#undef MAKE_STR
 
    // 非特权应用不能使用Andrlid cmdline
    CHECKCALL(chmod("/proc/cmdline", 0440));
    gid_t groups[] = {AID_READPROC};
    CHECKCALL(setgroups(arraysize(groups), groups));
    CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
    CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
 
    CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));
 
    if constexpr (WORLD_WRITABLE_KMSG) {
        CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
    }
 
    CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
    CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9))); //
 
 
    This is required for the logging wrapper, which is called before ueventd runs
    CHECKCALL(mknod("/dev/ ptmx", S_IFCHR | 0666, makedev(5, 2)));
    CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3))); //
 
 
    hang in tmpfs, mnt/vendor, mount/product partition. Other partitions do not need to be loaded in the first stage,
    //only need to be loaded by parsing the rc file in the second stage.
    CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=1000"));
    
    //Create a read-write vendor directory
    CHECKCALL (mkdir("/mnt/vendor", 0755));
    // /mnt/product is used to mount product-specific partitions that can not be
    // part of the product partition, eg
    CHECKCALL(mkdir("/mnt/product", 0755));
 
    // Mount APEX, which is specially introduced in Android 10.0 to solve the fragmentation problem, similar to a component method, an enhancement of Treble, //
    no It is not necessary to completely upgrade the entire system version to write Google special updates, just like upgrading APK, perform APEX component upgrade
    CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755 ,uid=0,gid=0"));
 
    // /debug_ramdisk is used to preserve additional files from the debug ramdisk
    CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    " mode=0755,uid=0,gid=0"));
#undef CHECKCALL
 
    //Redirect standard input, standard output and standard error to the empty device file "/dev/null"
    SetStdioToDevNull(argv);
    //Mount tmpfs and kmsg 
    //In this way, the /kernel Log system can be initialized for users to print logs
    InitKernelLogging(argv);
 
    ...
 
    /* Initialize some necessary partitions
     *The main function is to parse /proc/device-tree/firmware/android/fstab,
     * Then get "/system", "/vendor", "/odm "Mount information of three directories
     */
    if (!DoFirstStageMount()) {         LOG(FATAL) << "Failed to mount required partitions early ...";     }     struct stat new_root_info;     if (stat("/", &new_root_info ) != 0) {         PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";         old_root_dir.reset();     }     if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) {         FreeRamdisk(old_root_dir. get(),old_root_info.st_dev);     }


 





 



 
    SetInitAvbVersionInRecovery();
 
    static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
    uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
    setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
 
    //启动init进程,传入参数selinux_steup
    // 执行命令: /system/bin/init selinux_setup
    const char* path = "/system/bin/init";
    const char* args[] = {path, "selinux_setup", nullptr};
    execv(path, const_cast<char**>(args));
    PLOG(FATAL) << "execv(\"" << path << "\") failed";
 
    return 1;
}


4.4 Load SELinux rules


SELinux is the abbreviation of "Security-Enhanced Linux", which is the National Security Agency "NSA=The National Security Agency"

An extended mandatory access control security module for Linux developed by SCC (Secure Computing Corporation).

Under the restriction of this access control system, a process can only access those files needed in its task.

selinux has two working modes:

Permissive, all operations are allowed (that is, no MAC), but if the permission is violated, the log will be recorded. Generally,
enforcing is used in eng mode, and all operations will be checked for permission. Generally, the user and user-debug modes are used
to operate the /sys/fs/selinux/enforce file, whether it is security_setenforce or security_getenforce, 0 means permissive 1 means enforcing

4.4.1 SetupSelinux

Description: Initialize selinux, load SELinux rules, configure SELinux related log output, and start the second stage

Code path: platform\system\core\init\selinux.cpp

/* This function initializes selinux, then executes init to run in init selinux */
int SetupSelinux(char** argv) {        //Initialize Kernel log     InitKernelLogging(argv);        //Restart bootloader when Debug version init crashes     if ( REBOOT_BOOTLOADER_ON_PANIC) {         InstallRebootSignalHandlers();     }     //Register a callback to set the selinux log that needs to be written to kmsg     SelinuxSetupKernelLogging();      //Load SELinux rules     SelinuxInitialize();     /*        *We are in the kernel domain and want to switch to the init domain . Filesystems that store selabels in their xattrs (such as ext4) do not require an explicit restorecon,        * but other filesystems do. Especially for ramdisks, such as recovery images for a/b devices, this is a necessary step.        *In fact, it is currently in the kernel domain. After loading Seliux, you need to re-execute init to switch to the user mode of C space        */


 




 


   


 





    if (selinux_android_restorecon("/system/bin/init", 0) == -1) {         PLOG(FATAL) << "restorecon failed of /system/bin/init failed";     }   //Prepare to start the innit process, pass in Parameter second_stage     const char* path = "/system/bin/init";     const char* args[] = {path, "second_stage", nullptr};     execv(path, const_cast<char**>(args));     /*        *Execute /system/bin/init second_stage, enter the second stage        */     PLOG(FATAL) << "execv(\"" << path << "\") failed";     return 1; }


 




 




 


4.4.2 SelinuxInitialize()

/*Load selinux rules*/
void SelinuxInitialize() {     LOG(INFO) << "Loading SELinux policy";     if (!LoadPolicy()) {         LOG(FATAL) << "Unable to load SELinux policy";     }     //Get The working mode of the current Kernel     bool kernel_enforcing = (security_getenforce() == 1);     //Get the configuration of the working mode     bool is_enforcing = IsEnforcing();     //If the current working mode is different from the configuration, change the current working mode Drop     if (kernel_enforcing != is_enforcing) {         if (security_setenforce(is_enforcing)) {             PLOG(FATAL) << "security_setenforce(" << (is_enforcing ? "true" :"false")                         << ") failed";         }     }




 


 


 







 
    if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result) {         LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();     } } /*  * Load SELinux rules  * There are two cases distinguished here, these two cases only distinguish where to load the security policy file,  * the first one is read from /vendor/etc/selinux/precompiled_sepolicy Take,  *The second one is read from /sepolicy, they all end up calling the selinux_android_load_policy_from_fd method  */ bool LoadPolicy() {     return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy(); }












4.5 The init process starts the second phase


The main content of the second stage:

Create a process session key and initialize the attribute system
Perform the second stage of SELinux and restore some file security contexts
Create a new epoll and initialize the child process termination signal processing function, see Section 5-Signal Processing
Start the server with matching attributes for details, see Section 6 for details Section-Property Service
Parse init.rc and other files, create action and service of rc file, start other processes, see Section 7-rc file analysis for details
 

4.5.1 SecondStageMain
 

int SecondStageMain(int argc, char** argv) {     /* 01. Create a process session key and initialize the attribute system*/     keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);     //Create a /dev/.booting file, which is a mark, indicating booting In progress     close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));     // Initialize the property system and read the property     property_init() from the specified file;     /* 02. Conduct the second stage of SELinux And restore some file security context */         SelinuxRestoreContext();     /* 03. Create a new epoll and initialize the child process termination signal processing function */     Epoll epoll;     if (auto result = epoll.Open(); !result) {         PLOG(FATAL) << result.error();     }              InstallSignalFdHandler(&epoll);     /* 04. Set other system properties and start the system property service*/     StartPropertyService(&epoll);


 


 


 


 





 

 


 
           /* 05 Analyze files such as init.rc, create actions and services of rc files, and start other processes*/
    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();
    LoadBootScripts(am, sm);
}
Detailed analysis of the code flow:

int SecondStageMain(int argc, char** argv) {     /*      *Restart the boot loader when init crash     * This function is mainly used to set the behavior of various semaphores, such as SIGABRT, SIGBUS, etc. to SA_RESTART, once these signals are monitored Execute reboot system     */     if (REBOOT_BOOTLOADER_ON_PANIC) {         InstallRebootSignalHandlers();     }     //redirect standard input, standard output and standard error to empty device file "/dev/null"     SetStdioToDevNull(argv);     //under /dev directory Mount tmpfs and kmsg      //In this way, the /kernel Log system can be initialized for users to print log     InitKernelLogging(argv);     LOG(INFO) << "init second stage started!";     // 01. Create a process session key and Initialize the attribute system     keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);     //Create a /dev/.booting file, which is a mark, indicating that booting is in progress







 






 


 

    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000)); //
 
    Initialize the property system, and read the property
    property_init() from the specified file;
 
    /*
     * 1. If the parameters are read from the command line at the same time Pass it with DT, the priority of DT is always higher than that of the command line
     * 2. DT is device-tree, which means device tree in Chinese, which records its own hardware configuration and system operating parameters,
     */
    process_kernel_dt(); // Process DT properties
    process_kernel_cmdline(); // Process command line properties
 
    // Process some other properties
    export_kernel_boot_props();
 
    // Make the time that init started available for bootstat to log.property_set
    ("ro.boottime.init", getenv( "INIT_STARTED_AT"));
    property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
 
    // Set libavb version for Framework-only OTA match in Treble build.
    const char* avb_version = getenv("INIT_AVB_VERSION");
    if (avb_version) property_set("ro.boot.avb_version", avb_version);
 
    // See if need to load debug props to allow adb root, when the device is unlocked.
    const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
    if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
        load_debug_prop = "true"s == force_debuggable_env;
    }
 
    // 基于cmdline设置memcg属性
    bool memcg_enabled = android::base::GetBoolProperty("ro.boot.memcg",false);
    if (memcg_enabled) {
       // root memory control cgroup
       mkdir("/dev/memcg", 0700);
       chown("/dev/memcg",AID_ROOT,AID_SYSTEM);
       mount("none", "/dev/memcg", "cgroup", 0, "memory");
       // app mem cgroups, used by activity manager, lmkd and zygote
       mkdir("/dev/memcg/apps/", 0755);
       chown("/dev/memcg/apps/",AID_SYSTEM,AID_SYSTEM);
       mkdir("/dev/memcg/system",0550);
       chown("/dev/memcg/system",AID_SYSTEM,AID_SYSTEM);
    }
 
    // Clear these environment variables, which have been stored in the system properties before
    unsetenv("INIT_STARTED_AT");
    unsetenv("INIT_SELINUX_TOOK");
    unsetenv("INIT_AVB_VERSION");
    unsetenv("INIT_FORCE_DEBUGGABLE");
 
    // Now set up SELinux for second stage.
    SelinuxSetupKernelLogging();
    SelabelInitialize();
 
    /*
     * 02. Perform the second stage of SELinux and restore some file security contexts 
     * Restore the security context of related files, because these files are created before the SELinux security mechanism is initialized, *
     so the context needs to be restored
     */
    SelinuxRestoreContext();
 
   /*
    * 03. Create a new epoll and initialize the child process termination signal processing function
    * Create an epoll instance and return the file descriptor of epoll
    */
    Epoll epoll;
    if (auto result = epoll.Open(); !result) {         PLOG(FATAL) << result.error();     }     /*       *Mainly create a handler to handle the termination signal of the child process, register a signal to epoll for monitoring      * for sub-inheritance processing      */     InstallSignalFdHandler(&epoll);     // Do the work related to default property configuration     property_load_boot_defaults( load_debug_prop);     UmountDebugRamdisk();


 





 



    fs_mgr_vendor_overlay_mount_all();
    export_oem_lock_status();
 
    /*
     *04. Set other system properties and enable system property services
     */
    StartPropertyService(&epoll);
    MountHandler mount_handler(&epoll);
 
    //Set udc Contorller for USB storage, sys/class/udc
    set_usb_controller ();
 
    // match the correspondence between commands and functions
    const BuiltinFunctionMap function_map;
    Action::set_function_map(&function_map);
 
    if (!SetupMountNamespaces()) {         PLOG(FATAL) << "SetupMountNamespaces failed";     }     // initialization file Context     subcontexts = InitializeSubcontexts();    /*      *05 Analyze init.rc and other files, create action and service of rc files, and start other processes      */


 


 



    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();
 
    LoadBootScripts(am, sm);
 
    // Turning this on and letting the INFO logging be discarded adds 0.2s to
    // Nexus 9 boot time, so it's disabled by default.
    if (false) DumpState();
 
    // When the GSI script is running, make sure the GSI state is available.
    if (android::gsi::IsGsiRunning()) {         property_set("ro.gsid.image_running", "1");     } else {         property_set("ro.gsid.image_running", "0");     }     am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");     // Execute the statement whose trigger is on early-init in the rc file     am.QueueEventTrigger("early-init");




 
 

 


 
    // Wait for the initialization of the cold-plug device to complete
    am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
 
    // Start querying actions from /dev
    am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
    am.QueueBuiltinAction(SetMmapRndBit sAction, "SetMmapRndBits") ;
    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
 
    // Initialization operation of device keychords
    keychords;
    am.QueueBuiltinAction(
        [&epoll, &keychords](const BuiltinArguments& args) -> Result<Success> {             for (const auto& svc : ServiceList::GetInstance()) {                 keychords. Register(svc->keycodes());             }



            keychords.Start(&epoll, HandleKeychord);
            return Success();
        },
        "KeychordInit");
 
    //Display the Android static LOGO on the screen
    am.QueueBuiltinAction(console_init_action, "console_init");
 
    // Execute the trigger in the rc file as The statement on init
    am.QueueEventTrigger("init");
 
    // Starting the BoringSSL self test, for NIAP certification compliance.
    am.QueueBuiltinAction(StartBoringSslSelfTest, "StartBoringSslSelfTest");
 
    // Repeat mix_hwrng_into_linux_rng in case /dev/hw_rand om or /dev /random
    // wasn't ready immediately after wait_for_coldboot_done
    am. QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
 
    // Initialize binder before bringing up other system services
    am.QueueBuiltinAction(InitBinder, "InitBinder");
 
    // When the device is in charging mode, there is no need to mount the file system or start system services
    // In charging mode, the charger will be executed as a queue , otherwise put late-init if the execution queue
    std::string bootmode = GetProperty("ro.bootmode", "");
    if (bootmode == "charger") {         am.QueueEventTrigger("charger");     } else {         am .QueueEventTrigger("late-init");     }     // Run all property triggers based on the current state of the property.     am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");     while (true) {         // By default, sleep until something happens.




 


 


        auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
 
        if (do_shutdown && !shutting_down) {             do_shutdown = false;             if (HandlePowerctlMessage(shutdown_command)) {                 shutting_down = true;             }         } // execute each An action carries the execution function corresponding to the command         if (!(waiting_for_prop || Service::is_exec_service_running())) {             am.ExecuteOneCommand();         }         if (!(waiting_for_prop || Service::is_exec_service_running())) {             if ( !shutting_down) {                 auto next_process_action_time = HandleProcessActions();





 







 
                // If there's a process that needs restarting, wake up in time for that.
                if (next_process_action_time) {
                    epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
                            *next_process_action_time - boot_clock::now());
                    if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
                }
            }
 
            // If there's more work to do, wake up again immediately.
            if (am.HasMoreCommands()) epoll_timeout = 0ms;
        }
 
        // 循环等待事件发生
        if (auto result = epoll.Wait(epoll_timeout); !result) {
            LOG(ERROR) << result.error();
        }
    }
 
    return 0;
}
 

5. Signal processing


init is a daemon process. In order to prevent the child process of init from becoming a zombie process (zombie process), init needs to obtain the end code of the child process when the child process ends, and remove the child process in the program table through the end code to prevent it from becoming a zombie process. The child process of the zombie process occupies the space of the program table (when the space of the program table reaches the upper limit, the system can no longer start new processes, which will cause serious system problems).

The child process restart process is shown in the following figure:

The main work of signal processing:

Initialize signal signal handle
Loop processing child process
Register epoll handle Handle
child process termination
Note: EPOLL is similar to POLL, which is used for event triggering in Linux, similar to EventBus function. Linux has been using select for event triggering for a long time. It is processed by polling. The more fds polled, the more time-consuming it will be. For processing a large number of descriptors, EPOLL has more advantages

5.1 InstallSignalFdHandler


In Linux, the parent process knows the end of the child process by capturing the SIGCHLD signal. The SIGCHLD signal will be sent when the child process terminates. After understanding these backgrounds, let's take a look at how the init process handles this signal.

First, create a new sigaction structure, sa_handler is a signal processing function, pointing to the function pointer SIG_DFL specified by the kernel is different from Android 9.0 and previous versions, here no longer receive signals through the read and write handle of the socket, but change it to the signal processing of the kernel Function SIG_DFL.
Then, sigaction(SIGCHLD, &act, nullptr) is to establish a signal binding relationship, that is to say, when the SIGCHLD signal is monitored, it will be processed by the sigaction structure of act Finally, the role of RegisterHandler is signal_read_fd (the previous s[1]
) After receiving the signal, trigger handle_signal.
As mentioned above, the function of the InstallSignalFdHandler function is to trigger HandleSignalFd for signal processing when the SIGCHLD signal is received.

                                   Schematic diagram of signal processing:

Code path: platform/system/core/init.cpp

Description: The main function of this function is to initialize the child process termination signal processing process

static void InstallSignalFdHandler(Epoll* epoll) {     // SA_NOCLDSTOP causes the init process to receive the SIGCHLD signal only when its child process terminates     const struct sigaction act { .sa_handler = SIG_DFL, .sa_flags = SA_NOCLDSTOP };     sigaction(SIGCHLD, &act, nullptr );     sigset_t mask;     sigemptyset(&mask);     sigaddset(&mask, SIGCHLD);      if (!IsRebootCapable()) {         // If init does not have the capability of CAP_SYS_BOOT, it is currently running in the container         // in this scenario } if (sigprocmask(SIG_BLOCK, &         mask,     nullptr     ) == -1) {         PLOG(FATAL) << "failed to block signals";     }     // register Handler to unblock signals in child processes
 



 



 





 



 

    const int result = pthread_atfork(nullptr, nullptr, &UnblockSignals);
    if (result != 0) {         LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);     }     //create signal handler     signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);     if (signal_fd == -1) {         PLOG(FATAL) << "failed to create signalfd";     }     //Signal registration, when signal_fd receives a signal, trigger HandleSignalFd     if ( auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd); !result) {         LOG(FATAL) << result.error();     } }


 





 





5.2 RegisterHandler
 

Code path: /platform/system/core/epoll.cpp

Description: Signal registration, add the fd handle to the listening queue of epoll_fd_

Result<void> Epoll::RegisterHandler(int fd, std::function<void()> handler, uint32_t events) {
    if (!events) {
        return Error() << "Must specify events";
    }
    auto [it, inserted] = epoll_handlers_.emplace(fd, std::move(handler));
    if (!inserted) {
        return Error() << "Cannot specify two epoll handlers for a given FD";
    }
    epoll_event ev;
    ev.events = events;
    // std::map's iterators do not get invalidated until erased, so we use the
    // pointer to the std::function in the map directly for epoll_ctl.
    ev.data.ptr = reinterpret_cast<void*>(&it->second);
    // 将fd的可读事件加入到epoll_fd_的监听队列中
    if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &ev) == -1) {
        Result<void> result = ErrnoError() << "epoll_ctl failed to add fd";
        epoll_handlers_.erase(fd);
        return result;
    }
    return {};
}
 

5.3 HandleSignalFd
 

Code path: platform/system/core/init.cpp

Description: Monitor the SIGCHLD signal and call ReapAnyOutstandingChildren to terminate the problematic child process

static void HandleSignalFd() {
    signalfd_siginfo siginfo;
    ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
    if (bytes_read != sizeof(siginfo)) {
        PLOG(ERROR) << "Failed to read siginfo from signal_fd";
        return;
    }
 
   //监控SIGCHLD信号
    switch (siginfo.ssi_signo) {
        case SIGCHLD:
            ReapAnyOutstandingChildren();
            break;
        case SIGTERM:
            HandleSigtermSignal(siginfo);
            break;
        default:
            PLOG(ERROR) << "signal_fd: received unexpected signal " << siginfo.ssi_signo;
            break;
    }
}
 

5.4 ReapOneProcess
 

Code path: /platform/system/core/sigchld_handle.cpp

Explanation: ReapOneProcess is the final processing function. This function first uses waitpid to find out the pid of the hanging process, and then finds the corresponding Service according to the pid.

Finally, call the Reap method of Service to clear resources, and decide whether to restart the machine or restart the process according to the type of the process

void ReapAnyOutstandingChildren() {     while (ReapOneProcess()) {     } } static bool ReapOneProcess() {     siginfo_t siginfo = {};     //Use the waitpid function to obtain the child process pid whose status has changed     //The waitpid flag is WNOHANG, that is, non-blocking If it returns a positive value, it means that a process has hung up     if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {         PLOG(ERROR) << "waitid failed";         return false;     }     auto pid = siginfo.si_pid;     if (pid == 0) return false;     // When we know there is a zombie pid, we use scopeguard to clear the pid     auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid , nullptr, WNOHANG)); });     std::string name;



 








 


 


 

    std::string wait_string;
    Service* service = nullptr;
 
    if (SubcontextChildReap(pid)) {
        name = "Subcontext";
    } else {
        //通过pid找到对应的service
        service = ServiceList::GetInstance().FindService(pid, &Service::pid);
 
        if (service) {
            name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid);
            if (service->flags() & SVC_EXEC) {
                auto exec_duration = boot_clock::now() - service->time_started();
                auto exec_duration_ms =
                    std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
                wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);
            } else if (service->flags() & SVC_ONESHOT) {
                auto exec_duration = boot_clock::now() - service->time_started();
                auto exec_duration_ms =
                        std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration)
                                .count();
                wait_string = StringPrintf(" oneshot service took %f seconds in background",exec_duration_ms / 1000.0f);
            }
        } else {
            name = StringPrintf("Untracked pid %d", pid);
        }
    }
 
    if (siginfo.si_code == CLD_EXITED) {
        LOG(INFO) << name << " exited with status " << siginfo.si_status << wait_string;
    } else {         LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;     }     / /No service found, indicating that it is over, exit     if (!service) return true;     service->Reap(siginfo);//Clear resources related to child process     if (service->flags() & SVC_TEMPORARY) {         ServiceList:: GetInstance().RemoveService(*service); //Remove the service     }     return true; }


 


 

 



 


 

6. Attribute service


During the development and debugging process, we saw that system properties can be easily set through property_set, so why start a property service here? This actually involves some permission issues. Not all processes can modify any system properties at will.

Android assigns the setting of attributes to the init process for management. Other processes cannot directly modify attributes, but can only notify the init process to modify them. In this process, the init process can perform permission control. Let's take a look at the specific process.

6.1 property_init


Code path: platform/system/core/property_service.cpp

Description: Initialize the attribute system, read the attribute from the specified file, register with SELinux, and perform attribute permission control

Clear the cache, here is mainly to clear several linked lists and mappings in memory, create a new property_filename directory, the value of this directory is /dev/_properties_

Then call CreateSerializedPropertyInfo to load the category information of some system properties, and finally write the loaded linked list to a file and map it to memory

void property_init() {
 
    //设置SELinux回调,进行权限控制
    selinux_callback cb;
    cb.func_audit = PropertyAuditCallback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);
 
    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
    CreateSerializedPropertyInfo();
    if (__system_property_area_init()) {
        LOG(FATAL) << "Failed to initialize property area";
    }
    if (!property_info_area.LoadDefaultPath()) {
        LOG(FATAL) << "Failed to load serialized property info file";
    }
}
 

Use CreateSerializedPropertyInfo to load the contexts of the following directories:

1) Related to SELinux

/system/etc/selinux/plat_property_contexts
 
/vendor/etc/selinux/vendor_property_contexts
 
/vendor/etc/selinux/nonplat_property_contexts
 
/product/etc/selinux/product_property_contexts
 
/odm/etc/selinux/odm_property_contexts
2)与SELinux无关

/plat_property_contexts
 
/vendor_property_contexts
/nonplat_property_contexts
 
/product_property_contexts
/odm_property_contexts
 

6.2 StartPropertyService


Code path: platform/system/core/init.cpp

Description: Start the property service

First create a socket and return the file descriptor, and then set the maximum concurrency to 8. Other processes can notify the init process to modify the system properties through this socket.

Finally, register the epoll event, that is, call handle_property_set_fd when the property_set_fd change is monitored

void StartPropertyService(Epoll* epoll) {     property_set("ro.property_service.version", "2");     // Establish socket connection     if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,                                    false, 0666, 0, 0 , {})) {         property_set_fd = *result;     } else {         PLOG(FATAL) << "start_property_service socket creation failed: " << result.error();     }     // Maximum 8 simultaneous listens     listen(property_set_fd, 8);     // Register property_set_fd, when the handle is changed, handle it through handle_property_set_fd     if (auto result = epoll->RegisterHandler(property_set_fd, handle_property_set_fd); ! result) {         PLOG(FATAL) << result.error();

 







 


 



    }
}


6.3 handle_property_set_fd


Code path: platform/system/core/property_service.cpp

Description: Establish a socket connection, then read the operation information from the socket, and call HandlePropertySet to perform specific operations according to different operation types

HandlePropertySet is the final processing function. The key starting with "ctl" will perform some Service Start, Stop, and Restart operations. Others are to call property_set to set properties. Whether it is the former or the latter, SELinux security checks must be performed. Only the process has the operation permission to perform the corresponding operation

static void handle_property_set_fd() {     static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */     // Waiting for client connection     int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);     if (s == -1) {         return;     }     ucred cr;     socklen_t cr_size = sizeof(cr);     // Get the credentials of the process connected to this socket     if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {         close(s);         PLOG(ERROR) < < "sys_prop: unable to get SO_PEERCRED";         return;     }     // Establish socket connection     SocketConnection socket(s, cr);     uint32_t timeout_ms = kDefaultSocketTimeout;

 





 








 



 
    uint32_t cmd = 0;
    // Read the operation information in the socket
    if (!socket.RecvUint32(&cmd, &timeout_ms)) {         PLOG(ERROR) << "sys_prop: error while reading command from the socket";         socket.SendUint32(PROP_ERROR_READ_CMD );         return;     }     // Perform corresponding processing according to the operation information, the difference between the two is that one reads in char form, and the other reads in String form switch (     cmd) {     case PROP_MSG_SETPROP: {         char prop_name[PROP_NAME_MAX];         char prop_value[ PROP_VALUE_MAX];         if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||             !socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {




 





 


          PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
          return;
        }
 
        prop_name[PROP_NAME_MAX-1] = 0;
        prop_value[PROP_VALUE_MAX-1] = 0;
 
        std::string source_context;
        if (!socket.GetSourceContext(&source_context)) {
            PLOG(ERROR) << "Unable to set property '" << prop_name << "': getpeercon() failed";
            return;
        }
 
        const auto& cr = socket.cred();
        std::string error;
        uint32_t result = HandlePropertySet(prop_name, prop_value, source_context, cr, &error);
        if (result != PROP_SUCCESS) {
            LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
                       << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
        }
 
        break;
      }
 
    case PROP_MSG_SETPROP2: {
        std::string name;
        std::string value;
        if (!socket.RecvString(&name, &timeout_ms) ||
            !socket.RecvString(&value, &timeout_ms)) {
          PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP2): error while reading name/value from the socket";
          socket.SendUint32(PROP_ERROR_READ_DATA);
          return;
        }
 
        std::string source_context;
        if (!socket.GetSourceContext(&source_context)) {
            PLOG(ERROR) << "Unable to set property '" << name << "': getpeercon() failed";
            socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
            return;
        }
 
        const auto& cr = socket.cred();
        std::string error;
        uint32_t result = HandlePropertySet(name, value, source_context, cr, &error);
        if (result != PROP_SUCCESS) {
            LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
                       << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
        }
        socket.SendUint32(result);
        break;
      }
 
    default:
        LOG(ERROR) << "sys_prop: invalid command " << cmd;
        socket.SendUint32(PROP_ERROR_INVALID_CMD);
        break;
    }
}


7. The third stage init.rc


When the attribute service is established, the init's own functions basically come to an end, and then other processes need to be started. But what about the init process and other processes? The other process is a binary file, we can start it directly through the exec command, such as ./system/bin/init second_stage, to start the second stage of the init process. But there are so many Native processes in the Android system, if they all execute the processes one by one in the code by passing exec, it is undoubtedly a disastrous design.

On this basis, Android introduces an init.rc mechanism, which is similar to reading configuration files to start different processes. Next, let's take a look at how init.rc works.

init.rc is a configuration file that contains scripts written in the Android Init Language.

init.rc in the directory of the mobile phone: ./init.rc

init.rc mainly contains five types of statements:

Action
Command
Service
Option
Import

7.1 Action


Actions represent a set of commands. Actions include a trigger that determines when to run the action

Action: Use the trigger trigger, that is, the statement starting with on to determine the timing of executing the corresponding service. The specific timing is as follows:

on early-init; Triggered in the early stage of initialization;
on init; Triggered in the initialization stage;
on late-init; Triggered in the late stage of initialization;
on boot/charger: Triggered when the system starts/charges;
on property:<key>= <value>: Triggered when the attribute value meets the condition;
 

7.2 Command


command is the command in the command list of the action, or the parameter command of the option onrestart in the service, and the commands will be executed one by one when the corresponding event occurs.

Commonly used commands are listed below

class_start <service_class_name>: Start all services belonging to the same class;
class_stop <service_class_name>: Stop the service of the specified class
start <service_name>: Start the specified service, if it is already started, skip it;
stop <service_name>: Stop the running one service
setprop <name> <value>: set property value
mkdir <path>: create specified directory
symlink <target> <sym_link>: create <sym_link> symbolic link connected to <target>;
write <path> <string>: write to Write a string in the file path;
exec: fork and execute, it will block the init process until the program is completed;
exprot <name> <name>: set the environment variable;
loglevel <level>: set the log level
hostname <name>: set the host name
import <filename> : Import an additional init configuration file
 

7.3 Service


Service Service, starting with service, is started by the init process, and generally runs in a sub-process of init, so it is necessary to determine whether the corresponding executable file exists before starting the service.

命令:service <name><pathname> [ <argument> ]* <option> <option>

parameter

meaning

<name>

Indicates the name of this service

<pathname>

Because the path where this service is located is an executable file, there must be a storage path.

<argument>

Parameters to start the service

<option>

Constraint options for this service

The child process generated by init is defined in the rc file, and each service will generate a child process by fork when it starts.

For example: service servicemanager /system/bin/servicemanager means that the service name is servicemanager, and the service execution path is /system/bin/servicemanager.

7.4 Options


Options is optional for Service, used in conjunction with service

disabled: Do not start automatically with the class, only start according to the service name;
oneshot: No restart after the service exits;
user/group: Set the user/user group that executes the service, the default is root;
class: Set the class name to which it belongs, when When the class starts/exits, the service also starts/stops, the default is default;
onrestart: execute the corresponding command when the service restarts;
socket: create a socket named /dev/socket/<name>
critical: the service within the specified time If you keep restarting, the system will restart and enter the recovery mode
default: means disabled=false, oneshot=false, critical=false.

7.5 import


Used to import other rc files

Command: import <filename>

7.6 init.rc parsing process

7.6.1 LoadBootScripts

Code path: platform\system\core\init\init.cpp

Description: If there is no special configuration ro.boot.init_rc, then parse ./init.rc

把/system/etc/init,/product/etc/init,/product_services/etc/init,/odm/etc/init,

/vendor/etc/init These paths are added to the paths parsed after init.rc, after the init.rc parsing is completed, parse the rc files in these directories

static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser = CreateParser(action_manager, service_list);
 
    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        parser.ParseConfig("/init.rc");
        if (!parser.ParseConfig("/system/etc/init")) {
            late_import_paths.emplace_back("/system/etc/init");
        }
        if (!parser.ParseConfig("/product/etc/init")) {
            late_import_paths.emplace_back("/product/etc/init");
        }
        if (!parser.ParseConfig("/product_services/etc/init")) {
            late_import_paths.emplace_back("/product_services/etc/init");
        }
        if (!parser.ParseConfig("/odm/etc/init")) {
            late_import_paths.emplace_back("/odm/etc/init");
        }
        if (!parser.ParseConfig("/vendor/etc/init")) {
            late_import_paths.emplace_back("/vendor/etc/init");
        }
    } else {
        parser.ParseConfig(bootscript);
    }
}
 

After Android7.0, init.rc was split, each service has its own rc file, they are basically loaded into /system/etc/init, /vendor/etc/init, /odm/etc/init Wait for the directory, after the init.rc analysis is completed, it will analyze the rc files in these directories to perform related actions.

Code path: platform\system\core\init\init.cpp

Description: Create Parser parsing objects, such as service, on, import objects

Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser;
 
    parser.AddSectionParser(
            "service", std::make_unique<ServiceParser>(&service_list, subcontexts, std::nullopt));
    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
    return parser;
}

7.6.2 Executing Actions

Add related Actions to the trigger queue in order, the order is early-init -> init -> late-init. Then in the loop, execute all the execution functions with Command in the Action in the trigger queue.

am.QueueEventTrigger("early-init");
am.QueueEventTrigger("init");
am.QueueEventTrigger("late-init");
...
while (true) {
if (!(waiting_for_prop || Service::is_exec_service_running())) {
            am.ExecuteOneCommand();
        }
}
 

7.6.3 Zygote startup

Starting from Android 5.0, Android supports 64-bit compilation, so zygote itself also supports 32-bit and 64-bit. Use the attribute ro.zygote to control the startup of different versions of the zygote process.

In the import section of init.rc we see the following code:

import /init.${ro.zygote}.rc // It can be seen that init.rc no longer directly imports a fixed file, but imports different files according to the content of the attribute ro.zygote

 init.rc is located under /system/core/rootdir. In this path also include four rc files about zygote.

They are init.zygote32.rc, init.zygote32_64.rc, init.zygote64.rc, init.zygote64_32.rc, which file is determined by the hardware.

Take the 64-bit processor as an example, the code of init.zygote64.rc is as follows:

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    class main # class is an option, specifying the type of zygote service as main
            priority -20
            user root
           group root readproc reserved_disk
            socket zygote stream 660 root system # socket keyword indicates an option, create a socket named dev/socket/zygote, type is stream, permission is 660 socket
           usap_pool_primary stream 660 root system
            onrestart write /sys/android_power/request_state wake # onrestart is a option, indicating the command to be executed when the zygote restarts.
            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
 

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server 解析:

service zygote: A zygote service is defined in init.zygote64.rc. The init process is to create the zygote process through this service name

/system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server解析:

The zygote service is run by executing /system/bin/app_process64 and passing in 4 parameters:

Parameter 1: -Xzygote This parameter will be used as the parameter required when the virtual machine starts
Parameter 2: /system/bin represents the directory where the virtual machine program is located
Parameter 3: --zygote indicates that the main function in the ZygoteInit.java class is executed as a virtual machine Entry
parameter 4: --start-system-server tells the Zygote process to start the systemServer process
 

8. Summary


The main work of the first stage of the init process is to mount partitions, create device nodes and some key directories, initialize the log output system, and enable SELinux security policies.

The main work of the second stage of the init process is to initialize the attribute system, analyze the matching rules of SELinux, process the termination signal of the child process, and start the system attribute service. It can be said that each item is very important. If the first stage is for the attribute system, SELinux does Prepare, then the second stage is to actually implement these functions.

The third stage of init is mainly to analyze init.rc to start other processes, enter an infinite loop, and monitor sub-processes in real time.

Guess you like

Origin blog.csdn.net/s_nshine/article/details/131712990