温馨提示:这篇文章已超过458天没有更新,请注意相关的内容是否还可用!
摘要:安卓开机启动流程包括多个阶段,从按下电源按钮开始,系统启动引导程序,加载内核,初始化硬件,运行init进程等。接着系统加载启动项,启动系统服务,如启动系统服务器等。启动用户应用程序界面,完成系统启动过程。整个过程涉及多个组件和步骤,确保手机能够正常启动并运行各种应用程序和服务。
目录
- 一、整体框架
- 二、流程+代码分析
- 2.1 Boot ROM
- 2.2 Boot Loader
- 2.3 Kernel层
- Kernel代码部分
- 2.4 Init进程
- Init进程代码部分
- 2.5 zygote进程
- zygote代码部分
- 2.6 SystemServer进程
- SystemServer代码部分
- 2.7 启动Launcher与SystemUI
- 三、SystemServices
- 3.1 引导服务
- 3.2 核心服务
- 3.3 其他服务
- 四、总结
一、整体框架
二、流程+代码分析
按照开机流程,重点分析安卓init进程及之后上层部分的代码
2.1 Boot ROM
BOOT ROM :当手机处于关机状态时,长按Power键开机,引导芯片开始从固化在ROM里的预设出代码开始执行,然后加载引导程序到RAM;
ROM是存储在设备芯片中的只读存储器(ROM),负责在设备上电后最先运行的引导程序。Boot ROM的主要作用是初始化硬件设备(如内存、CPU、外设等),加载并执行Boot Loader。Boot ROM通常是设备制造商预先写入芯片中的固化程序,用于引导设备启动。
2.2 Boot Loader
Boot Loader:这是启动Android系统之前的引导程序,主要是检查RAM,初始化硬件参数等功能。
Boot Loader是位于设备存储器中的引导加载程序,负责在Boot ROM之后被加载和执行。Boot Loader的主要任务包括:
初始化设备硬件,如内存管理、外设初始化等。
加载Linux Kernel到内存中,并启动Linux Kernel。
提供启动选项和引导参数的设置。
启动Linux Kernel后,Boot Loader的任务就完成了,控制权交给Linux Kernel。
2.3 Kernel层
Kernel是指Android内核层,到这里才刚刚开始进入Android系统。
swapper进程(pid=0):
启动Kernel的swapper进程(pid=0):该进程又称为idle进程, 系统初始化过程Kernel由无到有开创的第一个进程, 用于初始化进程管理、内存管理,加载Display,Camera Driver,Binder Driver等相关工作;
kthreadd进程(pid=2):
启动kthreadd进程(pid=2):是Linux系统的内核进程,会创建内核工作线程kworkder,软中断线程ksoftirqd,thermal等内核守护进程。kthreadd进程是所有内核进程的鼻祖。
Linux Kernel是整个系统的核心部分,负责管理硬件资源、提供系统调度和内存管理等功能。Android系统开机启动流程中的Linux Kernel阶段:
- 1.加载Linux Kernel:
在Boot Loader加载完成后,Boot Loader会将Linux Kernel从存储器中加载到设备的内存中,并开始执行Linux Kernel的启动代码。
- 2.初始化阶段:
Linux Kernel启动后,首先会进行一系列初始化操作,包括初始化内核数据结构、硬件设备、内存管理等。这些初始化操作是确保系统能够正常运行的基础。
- 3.设备检测和驱动加载:
Linux Kernel会进行设备检测,识别设备硬件,并加载相应的设备驱动程序。这些设备驱动程序负责与硬件设备进行通信和控制,确保系统能够正确地访问和操作硬件设备。
Kernel代码部分
不同版本略有区别
kernel/msm-4.14/init/main.c
kernel_init开始启动init进程
asmlinkage __visible void __init start_kernel(void) { /** * *省略部分代码,涉及到内核、堆栈、进程管理等内容的初始化 * **/ /* Do the rest non-__init'ed, we're now alive */ //进入用户空间,执行后续初始化操作 rest_init(); prevent_tail_call_optimization(); } static noinline void __ref rest_init(void) { struct task_struct *tsk; int pid; rcu_scheduler_starting(); /* * We need to spawn init first so that it obtains pid 1, however * the init task will end up wanting to create kthreads, which, if * we schedule it before we create kthreadd, will OOPS. */ //通过 kernel_thread 函数创建两个内核线程 kernel_init 和 kthreadd。 //其中 kernel_init 进程用于执行 /bin/init 程序,成为用户空间的第一个进程(PID 为 1),而 kthreadd 则是系统中所有内核线程的父进程。 //kernel_thread() 是一个创建内核线程的函数,它的原型定义在 include/linux/kthread.h 头文件中 //第一个参数是一个函数指针,指向要在新线程中执行的函数。第二个参数是传递给 fn 函数的参数,如果不需要传递参数,则可以将其设置为 NULL。第三个参数用于指定新线程的行为,可以使用标志值对其进行设置。 //kernel_thread(kernel_init, NULL, CLONE_FS) 的作用是创建一个内核线程,并在其中执行 kernel_init 函数。由于第二个参数为 NULL,因此 kernel_init 函数不会接收任何参数。而 CLONE_FS 标志表示新线程会继承当前进程的文件系统相关的属性,例如根目录、当前工作目录等等 pid = kernel_thread(kernel_init, NULL, CLONE_FS); /* * Pin init on the boot CPU. Task migration is not properly working * until sched_init_smp() has been run. It will set the allowed * CPUs for init to the non isolated CPUs. */ rcu_read_lock(); tsk = find_task_by_pid_ns(pid, &init_pid_ns); set_cpus_allowed_ptr(tsk, cpumask_of(smp_processor_id())); rcu_read_unlock(); numa_default_policy(); pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); rcu_read_lock(); kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns); rcu_read_unlock(); /* * Enable might_sleep() and smp_processor_id() checks. * They cannot be enabled earlier because with CONFIG_PRREMPT=y * kernel_thread() would trigger might_sleep() splats. With * CONFIG_PREEMPT_VOLUNTARY=y the init task might have scheduled * already, but it's stuck on the kthreadd_done completion. */ //system_state 是一个全局变量,表示系统的当前状态。它的取值包括以下几种: //SYSTEM_BOOTING:表示系统正在启动。 //SYSTEM_RUNNING:表示系统已经启动并正在正常运行。 //SYSTEM_HALT:表示系统已经关闭并停止了所有运行。 //SYSTEM_POWER_OFF:表示系统已经关闭,但是可能仍然有硬件在运行。 //SYSTEM_SCHEDULING:表示系统正在进行进程调度,即系统已经进入正常运行状态。 //即表示系统已经完成了启动过程,并正常地开始了进程调度。这意味着内核已经初始化完毕,可以正常地执行用户进程和系统服务。 system_state = SYSTEM_SCHEDULING; //通过complete(&kthreadd_done) 向 kthreadd 进程发出完成信号,以便 kthreadd 进程可以开始创建其它内核线程 complete(&kthreadd_done); /* * The boot idle thread must execute schedule() * at least once to get things moving: */ schedule_preempt_disabled(); /* Call into cpu_idle with preempt disabled */ cpu_startup_entry(CPUHP_ONLINE); } static int __ref kernel_init(void *unused) { /** * *省略部分代码,设置进程的状态 * **/ //设置系统状态为运行状态 system_state = SYSTEM_RUNNING; /** * *省略部分代码,设置进程的状态 * **/ //如果ramdisk_execute_command与execute_command非空,则调用run_init_process()函数尝试执行该命令,并将返回值保存在ret变量中。如果返回值为0,表示执行成功,就直接返回0表示初始化成功 if (ramdisk_execute_command) { ret = run_init_process(ramdisk_execute_command); if (!ret) return 0; pr_err("Failed to execute %s (error %d)\n", ramdisk_execute_command, ret); } /* * We try each of these until one succeeds. * * The Bourne shell can be used instead of init if we are * trying to recover a really broken machine. */ if (execute_command) { ret = run_init_process(execute_command); if (!ret) return 0; panic("Requested init %s failed (error %d).", execute_command, ret); } //会依次寻找以下目录中的可执行文件进行执行,只要一个执行成功就直接返回0,否则触发异常 //"/bin/init"是在Android系统源码编译时编译出的一个可执行程序,路径为Android设备上的"system/bin/init",而这个init程序则是由system/core/init/main.cpp文件编译生成的 if (!try_to_run_init_process("/sbin/init") || !try_to_run_init_process("/etc/init") || !try_to_run_init_process("/bin/init") || !try_to_run_init_process("/bin/sh")) return 0; panic("No working init found. Try passing init= option to kernel. " "See Linux Documentation/admin-guide/init.rst for guidance."); } static int try_to_run_init_process(const char *init_filename) { int ret; ret = run_init_process(init_filename); if (ret && ret != -ENOENT) { pr_err("Starting init: %s exists but couldn't execute it (error %d)\n", init_filename, ret); } return ret; } static int run_init_process(const char *init_filename) { argv_init[0] = init_filename; //do_execve()函数的作用是加载并执行一个新的用户程序,它接受以下参数: //filename:一个struct filename类型的指针,表示要执行的可执行文件的路径和名称。 //argv:一个以NULL结尾的字符串数组,表示要传递给新程序的命令行参数。 //envp:一个以NULL结尾的字符串数组,表示要传递给新程序的环境变量。 //函数返回一个整数值,代表执行结果。如果执行成功,函数不会返回,而是直接切换到新程序的上下文;如果执行失败,函数返回一个负值,代表错误代码。 return do_execve(getname_kernel(init_filename), (const char __user *const __user *)argv_init, (const char __user *const __user *)envp_init); }
system/core/init/Android.bp
phony { name: "init", required: [ "init_second_stage", ], } cc_binary { name: "init_second_stage", recovery_available: true, stem: "init", defaults: ["init_defaults"], static_libs: ["libinit"], required: [ "e2fsdroid", "init.rc", "mke2fs", "sload_f2fs", "make_f2fs", "ueventd.rc", ], srcs: ["main.cpp"], symlinks: ["ueventd"], target: { recovery: { cflags: ["-DRECOVERY"], exclude_shared_libs: [ "libbinder", "libutils", ], }, }, }
2.4 Init进程
init进程是linux系统中用户空间的第一个进程,进程号为1。
当bootloader启动后,启动kernel,kernel启动完后,在用户空间启动init进程,再通过init进程,来读取init.rc中的相关配置。
从而来启动其他相关进程以及其他操作。 init进程被赋予了很多重要工作,init进程启动主要分为两个阶段:
- 1.第一个阶段完成以下内容:
ueventd/watchdogd跳转及环境变量设置
挂载文件系统并创建目录
初始化日志输出、挂载分区设备
启用SELinux安全策略
开始第二阶段前的准备
- 2.第二个阶段完成以下内容:
初始化属性系统
执行SELinux第二阶段并恢复一些文件安全上下文
新建epoll并初始化子进程终止信号处理函数
设置其他系统属性并开启属性服务
Init进程代码部分
system/core/init/main.cpp
int main(int argc, char** argv) { #if __has_feature(address_sanitizer) __asan_set_error_report_callback(AsanReportCallback); #endif 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")) { //第二次通过first_stage_init执行SetupSelinux return SetupSelinux(argv); } if (!strcmp(argv[1], "second_stage")) { //第三次设置selinux后启动SecondStageMain return SecondStageMain(argc, argv); } } //代码会执行多次,首次通过try_to_run_init_process执行时没有额外的命令行参数,所以会直接执行FirstStageMain return FirstStageMain(argc, argv); }
FirstStageMain流程:
system/core/init/first_stage_init.cpp
int FirstStageMain(int argc, char** argv) { if (REBOOT_BOOTLOADER_ON_PANIC) { InstallRebootSignalHandlers(); } boot_clock::time_point start_time = boot_clock::now(); std::vector errors; //定义了一个宏CHECKCALL(x),如果参数x执行返回的结果为不成功,则会将错误结果保存到errors中。 //errors.emplace_back(#x " failed", errno) 的作用是将错误信息添加到 errors 容器中。#x 是一个预处理器宏,表示参数 x 的字符串字面值。这里 #x " failed" 将会被替换为类似 "mount() failed" 的字符串。 //errno 是一个全局变量,用于保存最近一次系统调用失败的错误码。通过将 errno 作为第二个参数传递给 emplace_back() 函数,可以将错误码与错误信息一起存储在 errors 容器中。 #define CHECKCALL(x) \ if ((x) != 0) errors.emplace_back(#x " failed", errno); // Clear the umask. umask(0); //创建和挂载启动所需的目录文件 //clearenv():清除当前进程的环境变量。 //setenv():设置环境变量 PATH。 //mount():挂载文件系统,包括 tmpfs、devpts、proc、sysfs 和 selinuxfs 等。 //mkdir():创建目录 /dev/pts 和 /dev/socket。 //mknod():创建设备节点,包括 /dev/kmsg、/dev/kmsg_debug、/dev/random、/dev/urandom、/dev/ptmx 和 /dev/null 等。 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(mount("devpts", "/dev/pts", "devpts", 0, NULL)); #define MAKE_STR(x) __STRING(x) 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); 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))); // 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")); #undef CHECKCALL //将标准输入输出重定向到 /dev/null 设备节点,为了避免在启动过程中产生不必要的输出。 SetStdioToDevNull(argv); // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually // talk to the outside world... //初始化内核日志记录器,并调用 LogInit() 函数记录系统启动日志 InitKernelLogging(argv); //函数遍历 errors 变量,如果出现错误,则打印错误信息并终止系统启动。否则,函数将打印一条 INFO 级别的日志,表明第一阶段初始化已经开始 if (!errors.empty()) { for (const auto& [error_string, error_errno] : errors) { LOG(ERROR) opendir("/"), closedir}; if (!old_root_dir) { PLOG(ERROR) PLOG(ERROR) if (want_console != FirstStageConsoleParam::DISABLED) { LOG(ERROR) LOG(FATAL) StartConsole(); } //通过检查 ForceNormalBoot(cmdline) 的返回值来判断当前是否处于强制正常引导模式。如果是,则继续执行下面的操作;否则,直接跳过该代码段。 if (ForceNormalBoot(cmdline)) { //使用 mkdir() 函数创建一个名为 "/first_stage_ramdisk" 的目录,并将其权限设置为 0755。 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. //接着,使用 mount() 函数将 "/first_stage_ramdisk" 目录绑定到自身。这样做的目的是为了确保在切换根文件系统之前,"/first_stage_ramdisk" 目录已经被挂载。 if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) { LOG(FATAL) std::error_code ec; // to invoke the overloaded copy_file() that won't throw. if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) || !fs::copy_file("/userdebug_plat_sepolicy.cil", kDebugRamdiskSEPolicy, ec)) { LOG(ERROR) // setenv for second-stage init to read above kDebugRamdisk* files. setenv("INIT_FORCE_DEBUGGABLE", "true", 1); } } //根据DoFirstStageMount()函数,检查挂载必需分区是否成功。如果挂载失败,则输出错误 if (!DoFirstStageMount()) { LOG(FATAL) //输出log,指示无法获取根目录的文件信息 PLOG(ERROR) //代表着在根文件系统更改的情况下,需要释放旧的 RAM 磁盘资源,以便重新分配给新的根文件系统 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); const char* path = "/system/bin/init"; const char* args[] = {path, "selinux_setup", nullptr}; auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); close(fd); //再次执行/system/bin/init文件,并设置selinux_setup execv(path, const_cast SetStdioToDevNull(argv); InitKernelLogging(argv); if (REBOOT_BOOTLOADER_ON_PANIC) { InstallRebootSignalHandlers(); } boot_clock::time_point start_time = boot_clock::now(); MountMissingSystemPartitions(); // Set up SELinux, loading the SELinux policy. SelinuxSetupKernelLogging(); //加载SeLinux配置 SelinuxInitialize(); // We're in the kernel domain and want to transition to the init domain. File systems that // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here, // but other file systems do. In particular, this is needed for ramdisks such as the // recovery image for A/B devices. if (selinux_android_restorecon("/system/bin/init", 0) == -1) { PLOG(FATAL) path, "second_stage", nullptr}; execv(path, const_cast LOG(INFO) selinux_android_restorecon("/dev/kmsg_debug", 0); } selinux_android_restorecon("/dev/null", 0); selinux_android_restorecon("/dev/ptmx", 0); selinux_android_restorecon("/dev/socket", 0); selinux_android_restorecon("/dev/random", 0); selinux_android_restorecon("/dev/urandom", 0); selinux_android_restorecon("/dev/__properties__", 0); selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE); selinux_android_restorecon("/dev/device-mapper", 0); selinux_android_restorecon("/apex", 0); selinux_android_restorecon("/linkerconfig", 0); // adb remount, snapshot-based updates, and DSUs all create files during // first-stage init. selinux_android_restorecon(SnapshotManager::GetGlobalRollbackIndicatorPath().c_str(), 0); selinux_android_restorecon("/metadata/gsi", SELINUX_ANDROID_RESTORECON_RECURSE | SELINUX_ANDROID_RESTORECON_SKIP_SEHASH); } if (REBOOT_BOOTLOADER_ON_PANIC) { InstallRebootSignalHandlers(); } /** * *省略部分代码,设置环境状态 * **/ //初始化系统属性Property PropertyInit(); // 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. SelinuxSetupKernelLogging(); //初始化 SELinux 标签库 SelabelInitialize(); //恢复 SELinux 上下文。 SelinuxRestoreContext(); Epoll epoll; if (auto result = epoll.Open(); !result.ok()) { PLOG(FATAL)
- 1.第一个阶段完成以下内容:
- 1.加载Linux Kernel:
还没有评论,来说两句吧...