init进程是Android用户空间的第一个进程,进程号为1,是Android系统启动流程中一个关键步骤,作为第一个进程,被赋予了许多极其重要的工作职责,比如创建Zygote(孵化器)和属性服务等。init进程是由多个源文件共同组成的,这些文件位于源码目录system/core/init中。
总的来说主要做了以下三件事:
1.创建和挂载启动所需的文件目录。
2.初始化和启动属性服务。
3.解析init.rc配置文件并启动Zygote进程。
一、kernel启动init进程
bootloader完成初始化工作后,会载入 /boot 目录下面的 kernel,此时控制权转交给操作系统。操作系统将要完成的存储管理、设备管理、文件管理、进程管理、加载驱动等任务的初始化工作,以便进入用户态。
内核启动完成后,将会寻找init文件(init文件位于Android11/linux/linux-4.14/init/main.c),启动init进程,也就是android的第一个进程。
Android11/linux/linux-4.14/init/main.c\中的kernel_init方法。可以看到,在kernel_init,会调用kernel_init_freeable,将全局变量ramdisk_execute_command设为"/init",所以会启动/init。
在机器根目录用:ls -l 查看得:
lrwxr-x— 1 root shell 16 2008-12-31 20:00 init -> /system/bin/init
init是链接到/system/bin/init的, 所以说kernel最终启动的就是/system/bin/init
二、init入口
源码路径位于Android11/system/core/init,入口函数是main.cpp。
int main(int argc, char** argv) 第一个参数argc表示参数个数,第二个参数是参数列表,也就是具体的参数。
main函数有四个参数入口:
一是参数中有ueventd,进入ueventd_main
二是参数中有subcontext,进入InitLogging 和SubcontextMain
三是参数中有selinux_setup,进入SetupSelinux
四是参数中有second_stage,进入SecondStageMain
第一步、启动时并没有带任何参数,所在最先执行的就是FirstStageMain(argc, argv);
经过一系列操作,主要是创建目录、挂载分区等,最后进入第二次运行init,可以看到这次是带了selinux_setup参数。
第二步:进入SetupSelinux(argv),这个函数实现在system/core/init/selinux.cpp中,只看主要代码段
主要就是启动SElinux。
第三步、SecondStageMain(argc, argv);函数实现是在system/core/init/init.cpp中
初始化property系统,解析init.rc,然后添加一些任务。
PS:
最开始的ueventd_main(argc, argv);的启动,在init.rc中:
三、init调用时序图:
注:以下源文件均在Android11/system/core/init下。
在系统启动过程中会多次调用 execv(),如:FirstStageMain每次调用该方法时会重新执行 main() 方法。该方法如下:
int execv(const char* name, char* const* argv) { return execve(name,
argv, environ); }
execv() 会停止执行当前的进程,并且以 progname 应用进程替换被停止执行的进程,进程 ID 不会改变。 init.cpp
进程的入口函数 main
四、SecondStageMain做了啥
int SecondStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
boot_clock::time_point start_time = boot_clock::now();
trigger_shutdown = [](const std::string& command) {
shutdown_state.TriggerShutdown(command); };
SetStdioToDevNull(argv);
InitKernelLogging(argv);// 初始化 kernel Log
LOG(INFO) << "init second stage started!";
// 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()) {