Android 12 init 启动流程分析
Posted pecuyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 12 init 启动流程分析相关的知识,希望对你有一定的参考价值。
文章托管在gitee上 Android Notes , 同步csdn
本文基于android12 分析
概述
init是 Android 启动的第一个用户空间进程,它的地位非常重要,它fork产生系统的一些关键进程(如zygote,surfaceflinger进程),而zygote进一步fork产生system_server和其他应用进程,通过这套逻辑构建了Android的进程层次结构体系。init进程的功能包含但不限于以下:
- 挂载系统分区和加载一些内核模块
- 加载sepolicy 及使能 selinux
- 支持属性服务
- 启动脚本rc文件解析
- 执行事件触发器和属性改变的事件
- 子进程死亡监听,回收僵尸进程
- 非oneshot服务保活
通过ps命令看看init进程信息
# ps -A|grep init
root 1 0 10847128 4020 do_epoll_wait 0 S init # 这个是 init 进程
root 166 1 10817360 1916 do_sys_poll 0 S init # 这个是 subcontext 进程
在启动内核的start_kernel函数流程中,会调用run_init_process函数执行init程序,来启动init进程
run_init_process
在Android中执行的init是/init
/// @kernel_common/init/main.c
static int run_init_process(const char *init_filename)
argv_init[0] = init_filename;
pr_info("Run %s as init process\\n", init_filename);
return do_execve(getname_kernel(init_filename),
(const char __user *const __user *)argv_init,
(const char __user *const __user *)envp_init);
/init 实际上是一个软链接,指向的是/system/bin/init
# ls /init -lZ
lrwxr-x--- 1 root shell u:object_r:init_exec:s0 16 2021-12-20 15:52 /init -> /system/bin/init
接下来,进入init的main函数。
main
main执行分为几个阶段:
- FirstStage 挂载一些基础文件系统和加载内核模块等
- selinux_setup 执行selinux的初始化
- SecondStage 挂载其他文件系统,启动属性服务,执行boot流程等,主要逻辑都在这里实现
/// @system/core/init/main.cpp
int main(int argc, char** argv)
#if __has_feature(address_sanitizer)
__asan_set_error_report_callback(AsanReportCallback);
#endif
// Boost prio which will be restored later
setpriority(PRIO_PROCESS, 0, -20);
if (!strcmp(basename(argv[0]), "ueventd")) // 处理uventd启动,共用一个main
return ueventd_main(argc, argv);
if (argc > 1)
if (!strcmp(argv[1], "subcontext")) // subcontext 子进程入口,用于执行来自init的某些任务
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
return SubcontextMain(argc, argv, &function_map);
if (!strcmp(argv[1], "selinux_setup")) // selinux初始化阶段
return SetupSelinux(argv);
if (!strcmp(argv[1], "second_stage")) // 启动第二阶段
return SecondStageMain(argc, argv);
return FirstStageMain(argc, argv); // 启动第一阶段
FirstStageMain
第一阶段初始化
/// @system/core/init/first_stage_init.cpp
int FirstStageMain(int argc, char** argv)
if (REBOOT_BOOTLOADER_ON_PANIC) // 设置panic处理器
InstallRebootSignalHandlers();
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(mkdir("/dev/dm-user", 0755));
CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
#define MAKE_STR(x) __STRING(x)
// /proc 伪文件系统,记录进程、线程相关实时状态
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)); // 只读
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);
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)));
// 重要的在第一阶段挂载,其他可以在rc执行流程中挂载
// 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));
// /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);
// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
// talk to the outside world...
InitKernelLogging(argv); // 初始化 kernel logger
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();
auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0;
boot_clock::time_point module_start_time = boot_clock::now();
int module_count = 0;
// 加载内核模块
if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,
module_count))
if (want_console != FirstStageConsoleParam::DISABLED)
LOG(ERROR) << "Failed to load kernel modules, starting console";
else
LOG(FATAL) << "Failed to load kernel modules";
if (module_count > 0)
auto module_elapse_time = std::chrono::duration_cast<std::chrono::milliseconds>(
boot_clock::now() - module_start_time);
setenv(kEnvInitModuleDurationMs, std::to_string(module_elapse_time.count()).c_str(), 1);
LOG(INFO) << "Loaded " << module_count << " kernel modules took "
<< module_elapse_time.count() << " ms";
bool created_devices = false;
if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE)
if (!IsRecoveryMode())
created_devices = DoCreateDevices();
if (!created_devices)
LOG(ERROR) << "Failed to create device nodes early";
StartConsole(cmdline);
// 拷贝prop,Copied ramdisk prop to /second_stage_resources/system/etc/ramdisk/build.prop
if (access(kBootImageRamdiskProp, F_OK) == 0)
std::string dest = GetRamdiskPropForSecondStage();
std::string dir = android::base::Dirname(dest);
std::error_code ec;
if (!fs::create_directories(dir, ec) && !!ec)
LOG(FATAL) << "Can't mkdir " << dir << ": " << ec.message();
if (!fs::copy_file(kBootImageRamdiskProp, dest, ec))
LOG(FATAL) << "Can't copy " << kBootImageRamdiskProp << " to " << dest << ": "
<< ec.message();
LOG(INFO) << "Copied ramdisk prop to " << dest;
// If "/force_debuggable" 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)
constexpr const char adb_debug_prop_src[] = "/adb_debug.prop";
constexpr const char userdebug_plat_sepolicy_cil_src[] = "/userdebug_plat_sepolicy.cil";
std::error_code ec; // to invoke the overloaded copy_file() that won't throw.
if (access(adb_debug_prop_src, F_OK) == 0 &&
!fs::copy_file(adb_debug_prop_src, kDebugRamdiskProp, ec))
LOG(WARNING) << "Can't copy " << adb_debug_prop_src << " to " << kDebugRamdiskProp
<< ": " << ec.message();
if (access(userdebug_plat_sepolicy_cil_src, F_OK) == 0 &&
!fs::copy_file(userdebug_plat_sepolicy_cil_src, kDebugRamdiskSEPolicy, ec))
LOG(WARNING) << "Can't copy " << userdebug_plat_sepolicy_cil_src << " to "
<< kDebugRamdiskSEPolicy << ": " << ec.message();
// setenv for second-stage init to read above kDebugRamdisk* files.
setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
if (ForceNormalBoot(cmdline, bootconfig))
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");
// Mounts partitions specified by fstab in device tree.
// 通过fstab的配置挂载,在Android中通常在 /system/etc/fstab.xxx ,/vendor/etc/fstab.xxx 等地方
if (!DoFirstStageMount(!created_devices)) // 挂载一些必要分区,如/system
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();
setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),
1);
/// 再次执行init,此处传入了 selinux_setup 参数,会进入 selinux初始化阶段
/// 从init main 方法可知,此过程调用 SetupSelinux
const char* path = "/system/bin/init";
const char* args[] = path, "selinux_setup", nullptr;
auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
d以上是关于Android 12 init 启动流程分析的主要内容,如果未能解决你的问题,请参考以下文章