banner
fwrite

fwrite

好好生活
twitter
github
email

Init process

Boot Overview#

The first boot process of Android, with init's PID being 1, parses init.rc to construct the system's initial operating state, after which other systems will start sequentially.
Power On & System Boot
When the power is turned on, it will load the program (BootLoader) from a predetermined location (PC reads ROM) into RAM and begin execution.
BootLoader
The BootLoader is a small program that runs before the Android system starts, primarily responsible for bringing up and running the system OS.
Linux Kernel Boot
When the kernel starts, it sets up caches, protects storage, schedules lists, loads drivers, etc. After the kernel completes system setup, it will look for the init.rc file in the system and start the init process.
Init Process Startup
This process mainly initializes and starts the system property service and is also used to start the Zygote process.
Launcher App (Desktop Application)

Init Process Overview#

As the first process to start the Android system, init parses init.rc to sequentially start key system service processes, which include three key service processes:
ServiceManager (similar to DNS, used to find service handles in SystemServer)
Zygote (initial process, loads necessary Android resources, starts the virtual machine, etc.)
SystemServer (starts system-level services, such as AMS, WMS, etc.)
The init process will start the Zygote and SystemServer processes, while Zygote listens for AMS via Socket, and ServiceManager communicates using Binder.
Creates and mounts the necessary file directories for startup.
Initializes and starts essential services.
Parses the init.rc configuration file and starts the Zygote process.

Init Process - main.cpp Analysis#

  // /init/main.cpp
  
  #include "builtins.h"
  #include "first_stage_init.h"
  #include "init.h"
  #include "selinux.h"
  #include "subcontext.h"
  #include "ueventd.h"
  
  using namespace android::init;
  
  ... omitted parts
  
  int main(int argc, char** argv) {    
      ... omitted parts
  
      // Boost prio which will be restored later
      setpriority(PRIO_PROCESS, 0, -20);
  
      if (!strcmp(basename(argv[0]), "ueventd")) {    // ueventd is responsible for creating device nodes, permissions, anonymous shared memory, etc.
          return ueventd_main(argc, argv);
      }
  
      if (argc > 1) {
          if (!strcmp(argv[1], "subcontext")) {
              // Initialize Logger
              android::base::InitLogging(argv, &android::base::KernelLogger);
              const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
              return SubcontextMain(argc, argv, &function_map);
          }
          // SELinux security-related
          if (!strcmp(argv[1], "selinux_setup")) {
              return SetupSelinux(argv);
          }
  
          // Init process (second stage)
          if (!strcmp(argv[1], "second_stage")) {
              return SecondStageMain(argc, argv);
          }
      }
  
      // Start init process (first stage)
      return FirstStageMain(argc, argv);
  }

First Stage of Init Process - first_stage_init#FirstStageMain File Mounting#

The first stage mainly involves 1. creating and mounting related files, listing several important file systems here, detailed functions can be found in comments, 2. setting input parameters, 3. executing execv function (reloading main.cpp with different parameters).
The mounted files are all in memory (RAM) and are physical files.

