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之后的代码根本不会执行。在同一个进程,替换当前进程的代码段、数据段、堆栈等。

浙公网安备 33010602011771号