preface

The init process is started by idle (wrapper), a kernel – type 0 process

The body of the

Overview of the overall process

Specific source code analysis

The source code for init is in the system/core/init package. Let’s start with the entry class main.cpp

int main(int argc, char** argv) { #if __has_feature(address_sanitizer) __asan_set_error_report_callback(AsanReportCallback); #endif if (! strcmp(basename(argv[0]), "ueventd")) { return ueventd_main(argc, argv); } if (argc > 1) { if (! strcmp(argv[1], "subcontext")) { android::base::InitLogging(argv, &android::base::KernelLogger); const BuiltinFunctionMap function_map; return SubcontextMain(argc, argv, &function_map); } if (! strcmp(argv[1], "selinux_setup")) { // This function initializes SELinux then execs init to run in the init SELinux context. return SetupSelinux(argv); // Initialize SELinux and start the init process with a system call to execs} if (! strcmp(argv[1], "second_stage")) { return SecondStageMain(argc, argv); }} return FirstStageMain(argc, argv); // phase 1}Copy the code

You can see that the main. CPP function is quite different from the previous version

Get Android9.0 source androidxref.com/9.0.0_r3/xr… In Android10, instead of just calling init::main, some of the flow judgment is put into mian. CPP, so if you go to the main function in init. CPP, you won’t find the entry

How is the init process started

How does the init process start

system/core/Selinux.cpp

// This function initializes SELinux then execs init to run in the init SELinux context. int SetupSelinux(char** argv) {  InitKernelLogging(argv); if (REBOOT_BOOTLOADER_ON_PANIC) { InstallRebootSignalHandlers(); } // Set up SELinux, loading the SELinux policy. SelinuxSetupKernelLogging(); SelinuxInitialize(); // We're in the kernel domain and want to transition to the init domain. File systems that // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here, // but other file systems do. In particular, this is needed for ramdisks such as the // recovery image for A/B devices. if (selinux_android_restorecon("/system/bin/init", 0) == -1) { PLOG(FATAL) << "restorecon failed of /system/bin/init failed"; } const char* path = "/system/bin/init"; //init binary directory const char* args[] = {path, "second_stage", nullptr}; execv(path, const_cast<char**>(args)); Execv () only returns if an error happened, in which case we // panic and never return from this function. PLOG(FATAL) << "execv(\"" << path << "\") failed"; return 1; }Copy the code

The SetupSelinux function above mainly starts the init process with evecv

What FirstStageMain does

Let’s look again at what the first stage function FirstStageMain does

system/core/first_stage_main.cpp

int FirstStageMain(int argc, char** argv) {
    if (REBOOT_BOOTLOADER_ON_PANIC) {  //是否定义由init.mk决定
        InstallRebootSignalHandlers(); //处理init挂掉的情况,会重启bootloader
    }

    boot_clock::time_point start_time = boot_clock::now();

    std::vector<std::pair<std::string, int>> errors;
#define CHECKCALL(x) 
    if (x != 0) errors.emplace_back(#x " failed", errno);

    // Clear the umask.
    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.
    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
    // Don't expose the raw commandline to unprivileged processes.
    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 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)));

    // These below mounts are done in first stage init so that first stage mount can mount
    // subdirectories of /mnt/{vendor,product}/.  Other mounts, not required by first stage mount,
    // should be done in rc files.
    // Mount staging areas for devices managed by vold
    // See storage config details at http://source.android.com/devices/storage/
    CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=1000"));
    // /mnt/vendor is used to mount vendor-specific partitions that can not be
    // part of the vendor partition, e.g. because they are mounted read-write.
    CHECKCALL(mkdir("/mnt/vendor", 0755));
    // /mnt/product is used to mount product-specific partitions that can not be
    // part of the product partition, e.g. because they are mounted read-write.
    CHECKCALL(mkdir("/mnt/product", 0755));

    // /apex is used to mount APEXes
    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

    SetStdioToDevNull(argv);
    // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
    // talk to the outside world...
    InitKernelLogging(argv);

    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";
    }

    LOG(INFO) << "init first stage started!";

    auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};
    if (!old_root_dir) {
        PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";
    }

    struct stat old_root_info;
    if (stat("/", &old_root_info) != 0) {
        PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
        old_root_dir.reset();
    }

    if (ForceNormalBoot()) {
        mkdir("/first_stage_ramdisk", 0755);
        // SwitchRoot() must be called with a mount point as the target, so we bind mount the
        // target directory to itself here.
        if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
            LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
        }
        SwitchRoot("/first_stage_ramdisk");
    }

    // If this file is present, the second-stage init will use a userdebug sepolicy
    // and load adb_debug.prop to allow adb root, if the device is unlocked.
    if (access("/force_debuggable", F_OK) == 0) { //如果该文件存在且已经解锁bootbloade r,则允许调用adb root指令(userdebug sepolicy)
        std::error_code ec;  // to invoke the overloaded copy_file() that won't throw.
        if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) ||
            !fs::copy_file("/userdebug_plat_sepolicy.cil", kDebugRamdiskSEPolicy, ec)) {
            LOG(ERROR) << "Failed to setup debug ramdisk";
        } else {
            // setenv for second-stage init to read above kDebugRamdisk* files.
            setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
        }
    }

    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);

    const char* path = "/system/bin/init"; //找到init的二进制文件目录
    const char* args[] = {path, "selinux_setup", nullptr};
    execv(path, const_cast<char**>(args)); //通过execv来启动init进程

    // execv() only returns if an error happened, in which case we
    // panic and never fall through this conditional.
    PLOG(FATAL) << "execv(\"" << path << "\") failed";

    return 1;
}
Copy the code

