Android 源码解析系列1- Android init 进程启动流程

Android 源码解析系列1- Android init 进程启动流程

init进程是用户空间的第一个进程.如下

g1930fua_g210_ai:/ $ ps -ef | grep init
#uid              pid ppid C stime    TTY   time     cmd
root              1      0 0 21:12:44 ?     00:00:03 init second_stage
root            292      1 0 21:12:46 ?     00:00:01 init subcontext u:r:vendor_init:s0 15

入口文件 main.cpp

int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
    __asan_set_error_report_callback(AsanReportCallback);
#elif __has_feature(hwaddress_sanitizer)
    __hwasan_set_error_report_callback(AsanReportCallback);
#endif
    // Boost prio which will be restored later
    setpriority(PRIO_PROCESS, 0, -20);
    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 = GetBuiltinFunctionMap();

            return SubcontextMain(argc, argv, &function_map);
        }

        if (!strcmp(argv[1], "selinux_setup")) {
            return SetupSelinux(argv);
        }

        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);
        }
    }

    return FirstStageMain(argc, argv);
}

先执行FirstStageMain,主要做一些环境变量设置和挂载工作,然后执行SetupSelinux,初始化SeLinux,然后执行SecondStageMain主要加载 rc 脚本、启动系统服务.

SecondStageMain

