当前位置: 首页 > news >正文

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 latersetpriority(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/223076262boot_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 initMountExtraFilesystems();// 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 monitorif (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 loopsetpriority(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 startSetMountNamespace();}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 {};
}

进入子进程分支,即代码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_)) { // 1PLOG(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 系统调用,用一个新的可执行程序替换当前进程映像。

http://www.hskmm.com/?act=detail&tid=35344

相关文章:

  • 分层图
  • 10.20总结
  • 学习相关
  • 题解:Luogu P10644 [NordicOI 2022] 能源网格 Power Grid
  • 题解:Luogu P10004 [集训队互测 2023] Permutation Counting 2
  • 题解:Luogu P2075 区间 LIS
  • 英语_阅读_2050 Space tourism_待读
  • goframe框架命令行工具gf在zsh下不能用
  • 题解:Luogu P4143 采集矿石
  • 从18w到1600w播放量,我的一点思考。
  • 扣一个细节问题
  • 10.20java作业
  • 题解:Luogu P14175 【MX-X23-T5】向死存魏
  • 题解:Luogu P14254 分割(divide)
  • 题解:Luogu P6898 [ICPC 2014 WF] Metal Processing Plant
  • 20251020
  • 32-腾讯IM接入资料和定价
  • 题解:AtCoder ARC207A Affinity for Artifacts
  • 构造单
  • [笔记]高斯消元
  • 半导体设备各细分领域的国内外龙头公司
  • CSP-S 34
  • 02.Python百行代码实现抽奖系统
  • CSP-S 35
  • CSP-S 29
  • 2025网络安全振兴杯wp
  • 10.20每日总结
  • CSP-S 31
  • 后缀树
  • ES原理、zookeeper、kafka