Now that we’ve commented out the main code, let’s summarize the work of FirstStageMain

  1. Handle init process hangs
  2. Set user groups and mount related system files
  3. According to the/force_debuggableFile to determine whether to allow adb root directives
  4. Find the init binary directory and start the init process with execv

Now that the init process is started, let’s come back and see what the SecondStageMain function does next

What does SecondStageMain do

int SecondStageMain(int argc, char** argv) {
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }

    SetStdioToDevNull(argv);
    InitKernelLogging(argv);
    LOG(INFO) << "init second stage started!";

    // Set init and its forked children's oom_adj.
    if (auto result = WriteFile("/proc/1/oom_score_adj", "-1000"); !result) {
        LOG(ERROR) << "Unable to write -1000 to /proc/1/oom_score_adj: " << result.error();
    }

    // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
    GlobalSeccomp();

    // 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.
    keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);

    // Indicate that booting is in progress to background fw loaders, etc.
    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));

    property_init(); //初始化系统属性,使用mmap共享内存,"/dev/__properties__/property_info"

    // If arguments are passed both on the command line and in DT,
    // properties set in DT always have priority over the command-line ones.
    process_kernel_dt();
    process_kernel_cmdline();

    // Propagate the kernel variables to internal variables
    // used by init as well as the current required 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;
    }

    // Clean up our environment.
    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();
    SelinuxRestoreContext();

    Epoll epoll; //使用IO复用机制,epoll,即 event poll,是poll机制的升级版
    if (auto result = epoll.Open(); !result) {
        PLOG(FATAL) << result.error();
    }

    InstallSignalFdHandler(&epoll);  //使用epoll对init子进程的信号进行监听

    property_load_boot_defaults(load_debug_prop);
    UmountDebugRamdisk();
    fs_mgr_vendor_overlay_mount_all();
    export_oem_lock_status();
    StartPropertyService(&epoll); //开启属性服务,并注册到epoll中
    MountHandler mount_handler(&epoll);
    set_usb_controller();

    const BuiltinFunctionMap function_map;
    Action::set_function_map(&function_map);

    if (!SetupMountNamespaces()) {
        PLOG(FATAL) << "SetupMountNamespaces failed";
    }

    subcontexts = InitializeSubcontexts();

    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();

    LoadBootScripts(am, sm); //加载系统启动脚本"/init.rc"

    // 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();

    // Make the GSI status available before scripts start running.
    if (android::gsi::IsGsiRunning()) {
        property_set("ro.gsid.image_running", "1");
    } else {
        property_set("ro.gsid.image_running", "0");
    }

    am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");

    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(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
    am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
    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");
    am.QueueBuiltinAction(console_init_action, "console_init");

    // Trigger all the boot actions to get us started.
    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_random 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");

    // 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");
	//解析启动脚本
    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;
            }
        }

        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;
}

Copy the code

Summary of the main work of SecondStageMain

We found that the function is in fact is the system/core/init/init. CPP entry function, in Android9.0 version, the class of function to the main entrance, a key part of the code I have made a comment, now let’s sum up the main work of SecondStageMain

  1. Use epoll to listen for signals from the init child process
  2. Initialize system properties, use mmap shared memory, “/dev/properties/property_info” (important)
  3. Enable the properties service and register with epoll (important)
  4. Load system startup script “/init.rc”
  5. Parse the startup script to start the related services

Dig deep into system properties

Let’s dig a little deeper, how are system properties initialized

system/core/init/property_service.cpp