system/core/init/init.cpp

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

    // No threads should be spin up until signalfd
    // is registered. If the threads are indeed required,
    // each of these threads _should_ make sure SIGCHLD signal
    // is blocked. See b/223076262
    boot_clock::time_point start_time = boot_clock::now();

    trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };

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

    SelinuxSetupKernelLogging();

    // Update $PATH in the case the second stage init is newer than first stage init, where it is
    // first set.
    if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {
        PLOG(FATAL) << "Could not set $PATH to '" << _PATH_DEFPATH << "' in second stage";
    }

    // Init should not crash because of a dependence on any other process, therefore we ignore
    // SIGPIPE and handle EPIPE at the call site directly.  Note that setting a signal to SIG_IGN
    // is inherited across exec, but custom signal handlers are not.  Since we do not want to
    // ignore SIGPIPE for child processes, we set a no-op function for the signal handler instead.
    {
        struct sigaction action = {.sa_flags = SA_RESTART};
        action.sa_handler = [](int) {};
        sigaction(SIGPIPE, &action, nullptr);
    }

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

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

    // 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");
    bool load_debug_prop = false;
    if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
        load_debug_prop = "true"s == force_debuggable_env;
    }
    unsetenv("INIT_FORCE_DEBUGGABLE");

    // Umount the debug ramdisk so property service doesn't read .prop files from there, when it
    // is not meant to.
    if (!load_debug_prop) {
        UmountDebugRamdisk();
    }

    PropertyInit();

    // Umount second stage resources after property service has read the .prop files.
    UmountSecondStageRes();

    // Umount the debug ramdisk after property service has read the .prop files when it means to.
    if (load_debug_prop) {
        UmountDebugRamdisk();
    }

    // Mount extra filesystems required during second stage init
    MountExtraFilesystems();

    // Now set up SELinux for second stage.
    SelabelInitialize();
    SelinuxRestoreContext();

    Epoll epoll;
    if (auto result = epoll.Open(); !result.ok()) {
        PLOG(FATAL) << result.error();
    }

    // We always reap children before responding to the other pending functions. This is to
    // prevent a race where other daemons see that a service has exited and ask init to
    // start it again via ctl.start before init has reaped it.
    epoll.SetFirstCallback(ReapAnyOutstandingChildren);

    InstallSignalFdHandler(&epoll);
    InstallInitNotifier(&epoll);
    StartPropertyService(&property_fd);

    // If boot_timeout property has been set in a debug build, start the boot monitor
    if (GetBoolProperty("ro.debuggable", false)) {
        int timeout = GetIntProperty("ro.boot.boot_timeout", 0);
        if (timeout > 0) {
            StartSecondStageBootMonitor(timeout);
        }
    }

    // Make the time that init stages started available for bootstat to log.
    RecordStageBoottimes(start_time);

    // Set libavb version for Framework-only OTA match in Treble build.
    if (const char* avb_version = getenv("INIT_AVB_VERSION"); avb_version != nullptr) {
        SetProperty("ro.boot.avb_version", avb_version);
    }
    unsetenv("INIT_AVB_VERSION");

    fs_mgr_vendor_overlay_mount_all();
    export_oem_lock_status();
    MountHandler mount_handler(&epoll);
    SetUsbController();
    SetKernelVersion();

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

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

    InitializeSubcontext();

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

    LoadBootScripts(am, sm); // 1

    // 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.
    auto is_running = android::gsi::IsGsiRunning() ? "1" : "0";
    SetProperty(gsi::kGsiBootedProp, is_running);
    auto is_installed = android::gsi::IsGsiInstalled() ? "1" : "0";
    SetProperty(gsi::kGsiInstalledProp, is_installed);
    if (android::gsi::IsGsiRunning()) {
        std::string dsu_slot;
        if (android::gsi::GetActiveDsu(&dsu_slot)) {
            SetProperty(gsi::kDsuSlotProp, dsu_slot);
        }
    }

    // This needs to happen before SetKptrRestrictAction, as we are trying to
    // open /proc/kallsyms while still being allowed to see the full addresses
    // (since init holds CAP_SYSLOG, and Linux boots with kptr_restrict=0). The
    // address visibility through the saved fd (more specifically, the backing
    // open file description) will then be remembered by the kernel for the rest
    // of its lifetime, even after we raise the kptr_restrict.
    Service::OpenAndSaveStaticKallsymsFd();

    am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
    am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
    am.QueueEventTrigger("early-init");
    am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");

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

    // Restore prio before main loop
    setpriority(PRIO_PROCESS, 0, 0);
    while (true) {
        // By default, sleep until something happens. Do not convert far_future into
        // std::chrono::milliseconds because that would trigger an overflow. The unit of boot_clock
        // is 1ns.
        const boot_clock::time_point far_future = boot_clock::time_point::max();
        boot_clock::time_point next_action_time = far_future;

        auto shutdown_command = shutdown_state.CheckShutdown();
        if (shutdown_command) {
            LOG(INFO) << "Got shutdown_command '" << *shutdown_command
                      << "' Calling HandlePowerctlMessage()";
            HandlePowerctlMessage(*shutdown_command);
        }

        if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
            am.ExecuteOneCommand();
            // If there's more work to do, wake up again immediately.
            if (am.HasMoreCommands()) {
                next_action_time = boot_clock::now();
            }
        }
        // Since the above code examined pending actions, no new actions must be
        // queued by the code between this line and the Epoll::Wait() call below
        // without calling WakeMainInitThread().
        if (!IsShuttingDown()) {
            auto next_process_action_time = HandleProcessActions(); // 2

            // If there's a process that needs restarting, wake up in time for that.
            if (next_process_action_time) {
                next_action_time = std::min(next_action_time, *next_process_action_time);
            }
        }

        std::optional<std::chrono::milliseconds> epoll_timeout;
        if (next_action_time != far_future) {
            epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
                    std::max(next_action_time - boot_clock::now(), 0ns));
        }
        auto epoll_result = epoll.Wait(epoll_timeout);
        if (!epoll_result.ok()) {
            LOG(ERROR) << epoll_result.error();
        }
        if (!IsShuttingDown()) {
            HandleControlMessages();
            SetUsbController();
        }
    }

    return 0;
}

static std::optional<boot_clock::time_point> HandleProcessActions() {
    std::optional<boot_clock::time_point> next_process_action_time;
    for (const auto& s : ServiceList::GetInstance()) {
        if ((s->flags() & SVC_RUNNING) && s->timeout_period()) {
            auto timeout_time = s->time_started() + *s->timeout_period();
            if (boot_clock::now() > timeout_time) {
                s->Timeout();
            } else {
                if (!next_process_action_time || timeout_time < *next_process_action_time) {
                    next_process_action_time = timeout_time;
                }
            }
        }

        if (!(s->flags() & SVC_RESTARTING)) continue;

        auto restart_time = s->time_started() + s->restart_period();
        if (boot_clock::now() > restart_time) {
            if (auto result = s->Start(); !result.ok()) {
                LOG(ERROR) << "Could not restart process '" << s->name() << "': " << result.error();
            } // 3
        } else {
            if (!next_process_action_time || restart_time < *next_process_action_time) {
                next_process_action_time = restart_time;
            }
        }
    }
    return next_process_action_time;
}