Command UsedMount Directory/File Name, File ManagementFeaturesDescription
mounttmpfsExists in RAM and is not persistent (named tmp)A virtual memory file system that stores all files in virtual memory; once tmpfs is unmounted, all internal data will disappear.
mount/dev/devptsDynamically mounts devptsProvides a standard for pseudo-terminals; whenever the pty master device /dev/ptmx is opened, a new pty device file will be dynamically created under /dev/pts.
mountprocCan modify kernel parameters at runtimeA virtual file system that can be seen as an interface to the kernel's internal data structures.
mountsysfsIntroduced in Linux 2.6 kernel (mainly for hardware-related parameters, which relate to devpts devices)A virtual file system typically mounted at /sys.
  // first_stage_init.cpp
  
  int FirstStageMain(int argc, char** argv) {
  
      if (REBOOT_BOOTLOADER_ON_PANIC) {
          InstallRebootSignalHandlers();    // Restart Boot Loader on init error
      }
  
      // Record boot time
      boot_clock::time_point start_time = boot_clock::now();
      // Record list of errors
      std::vector<std::pair<std::string, int>> errors;
  
      
  #define CHECKCALL(x) \
      if ((x) != 0) errors.emplace_back(#x " failed", errno);
  
      // Clear umask, files and folders created afterwards will have full permissions
      umask(0);
      CHECKCALL(clearenv());
      CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
      // Get the basic filesystem setup we need put together in the initramdisk
      // on / and then we'll let the rc file figure out the rest.
      // mount is used to mount the tmpfs file system
      CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
  
      // Create folders and assign permissions
      CHECKCALL(mkdir("/dev/pts", 0755));
      CHECKCALL(mkdir("/dev/socket", 0755));
      CHECKCALL(mkdir("/dev/dm-user", 0755));
  
      CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
  
  #define MAKE_STR(x) __STRING(x)
      // Mount proc
      CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
  #undef MAKE_STR
      // Don't expose the raw command line to unprivileged processes.
      CHECKCALL(chmod("/proc/cmdline", 0440));
      std::string cmdline;
      android::base::ReadFileToString("/proc/cmdline", &cmdline);
      // Don't expose the raw bootconfig to unprivileged processes.
      chmod("/proc/bootconfig", 0440);
      std::string bootconfig;
      android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
  
      // 8.0 added user groups
      gid_t groups[] = {AID_READPROC};
      // Add groups to the current process's device
      CHECKCALL(setgroups(arraysize(groups), groups));
      
      // Mount sys, using sysfs to manage documents
      CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
      CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
  
      // Create nodes (mknod) for output Log
      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 needed for log wrapper, which gets called before ueventd runs.
      CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
      CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
      
      // See storage config details at http://source.android.com/devices/storage/
      // Mount /mnt/{vendor,product}, using tmpfs to manage
      CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                      "mode=0755,uid=0,gid=1000"));
      
      // /mnt/vendor is used to mount vendor
      CHECKCALL(mkdir("/mnt/vendor", 0755));
      // /mnt/product is used to mount product-specific partitions that cannot be
      // part of the product partition, e.g., because they are mounted read-write.
      CHECKCALL(mkdir("/mnt/product", 0755));
      // /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"));
      // /second_stage_resources is used to preserve files from first to second
      // stage init
      CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                      "mode=0755,uid=0,gid=0"))
  #undef CHECKCALL
      SetStdioToDevNull(argv);
    
      InitKernelLogging(argv);
  
      // List error messages
      if (!errors.empty()) {
          for (const auto& [error_string, error_errno] : errors) {
              LOG(ERROR) << error_string << " " << strerror(error_errno);
          }
          LOG(FATAL) << "Init encountered errors starting first stage, aborting";
      }
  
      ... omitted parts
  
      return 1;
  }

Mount Specific Partition Devices

  // first_stage_init.cpp
  
  int FirstStageMain(int argc, char** argv) {
      
      ... omitted parts
          
      // Mount specific partition devices
      if (!DoFirstStageMount(!created_devices)) {
          LOG(FATAL) << "Failed to mount required partitions early ...";
      }
      
      ... omitted parts
  }

SELinux Related Work

  // first_stage_init.cpp
  
  int FirstStageMain(int argc, char** argv) {
  
      ... omitted parts
  
      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);
      }
      
      // Initialize security framework: Android Verified Boot & AVB mainly used to prevent system files from being tampered with
      // Also prevents system rollback functionality
      SetInitAvbVersionInRecovery();
      setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),
             1);
  
      ... omitted parts
  
  }

Starting the Init Process via execv's SecondStageMain Method

  // first_stage_init.cpp
  
  int FirstStageMain(int argc, char** argv) {
  
      ... omitted parts
  
      // Set init process parameters
      const char* path = "/system/bin/init";
      const char* args[] = {path, "selinux_setup", nullptr};
      // Binder open (log message)
      auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
      dup2(fd, STDOUT_FILENO);
      dup2(fd, STDERR_FILENO);
      close(fd);
  
      // Pass the above args
      // Start the init process, passing the parameter "selinux_setup"
      execv(path, const_cast<char**>(args));        // Start the init process's SecondStageMain method via execv
  
  }

Init Process Interlude SE#