void property_init() { mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH); CreateSerializedPropertyInfo(); If (__system_property_area_init()) {// Initialize system_property memory area LOG(FATAL) << "Failed to initialize property area"; } if (! property_info_area.LoadDefaultPath()) { LOG(FATAL) << "Failed to load serialized property info file"; }}Copy the code

Again, the __system_property_area_init() function, Here has reached a bionic package/Volumes/Fwk_Jack/WORKING_DIRECTORY/bionic/libc/bionic/system_property_api CPP

__BIONIC_WEAK_FOR_NATIVE_BRIDGE int __system_property_area_init() { bool fsetxattr_failed = false; return system_properties.AreaInit(PROP_FILENAME, &fsetxattr_failed) && ! fsetxattr_failed ? 0:1; }Copy the code

This function calls system_properties.areainit () again, and we continue with /bionic/libc/system_properties/system_properties.cpp

bool SystemProperties::AreaInit(const char* filename, bool* fsetxattr_failed) { if (strlen(filename) >= PROP_FILENAME_MAX) { return false; } strcpy(property_filename_, filename); contexts_ = new (contexts_data_) ContextsSerialized(); if (! contexts_->Initialize(true, property_filename_, fsetxattr_failed)) { return false; } initialized_ = true; return true; }Copy the code

The contexts_->Initialize() function is called

/bionic/libc/system_properties/contexts_serialized.cpp

bool ContextsSerialized::Initialize(bool writable, const char* filename, bool* fsetxattr_failed) { filename_ = filename; if (! InitializeProperties()) {// Initialize system properties return false; } if (writable) { mkdir(filename_, S_IRWXU | S_IXGRP | S_IXOTH); bool open_failed = false; if (fsetxattr_failed) { *fsetxattr_failed = false; } for (size_t i = 0; i < num_context_nodes_; ++i) { if (! context_nodes_[i].Open(true, fsetxattr_failed)) { open_failed = true; } } if (open_failed || ! MapSerialPropertyArea(true, fsetxattr_failed)) { FreeAndUnmap(); return false; } } else { if (! MapSerialPropertyArea(false, nullptr)) { FreeAndUnmap(); return false; } } return true; }Copy the code

InitializeProperties() is called to initialize the system properties

bool ContextsSerialized::InitializeProperties() { if (! Property_info_area_file_.loaddefaultpath ()) {// Load the default system property path return false; } if (! InitializeContextNodes()) { FreeAndUnmap(); return false; } return true; }Copy the code

/system/core/property_service/libpropertyinfoparser/property_info_parser.cpp

bool PropertyInfoAreaFile::LoadDefaultPath() { return LoadPath("/dev/__properties__/property_info"); // Load the file into memory}Copy the code
bool PropertyInfoAreaFile::LoadPath(const char* filename) { int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY); struct stat fd_stat; if (fstat(fd, &fd_stat) < 0) { close(fd); return false; } if ((fd_stat.st_uid ! = 0) || (fd_stat.st_gid ! = 0) || ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) ! = 0) || (fd_stat.st_size < static_cast<off_t>(sizeof(PropertyInfoArea)))) { close(fd); return false; } auto mmap_size = fd_stat.st_size; // Call mmap to create shared memory Void * map_result = mmap(nullptr, MMAP_size, PROT_READ, MAP_SHARED, fd, 0); if (map_result == MAP_FAILED) { close(fd); return false; } auto property_info_area = reinterpret_cast<PropertyInfoArea*>(map_result); if (property_info_area->minimum_supported_version() > 1 || property_info_area->size() ! = mmap_size) { munmap(map_result, mmap_size); close(fd); return false; } close(fd); mmap_base_ = map_result; mmap_size_ = mmap_size; return true; }Copy the code

Initialization of system properties is done using mMAP’s memory sharing mechanism to allow other processes to obtain system properties

So why turn on a property service when you can access it through shared memory? Directly through the shared memory to set system properties, isn’t it good?

If all processes are free to modify system properties, can system properties still be called system properties? So Android is designed so that other processes can only access system properties through shared memory, and the “modify” permissions are all gathered into the init process to enable a property service, as shown in the figure below

Dig deeper into system services

Now let’s further analyze how the system service is started

/system/core/init/property_service.cpp

void StartPropertyService(Epoll* epoll) { selinux_callback cb; cb.func_audit = SelinuxAuditCallback; selinux_set_callback(SELINUX_CB_AUDIT, cb); property_set("ro.property_service.version", "2"); / / set the system properties property_set_fd = CreateSocket (PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, false, 0666, 0. 0, nullptr); If (property_set_fd == -1) {PLOG(FATAL) << "start_property_service socket creation failed"; } listen(property_set_fd, 8); // Listen to the sokcet service, If (auto result = epoll->RegisterHandler(property_set_fd, handle_property_set_fd); if (auto result = epoll->RegisterHandler(property_set_fd); ! result) { PLOG(FATAL) << result.error(); }}Copy the code

The important steps to enable the properties service have been commented out, and are now summarized

Process for enabling system services

  1. Example Create a socket server
  2. Listen to sokcet service. The maximum concurrency is 8
  3. It registers with the Epoll handler for I/O optimization

So far, the main work flow and important principle of init process have been analyzed

In the following article, I will take you to read the source code in the way of video and handouts, so that you really understand how to read the source code

Write in the last

Think so? Why don’t you just like it and follow it?

Solemnly declare

The original author of this article is Shuran, copyright ©️ belongs to Android Institute all rights, infringement will be investigated