system/core/init/service.cpp

Result<void> Service::Start() {
    auto reboot_on_failure = make_scope_guard([this] {
        if (on_failure_reboot_target_) {
            trigger_shutdown(*on_failure_reboot_target_);
        }
    });

    if (is_updatable() && !IsDefaultMountNamespaceReady()) {
        ServiceList::GetInstance().DelayService(*this);
        return Error() << "Cannot start an updatable service '" << name_
                       << "' before configs from APEXes are all loaded. "
                       << "Queued for execution.";
    }

    bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET));
    ResetFlagsForStart();

    // Running processes require no additional work --- if they're in the
    // process of exiting, we've ensured that they will immediately restart
    // on exit, unless they are ONESHOT. For ONESHOT service, if it's in
    // stopping status, we just set SVC_RESTART flag so it will get restarted
    // in Reap().
    if (flags_ & SVC_RUNNING) {
        if ((flags_ & SVC_ONESHOT) && disabled) {
            flags_ |= SVC_RESTART;
        }

        LOG(INFO) << "service '" << name_
                  << "' requested start, but it is already running (flags: " << flags_ << ")";

        // It is not an error to try to start a service that is already running.
        reboot_on_failure.Disable();
        return {};
    }

    // cgroups_activated is used for communication from the parent to the child
    // while setsid_finished is used for communication from the child process to
    // the parent process. These two communication channels are separate because
    // combining these into a single communication channel would introduce a
    // race between the Write() calls by the parent and by the child.
    InterprocessFifo cgroups_activated, setsid_finished;
    OR_RETURN(cgroups_activated.Initialize());
    OR_RETURN(setsid_finished.Initialize());

    if (Result<void> result = CheckConsole(); !result.ok()) {
        return result;
    }

    struct stat sb;
    if (stat(args_[0].c_str(), &sb) == -1) {
        flags_ |= SVC_DISABLED;
        return ErrnoError() << "Cannot find '" << args_[0] << "'";
    }

    std::string scon;
    if (!seclabel_.empty()) {
        scon = seclabel_;
    } else {
        auto result = ComputeContextFromExecutable(args_[0]);
        if (!result.ok()) {
            return result.error();
        }
        scon = *result;
    }

    if (!mount_namespace_.has_value()) {
        // remember from which mount namespace the service should start
        SetMountNamespace();
    }

    LOG(INFO) << "starting service '" << name_ << "'...";

    std::vector<Descriptor> descriptors;
    for (const auto& socket : sockets_) {
        if (auto result = socket.Create(scon); result.ok()) {
            descriptors.emplace_back(std::move(*result));
        } else {
            LOG(INFO) << "Could not create socket '" << socket.name << "': " << result.error();
        }
    }

    for (const auto& file : files_) {
        if (auto result = file.Create(); result.ok()) {
            descriptors.emplace_back(std::move(*result));
        } else {
            LOG(INFO) << "Could not open file '" << file.name << "': " << result.error();
        }
    }

    if (shared_kallsyms_file_) {
        if (auto result = CreateSharedKallsymsFd(); result.ok()) {
            descriptors.emplace_back(std::move(*result));
        } else {
            LOG(INFO) << "Could not obtain a copy of /proc/kallsyms: " << result.error();
        }
    }

    pid_t pid = -1;
    if (namespaces_.flags) {
        pid = clone(nullptr, nullptr, namespaces_.flags | SIGCHLD, nullptr);
    } else {
        pid = fork();
    }

    if (pid == 0) { // 父进程 init, 子进程 pid = 0 
        umask(077);
        cgroups_activated.CloseWriteFd();
        setsid_finished.CloseReadFd();
        RunService(descriptors, std::move(cgroups_activated), std::move(setsid_finished)); // 1
        _exit(127);
    } else {
        cgroups_activated.CloseReadFd();
        setsid_finished.CloseWriteFd();
    }

    if (pid < 0) {
        pid_ = 0;
        return ErrnoError() << "Failed to fork";
    }

    once_environment_vars_.clear();

    if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {
        std::string oom_str = std::to_string(oom_score_adjust_);
        std::string oom_file = StringPrintf("/proc/%d/oom_score_adj", pid);
        if (!WriteStringToFile(oom_str, oom_file)) {
            PLOG(ERROR) << "couldn't write oom_score_adj";
        }
    }

    time_started_ = boot_clock::now();
    pid_ = pid;
    flags_ |= SVC_RUNNING;
    start_order_ = next_start_order_++;
    process_cgroup_empty_ = false;

    if (CgroupsAvailable()) {
        bool use_memcg = swappiness_ != -1 || soft_limit_in_bytes_ != -1 || limit_in_bytes_ != -1 ||
                         limit_percent_ != -1 || !limit_property_.empty();
        errno = -createProcessGroup(uid(), pid_, use_memcg);
        if (errno != 0) {
            Result<void> result = cgroups_activated.Write(kActivatingCgroupsFailed);
            if (!result.ok()) {
                return Error() << "Sending notification failed: " << result.error();
            }
            return Error() << "createProcessGroup(" << uid() << ", " << pid_ << ", " << use_memcg
                           << ") failed for service '" << name_ << "': " << strerror(errno);
        }

        // When the blkio controller is mounted in the v1 hierarchy, NormalIoPriority is
        // the default (/dev/blkio). When the blkio controller is mounted in the v2 hierarchy, the
        // NormalIoPriority profile has to be applied explicitly.
        SetProcessProfiles(uid(), pid_, {"NormalIoPriority"});

        if (use_memcg) {
            ConfigureMemcg();
        }
    }

    if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {
        LmkdRegister(name_, uid(), pid_, oom_score_adjust_);
    }

    if (Result<void> result = cgroups_activated.Write(kCgroupsActivated); !result.ok()) {
        return Error() << "Sending cgroups activated notification failed: " << result.error();
    }

    cgroups_activated.Close();

    // Call setpgid() from the parent process to make sure that this call has
    // finished before the parent process calls kill(-pgid, ...).
    if (!RequiresConsole(proc_attr_)) {
        if (setpgid(pid, pid) < 0) {
            switch (errno) {
                case EACCES:  // Child has already performed setpgid() followed by execve().
                case ESRCH:   // Child process no longer exists.
                    break;
                default:
                    PLOG(ERROR) << "setpgid() from parent failed";
            }
        }
    } else {
        // The Read() call below will return an error if the child is killed.
        if (Result<uint8_t> result = setsid_finished.Read();
            !result.ok() || *result != kSetSidFinished) {
            if (!result.ok()) {
                return Error() << "Waiting for setsid() failed: " << result.error();
            } else {
                return Error() << "Waiting for setsid() failed: " << static_cast<uint32_t>(*result)
                               << " <> " << static_cast<uint32_t>(kSetSidFinished);
            }
        }
    }

    setsid_finished.Close();

    NotifyStateChange("running");
    reboot_on_failure.Disable();

    LOG(INFO) << "... started service '" << name_ << "' has pid " << pid_;

    return {};
}

