在Android系统启动时,会创建两个进程init和kthreadd。它们是后续所有进程的祖先,其中init进程主要负责完成系统的初始化工作,挂载文件、创建目录、配置信息等,它管理和创建用户空间层的所有进程,进程号为1。而kthreadd进程是对内核进程的管理,所有内核进程都是由kthreadd直接或间接创建的,它负责管理内核的生命周期等,进程号为2。
可以通过adb shell ps -ef查看进程信息。
1 2 3 4
| PS C:\Users\feng\Desktop> adb shell ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 06:36:23 ? 00:00:06 init second_stage root 2 0 0 06:36:23 ? 00:00:00 [kthreadd]
|
Init进程
本文基于Android13源码
init进程是在内核启动时,由内核中kernal_init方法通过try_init_process启动的。实际上它的代码主入口在system/core/init/main.cpp中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| int main(int argc, char** argv) { 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); }
|
ueventd
在主入口中,分为多个逻辑,其中当argv[0]为ueventd时,即可执行文件的名字为ueventd时,会通过ueventd_main方法启动ueventd服务,它主要是负责挂载和创建设备节点的。由内核启动时,执行的是/init,因此不会走到这里,它实际上被定义在rc文件中启动的,system/core/rootdir/init.rc。
1 2 3 4 5
| service ueventd /system/bin/ueventd class core critical seclabel u:r:ueventd:s0 shutdown critical
|
ueventd服务是通过rc文件启动的,因此它实际上的执行顺序会比较靠后,因为在init的第二阶段才会去解析rc文件。可以查看它的可执行文件,只是一个软链接,实际上仍指向init可执行文件,因此才会有上面的逻辑。
1 2
| PS C:\Users\feng\Desktop> adb shell ls -l /system/bin/ueventd lrwxr-xr-x 1 root shell 4 2022-06-03 22:32 /system/bin/ueventd -> init
|
first_stage
再由内核启动init的时候,实际上参数都不会匹配上述的判断逻辑,因此在首次执行init的时候,走的是第一阶段FirstStageMain。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
int FirstStageMain(int argc, char** argv) { ... CHECKCALL(clearenv()); CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1)); 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)); CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC))); ...
const char* path = "/system/bin/init"; const char* args[] = {path, "selinux_setup", nullptr}; ... execv(path, const_cast<char**>(args)); return 1; }
|
第一阶段的启动,主要是负责挂载各个目录,以及创建对应的文件可目录等,然后就是初始化内核日志等操作。当第一阶段的逻辑执行完成后,会再次执行init,并且传入selinux_setup参数,因此后续会进入到SetupSelinux中。
selinux_setup
第一阶段创建和挂载目录之后,就会进入下一阶段,该阶段就是SELinux的配置阶段。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
int SetupSelinux(char** argv) { SetStdioToDevNull(argv); InitKernelLogging(argv); MountMissingSystemPartitions(); SelinuxSetupKernelLogging(); PrepareApexSepolicy(); ReadPolicy(&policy); CleanupApexSepolicy(); auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded(); LoadSelinuxPolicy(policy); SelinuxSetEnforcement(); setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);
const char* path = "/system/bin/init"; const char* args[] = {path, "second_stage", nullptr}; execv(path, const_cast<char**>(args)); return 1; }
|
这里我们也不用过多深究,它主要就是负责加载SELinux的策划信息等,当所有逻辑执行结束后,会再次执行init来进入到下一阶段SecondStageMain。
second_stage
第二阶段可以说是init进程的最后一个阶段了,它会解析rc文件并创建对应的服务进程,并且会一直存在于系统中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| int SecondStageMain(int argc, char** argv) { 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(); } PropertyInit();
Epoll epoll; InstallSignalFdHandler(&epoll); InstallInitNotifier(&epoll); StartPropertyService(&property_fd);
ActionManager& am = ActionManager::GetInstance(); ServiceList& sm = ServiceList::GetInstance(); LoadBootScripts(am, sm); am.QueueEventTrigger("early-init"); am.QueueEventTrigger("init"); std::string bootmode = GetProperty("ro.bootmode", ""); if (bootmode == "charger") { am.QueueEventTrigger("charger"); } else { am.QueueEventTrigger("late-init"); } while (true) { auto pending_functions = epoll.Wait(epoll_timeout); ... }
return 0; }
|
我们重点关注第二阶段的初始化,因为这一阶段才算的上是Android的启动。
初始化属性服务
在第二阶段会初始化属性服务,也就是系统属性。它可以包含系统属性也可以用于应用设置属性,有点类似于系统环境变量,很多配置都是通过属性服务来设置的。在第二阶段的初始化中,会通过PropertyInit方法进入初始化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
void PropertyInit() { selinux_callback cb; cb.func_audit = PropertyAuditCallback; selinux_set_callback(SELINUX_CB_AUDIT, cb);
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH); CreateSerializedPropertyInfo(); if (__system_property_area_init()) { LOG(FATAL) << "Failed to initialize property area"; } if (!property_info_area.LoadDefaultPath()) { LOG(FATAL) << "Failed to load serialized property info file"; }
ProcessKernelDt(); ProcessKernelCmdline(); ProcessBootconfig();
ExportKernelBootProps(); PropertyLoadBootDefaults(); }
|
注册epoll监听
epoll机制是Linux中用来监听文件变化的一个机制,他可以在文件不可读时阻塞并释放cpu,然后在文件可读时唤醒,具体可以查看Handler唤起的基础–eventfd和epoll。
InstallSignalFdHandler
该方法主要就是注册信号量的epoll,因为init是所有用户进程的祖先进程,因此需要注册信号量来接收子孙进程发送的信号,来处理相关的回收等逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13
|
static void InstallSignalFdHandler(Epoll* epoll) { ... signal_fd = signalfd(-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK); constexpr int flags = EPOLLIN | EPOLLPRI; auto handler = std::bind(HandleSignalFd, false); if (auto result = epoll->RegisterHandler(signal_fd, handler, flags); !result.ok()) { LOG(FATAL) << result.error(); } }
|
InstallInitNotifier
该方法注册了一个eventfd,该文件描述符用于唤醒init进程,因为在系统启动后,init进程是一直常驻的,但肯定不会是一直活跃的,因此会通过epoll机制进行阻塞,如果想要唤醒init进程,只需要通过wake_main_thread_fd描述符写入内容即可唤醒。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| static int wake_main_thread_fd = -1; static void InstallInitNotifier(Epoll* epoll) { wake_main_thread_fd = eventfd(0, EFD_CLOEXEC); if (wake_main_thread_fd == -1) { PLOG(FATAL) << "Failed to create eventfd for waking init"; } auto clear_eventfd = [] { uint64_t counter; TEMP_FAILURE_RETRY(read(wake_main_thread_fd, &counter, sizeof(counter))); }; if (auto result = epoll->RegisterHandler(wake_main_thread_fd, clear_eventfd); !result.ok()) { LOG(FATAL) << result.error(); } }
|
StartPropertyService
为系统属性创建一个socket,当设置系统属性时,实际上就是通过该socket来处理并写入到对应的文件中。这样做是因为有些系统功能是需要一直监听系统属性的,当系统属性变化时会做出响应,通过ctl.开头的属性是会被直接响应的,如:SetProperty("ctl.start", "logd")实际上会直接通过start命令启动logd服务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
void StartPropertyService(int* ) { if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, false, false, 0666, 0, 0, {}); result.ok()) { property_set_fd = *result; } else { LOG(FATAL) << "start_property_service socket creation failed: " << result.error(); } listen(property_set_fd, 8); auto new_thread = std::thread{PropertyServiceThread}; property_service_thread.swap(new_thread); }
static void PropertyServiceThread() { Epoll epoll; if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd); !result.ok()) { LOG(FATAL) << result.error(); } if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result.ok()) { LOG(FATAL) << result.error(); } while (true) { auto pending_functions = epoll.Wait(std::nullopt); ... } }
|
解析rc文件
这部分是解析rc文件的,基本上所有的系统服务都是通过rc文件来定义的,然后在系统启动时会由init进程解析并启动。它主要的逻辑就是加载rc文件,然后通过触发器来决定是否启动对应的服务等。具体的rc文件规则,可以查看Android Init Language。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) { Parser parser = CreateParser(action_manager, service_list); std::string bootscript = GetProperty("ro.boot.init_rc", ""); if (bootscript.empty()) { parser.ParseConfig("/system/etc/init/hw/init.rc"); if (!parser.ParseConfig("/system/etc/init")) { late_import_paths.emplace_back("/system/etc/init"); } parser.ParseConfig("/system_ext/etc/init"); if (!parser.ParseConfig("/vendor/etc/init")) { late_import_paths.emplace_back("/vendor/etc/init"); } if (!parser.ParseConfig("/odm/etc/init")) { late_import_paths.emplace_back("/odm/etc/init"); } if (!parser.ParseConfig("/product/etc/init")) { late_import_paths.emplace_back("/product/etc/init"); } } else { parser.ParseConfig(bootscript); } }
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) { Parser parser;
parser.AddSectionParser("service", std::make_unique<ServiceParser>( &service_list, GetSubcontext(), std::nullopt)); parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext())); parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
return parser; }
|
总结
到这里init进程就分析完了,过程比较简陋,只关注了主要的逻辑。首先init进程分为三个阶段:第一阶段,SELinux阶段,第二阶段。其中第一阶段挂载和创建对应的目录,SELinux阶段用于初始化SELinux,第二阶段初始化系统属性已经加载rc文件并启动对应的系统服务。