selinux.cpp#SetupSelinux
Before the second stage, SE settings will be performed.
After the first_stage_init#FirstStageMain file mounting, the execv function will restart the main.cpp process, entering the main.cpp#main method again.

  // /init/main.cpp file
  
  #include "builtins.h"
  #include "first_stage_init.h"
  #include "init.h"
  #include "selinux.h"
  #include "subcontext.h"
  #include "ueventd.h"
  
  using namespace android::init;
  
  ... omitted parts
  
  // Upon re-entry, argv contains the string selinux_setup
  int main(int argc, char** argv) {    
      ... omitted parts
  
      // Boost prio which will be restored later
      setpriority(PRIO_PROCESS, 0, -20);
  
      // Check input parameters
      if (!strcmp(basename(argv[0]), "ueventd")) {    // ueventd is responsible for creating device nodes, permissions, anonymous shared memory, etc.
          return ueventd_main(argc, argv);
      }
  
      // When the number of parameters > 1
      if (argc > 1) {
          if (!strcmp(argv[1], "subcontext")) {
              // Initialize Log
              android::base::InitLogging(argv, &android::base::KernelLogger);
              const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
              return SubcontextMain(argc, argv, &function_map);
          }
          // SELinux security-related
          if (!strcmp(argv[1], "selinux_setup")) {
              return SetupSelinux(argv);
          }
  
          // Init process (second stage)
          if (!strcmp(argv[1], "second_stage")) {
              return SecondStageMain(argc, argv);
          }
      }
  
      // Start init process (first stage)
      return FirstStageMain(argc, argv);
  }

SetupSelinux mainly loads the SE policy, and after loading SE is complete, it will set new parameters and restart main.cpp via execv (as explained in the previous section).

  // /init/selinux.cpp
  
  int SetupSelinux(char** argv) {
  
      SetStdioToDevNull(argv);
  
      // 1. Initialize core Log
      InitKernelLogging(argv);
      if (REBOOT_BOOTLOADER_ON_PANIC) {
          InstallRebootSignalHandlers();
      }
      boot_clock::time_point start_time = boot_clock::now();
  
      // 2. Mount missing system blocks
      MountMissingSystemPartitions();
      SelinuxSetupKernelLogging();
      LOG(INFO) << "Opening SELinux policy";
      PrepareApexSepolicy();
  
      // Read the policy before potentially killing snapuserd.
      std::string policy;
      // Read policy
      ReadPolicy(&policy);
  
      CleanupApexSepolicy();
      auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
      if (snapuserd_helper) {
          // Kill the old snapused to avoid audit messages. After this we cannot
          // read from /system (or other dynamic partitions) until we call
          // FinishTransition().
          snapuserd_helper->StartTransition();
      }
  
      // Load SE policy
      LoadSelinuxPolicy(policy);
  
      ... omitted parts
  
      const char* path = "/system/bin/init";
      const char* args[] = {path, "second_stage", nullptr};
  
      // Restart the process
      execv(path, const_cast<char**>(args));
  
      return 1;
  }

Second Stage of Init Process#

init.cpp#SecondStageMain Parsing init.rc
After the initialization and mounting of devices in first_stage_init, the main.cpp class's SecondStageMain function will be executed (which can be seen as the second stage of the init process).

  // /init/main.cpp file
  
  #include "builtins.h"
  #include "first_stage_init.h"
  #include "init.h"
  #include "selinux.h"
  #include "subcontext.h"
  #include "ueventd.h"
  
  using namespace android::init;
  
  ... omitted parts
  
  // Upon re-entry, argv contains the string second_stage
  int main(int argc, char** argv) {    
      ... omitted parts
  
      // Boost prio which will be restored later
      setpriority(PRIO_PROCESS, 0, -20);
  
      // Check input parameters
      if (!strcmp(basename(argv[0]), "ueventd")) {   
          return ueventd_main(argc, argv);
      }
  
      if (argc > 1) {
          if (!strcmp(argv[1], "subcontext")) {
              // Initialize Log
              android::base::InitLogging(argv, &android::base::KernelLogger);
              const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
              return SubcontextMain(argc, argv, &function_map);
          }
          // SELinux security-related
          if (!strcmp(argv[1], "selinux_setup")) {
              return SetupSelinux(argv);
          }
  
          // Init process (second stage)
          if (!strcmp(argv[1], "second_stage")) {
              return SecondStageMain(argc, argv);
          }
      }
  
      // Start init process (first stage)
      return FirstStageMain(argc, argv);
  }