fork是一个系统调用。

/* Clone the calling process, creating an exact copy.
   Return -1 for errors, 0 to the new process,
   and the process ID of the new process to the old process.  */
extern __pid_t fork (void) __THROWNL;

解释下就是,从父进程克隆一个子进程。
在父进程中fork返回值为子进程的id, 在子进程中fork返回值为0.

int main(int __argc, char** __argv){
    const auto pid = fork();
    std::cout << "pid: " << pid << "\n";
    if(pid == 0){
        std::cout << "我是子进程, pid: " << getpid() << ", ppid: " << getppid() << "\n";
    } else {
        std::cout << "我是父进程, pid: " << getpid() << ", ppid: " << getppid() << "\n";
    }
    return 0;
}
/*
执行结果
pid: 2396
我是父进程, pid: 2394, ppid: 2355
pid: 0
我是子进程, pid: 2396, ppid: 2394
*/

进入子进程分支,即代码1处转而拉起系统服务,不会继续向下执行。

system/core/init/service.cpp

// Enters namespaces, sets environment variables, writes PID files and runs the service executable.
void Service::RunService(const std::vector<Descriptor>& descriptors,
                         InterprocessFifo cgroups_activated, InterprocessFifo setsid_finished) {
    if (auto result = EnterNamespaces(namespaces_, name_, mount_namespace_); !result.ok()) {
        LOG(FATAL) << "Service '" << name_ << "' failed to set up namespaces: " << result.error();
    }

    for (const auto& [key, value] : once_environment_vars_) {
        setenv(key.c_str(), value.c_str(), 1);
    }
    for (const auto& [key, value] : environment_vars_) {
        setenv(key.c_str(), value.c_str(), 1);
    }

    for (const auto& descriptor : descriptors) {
        descriptor.Publish();
    }

    if (auto result = WritePidToFiles(&writepid_files_); !result.ok()) {
        LOG(ERROR) << "failed to write pid to files: " << result.error();
    }

    // Wait until the cgroups have been created and until the cgroup controllers have been
    // activated.
    Result<uint8_t> byte = cgroups_activated.Read();
    if (!byte.ok()) {
        LOG(ERROR) << name_ << ": failed to read from notification channel: " << byte.error();
    }
    cgroups_activated.Close();
    if (*byte != kCgroupsActivated) {
        LOG(FATAL) << "Service '" << name_  << "' failed to start due to a fatal error";
        _exit(EXIT_FAILURE);
    }

    if (task_profiles_.size() > 0) {
        bool succeeded = SelinuxGetVendorAndroidVersion() < __ANDROID_API_U__
                                 ?
                                 // Compatibility mode: apply the task profiles to the current
                                 // thread.
                                 SetTaskProfiles(getpid(), task_profiles_)
                                 :
                                 // Apply the task profiles to the current process.
                                 SetProcessProfiles(getuid(), getpid(), task_profiles_);
        if (!succeeded) {
            LOG(ERROR) << "failed to set task profiles";
        }
    }

    // As requested, set our gid, supplemental gids, uid, context, and
    // priority. Aborts on failure.
    SetProcessAttributesAndCaps(std::move(setsid_finished));

    if (!ExpandArgsAndExecv(args_, sigstop_)) { // 1
        PLOG(ERROR) << "cannot execv('" << args_[0]
                    << "'). See the 'Debugging init' section of init's README.md for tips";
    }
}
static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigstop) {
    std::vector<std::string> expanded_args;
    std::vector<char*> c_strings;

    expanded_args.resize(args.size());
    c_strings.push_back(const_cast<char*>(args[0].data()));
    for (std::size_t i = 1; i < args.size(); ++i) {
        auto expanded_arg = ExpandProps(args[i]);
        if (!expanded_arg.ok()) {
            LOG(FATAL) << args[0] << ": cannot expand arguments': " << expanded_arg.error();
        }
        expanded_args[i] = *expanded_arg;
        c_strings.push_back(expanded_args[i].data());
    }
    c_strings.push_back(nullptr);

    if (sigstop) {
        kill(getpid(), SIGSTOP);
    }

    return execv(c_strings[0], c_strings.data()) == 0;
}

execv() 是一个 Linux 系统调用,用一个新的可执行程序替换当前进程映像。
根据上述代码写出如下execv系统调用演示

#include "learn001.hpp"


const char* const program = "/home/zsh/cppProjects/epoll_learn01/build/epoll_learn01";

void hello00(){
    std::cout << "before Hello hello00\n";
    const char * const args[] = {program, "hello01", nullptr};
    const auto result = execv(program, const_cast<char**>(args));
    std::cout << "after Hello hello00 execv result: " << result << "\n";
}


void hello01(){
    std::cout << "before Hello hello01\n";
    const char * const args[] = {program, "hello02", nullptr};
    const auto result = execv(program, const_cast<char**>(args));
    std::cout << "after Hello hello01 execv result: " << result << "\n";
}

void hello02(){
    std::cout << "before Hello hello02\n";
    const char * const args[] = {program, "hello03", nullptr};
    const auto result = execv(program, const_cast<char**>(args));
    std::cout << "after Hello hello02 execv result: " << result << "\n";
}

void hello03(){
    std::cout << "Hello hello03\n";
}

int main(int __argc, char** __argv){
    if(__argc <= 1){
        hello00();
    } else if(strcmp(__argv[1], "hello01") == 0){
        hello01();
    } else if(strcmp(__argv[1], "hello02") == 0){
        hello02();
    } else {
        hello03();
    }
    return 0;
}

你会发现所以execv之后的代码根本不会执行。在同一个进程,替换当前进程的代码段、数据段、堆栈等。

posted @ 2025-10-20 22:23  爱情丶眨眼而去  阅读(5)  评论(0)    收藏  举报