Begin analyzing init.cpp#SecondStageMain, divided into 10 stages, detailed in comments.
Initialize property domains, mainly performing the PropertyInit function.

  // init/init.cpp
  
  int SecondStageMain(int argc, char** argv) {
      if (REBOOT_BOOTLOADER_ON_PANIC) {
          InstallRebootSignalHandlers();        // Restart Boot Loader on init failure
      }
  
      // Boot time
      boot_clock::time_point start_time = boot_clock::now();
  
      trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
  
      // Redirect standard input, output, and error to the null device file `/dev/null`
      SetStdioToDevNull(argv);
  
      // Initialize kernel log system
      InitKernelLogging(argv);
      
      // Initialize system $PATH
      if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {
          PLOG(FATAL) << "Could not set $PATH to '" << _PATH_DEFPATH << "' in second stage";
      }
  
      // The init process does not depend on other processes, so it should not crash,
      // thus the function to handle signals is set to null here.
      {
          // sigaction is for signals
          struct sigaction action = {.sa_flags = SA_RESTART};
          action.sa_handler = [](int) {};
          // Ignore SIGPIPE signal
          sigaction(SIGPIPE, &action, nullptr);    // Set SIGPIPE to null
      }
      // Set init and its forked children's oom_adj.
      if (auto result =
                  WriteFile("/proc/1/oom_score_adj", StringPrintf("%d", DEFAULT_OOM_SCORE_ADJUST));
          !result.ok()) {
          LOG(ERROR) << "Unable to write " << DEFAULT_OOM_SCORE_ADJUST
                     << " to /proc/1/oom_score_adj: " << result.error();
      }
      // Set up a session keyring that all processes will have access to. It
      // will hold things like FBE encryption keys. No process should override
      // its session keyring.
      // Call syscall to set related parameters
      keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
  
      // Indicate that booting is in progress to background fw loaders, etc.
      // Create /dev/.booting file, marking that booting is in progress
      close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
  
      ... omitted parts
  
      PropertyInit();        // Implemented in init/property_service.cpp
  
      ... omitted parts
  
      return 0;
  }

Clear environment variables: Clear previously written environment variables from system properties.

  // /init/init.cpp
  
  int SecondStageMain(int argc, char** argv) {
      ... omitted parts
  
      // Clear previously written environment variables from system properties
      // Clean up our environment.
      unsetenv("INIT_AVB_VERSION");
      unsetenv("INIT_FORCE_DEBUGGABLE");
  
      ... omitted parts
  }

SELinux related work

  // /init/init.cpp
  
  int SecondStageMain(int argc, char** argv) {
      ... omitted parts
  
      // Now set up SELinux for the second stage.
      SelinuxSetupKernelLogging();        // Complete SELinux work for the second stage
      SelabelInitialize();                // Register some processors
      SelinuxRestoreContext();            // SELinux stored Context
  
      ... omitted parts
  }

Create epoll event notifications

  // /init/init.cpp
  
  int SecondStageMain(int argc, char** argv) {
  
      ... omitted parts
  
      Epoll epoll;
      // epoll.Open() creates child process I/O listener
      if (auto result = epoll.Open(); !result.ok()) {
          PLOG(FATAL) << result.error();
      }
  
      ... omitted parts
  
  }

Load child process signal handlers: init is a daemon process (can be understood as a daemon thread), with this process as the main one; if the init process ends, all other child processes will end.

  // /init/init.cpp
  
  int SecondStageMain(int argc, char** argv) {
  
      ... omitted parts
  
      // Create handler to process child process termination signals, register a signal to epoll listener (if there are changes, epoll will notify)
      // epoll will receive notifications first, then read data using socket
          
      // To prevent child processes of the init process from becoming zombie processes
      InstallSignalFdHandler(&epoll);
      InstallInitNotifier(&epoll);
  
      ... omitted parts
  
  }

Start property service: the main function StartPropertyService starts a socket listening for property changes.

  // /init/init.cpp
  static int property_fd = -1;
  
  int SecondStageMain(int argc, char** argv) {
  
      ... omitted parts
  
      // Start other property services
      StartPropertyService(&property_fd);
  
      unsetenv("INIT_AVB_VERSION");    // Remove environment variable INIT_AVB_VERSION 
      fs_mgr_vendor_overlay_mount_all();
      export_oem_lock_status();        // Ultimately decides the value of "ro.boot.flash.locked"
  
      MountHandler mount_handler(&epoll);
  
      // Set UDC Controller for USB storage (sys/class/udc)
      SetUsbController();
  
      ... omitted parts
  }

Match command & function correspondence: Cmd & Function Map, from this we can see which cmd instructions the system supports and how the instructions actually operate.

  // /init/init.cpp
  
  int SecondStageMain(int argc, char** argv) {
  
      ... omitted parts
  
      // Create Function & Cmd Map
      const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
  
      // Save function_map object in Action
      // Action::set_function_map, `::` is equivalent to static Function
      Action::set_function_map(&function_map);
      if (!SetupMountNamespaces()) {
          PLOG(FATAL) << "SetupMountNamespaces failed";
      }
  
      ... omitted parts
  }

LoadBootScripts: parse init.rc files.

  // /init/init.cpp
  
  int SecondStageMain(int argc, char** argv) {
  
      ... omitted parts
      
      // The object will be used to obtain analysis results
      ActionManager& am = ActionManager::GetInstance();    
      ServiceList& sm = ServiceList::GetInstance();
  
      // Parse scripts
      LoadBootScripts(am, sm);
  
      ... omitted parts
  
  }
  
  // Parse .rc scripts
  static void LoadBootScripts(ActionManager& action_manager, 
                              ServiceList& service_list) {
      // Create parser
      Parser parser = CreateParser(action_manager, service_list);
  
      // Pre-set to parse ro.boot.init_rc
      std::string bootscript = GetProperty("ro.boot.init_rc", "");
  
      if (bootscript.empty()) {
          // Parse specified init.rc file
          parser.ParseConfig("/system/etc/init/hw/init.rc");
  
          if (!parser.ParseConfig("/system/etc/init")) {
              late_import_paths.emplace_back("/system/etc/init");
          }
  
          parser.ParseConfig("/system_ext/etc/init");
          if (!parser.ParseConfig("/vendor/etc/init")) {
              late_import_paths.emplace_back("/vendor/etc/init");
          }
          if (!parser.ParseConfig("/odm/etc/init")) {
              late_import_paths.emplace_back("/odm/etc/init");
          }
          if (!parser.ParseConfig("/product/etc/init")) {
              late_import_paths.emplace_back("/product/etc/init");
          }
      } else {
          parser.ParseConfig(bootscript);
      }
  }

Add trigger events: Here we can see some settings in the rc file, where common commands include 1 early-init, 2 init, 3 late-init.

  // /init/init.cpp
  
  int SecondStageMain(int argc, char** argv) {
  
      ... omitted parts
  
      // ActionManager parsing completed
      am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
      am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
      am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
      am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
      
      // Trigger label early-init
      am.QueueEventTrigger("early-init");
      // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
      am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
      // ... so that we can start queuing up actions that require stuff from /dev.
      am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
      Keychords keychords;
      am.QueueBuiltinAction(
              [&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {
                  for (const auto& svc : ServiceList::GetInstance()) {
                      keychords.Register(svc->keycodes());
                  }
                  keychords.Start(&epoll, HandleKeychord);
                  return {};
              },
              "KeychordInit");
      // Trigger all the boot actions to get us started.
      am.QueueEventTrigger("init");
      // Don't mount filesystems or start core system services in charger mode.
      std::string bootmode = GetProperty("ro.bootmode", "");
      if (bootmode == "charger") {
          am.QueueEventTrigger("charger");
      } else {
          am.QueueEventTrigger("late-init");
      }
      // Run all property triggers based on current state of the properties.
      am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
  
      ... omitted parts
  }

Enter a loop: Continuously process received commands & execute Services.

  // /init/init.cpp
  
  int SecondStageMain(int argc, char** argv) {
  
      ... omitted parts
  
      while(true) {
          // By default, sleep until something happens.
          auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
  
          // Determine if a shutdown command has been received
          auto shutdown_command = shutdown_state.CheckShutdown();
          
          if (shutdown_command) {
              HandlePowerctlMessage(*shutdown_command);
              shutdown_state.set_do_shutdown(false);
          }
  
          ... omitted parts
  
          // No need to wait && Service is not running || Service has completed running
          if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
              am.ExecuteOneCommand();    // Execute one cmd
          }
  
          ... omitted parts
      }
  
  }
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.