开机有好几种方式启动,本文主要讲的是按Power键开机流程。
本文参考AOSP 12原生代码,链接为:AOSP 12 Search
http://aospxref.com/android-12.0.0_r3/
目录
3.1.4 加载/lib/moudles/下的ko文件, 打开串口log
3.1.5 拷贝ramdisk prop文件,创建new ramdisk, 删除old ramdisk
3.1.6 重新执行init进程并携带参数selinux_setup
当按下设备电源键时,最先运行的就是 bootloader(固化在ROM的程序),bootloader 的主要作用就是硬件设备(如 CPU、flash、内存)的初始化并加载到RAM,通过建立内存空间映射,为装载 Linux 内核做好准备,。如果 bootloader 在运行期间,按下预定义的组合按键,可以进入系统fastboot模式 或者 Receiver 模式。
1)当用户按下开机键时,引导芯片代码开始从预定义的地方(固定在ROM中)开始执行,加载BootLoader到内存中执行。
2)BootLoader是在操作系统内核运行之前运行的一段小程序,通过这段小程序初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境,最终目标是将系统OS拉起并运行。
3)整个系统的加载任务都是由BootLoader完成的。
在编译完AOSP时会生成boot.img或者boot_debug.img,该镜像就是 Linux 内核和根文件系统,bootloader 会把该镜像装载到内存中,然后 linux 内核会执行整个系统的初始化,完成后装载根文件系统,最后启动 init 进程。
1)当内核启动时,设置缓存、被保护存储器、计划列表、加载驱动。
2)在内核完成系统设置后,它首先在系统文件中寻找init.rc,并启动init进程
kernel会去启动init,启动log如下
- 01-05 23:45:42.396 1 1 I : Run /init as init process
- 01-05 23:45:42.396 1 1 D with arguments:
- 01-05 23:45:42.396 1 1 D : /init
- 01-05 23:45:42.396 1 1 D with environment:
- 01-05 23:45:42.396 1 1 D : HOME=/
- 01-05 23:45:42.396 1 1 D : TERM=linux
init是被kernel拉起来的。
init进程是Android系统启动后,由内核启动的第一个用户级进程,init的进程号为1.
Android中所有进程都是由init进程创建并运行的。
init启动主要有三大步骤,分别为
1. FirstStageMain(第一阶段):主要挂载基本的文件系统,挂载特定分区等
2. SetupSelinux(Selinux配置阶段)
3. SecondStageMain(第二阶段)
如果抓一份开机的log,会发现有如下log输出。分别对应各个步骤的log,且可以看到pid为1。
- 01-05 23:45:42.397 1 1 I init : init first stage started!
- 01-05 23:45:42.812 1 1 I init : Opening SELinux policy
- 01-05 23:45:42.819 1 1 I init : Loading SELinux policy
- 01-05 23:45:42.943 1 1 I init : init second stage started!
init相关代码目录为:
/system/core/init
init进程启动后,首先会走它的main方法,首先会设置init进程优先级为最大,再根据携带的参数决定走哪个方法,由于kernel启动的时候没有携带参数,所以argc是1。
代码如下
- /system/core/init/main.cpp
- int main(int argc, char** argv) {
- #if __has_feature(address_sanitizer)
- __asan_set_error_report_callback(AsanReportCallback);
- #endif
- //setpriority(int which, int who, int prio);
- //prio范围为:-20~20,prio越小,优先级越大。
- //which为PRIO_PROCESS时,含义为设置进程号为who的优先级为prio
- //设置init进程的优先级为最高优先级
- 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);
- }
- }
- //由于kernel启动init的时候没有携带任何参数,所以会首先走入此方法
- return FirstStageMain(argc, argv);
- }
接着讲FirstStageMain函数具体做了什么。
由于执行的步骤太多,下面一步步来分析具体做了什么
当init崩溃时,为了防止内核进入panic状态,会重新启动BootLoader。
- int FirstStageMain(int argc, char** argv) {
- //1
- if (REBOOT_BOOTLOADER_ON_PANIC) {
- InstallRebootSignalHandlers();
- }
1)InstallRebootSignalHandlers
InstallRebootSignalHandlers中主要做了以下几件事:
1. 初始化了一个自定义信号集,将其所有信号都填充满,即将信号集中的所有的标志位都置为1,使得这个集合包含所有可接受的信号,也就是阻塞所有信号。这个函数可以用于快速创建一个包含所有信号的信号集,然后可以根据需要删除其中的某些信号。
2. init创建出来的子进程不做处理,直接exit;如果不是子进程,则代表是init进程,则执行InitFatalReboot
3. 通过syscall向内核发送重启命令
4. 捕获一些信号
- void InstallRebootSignalHandlers() {
- struct sigaction action;
- memset(&action, 0, sizeof(action));
- //用于初始化一个自定义信号集,将其所有信号都填充满,也就是将信号集中的所有的
- //标志位置为1,使得这个集合包含所有可接受的信号
- //这个函数可以用于快速创建一个包含所有信号的信号集,然后可以根据需要删除其中的某些信号。
- sigfillset(&action.sa_mask);
- action.sa_handler = [](int signal) {
- //从init派生的进程也会捕获这些信号处理程序,但是我们不希望它们触发重新启动,
- //所以我们在这里直接为子进程调用_exit()。
- if (getpid() != 1) {
- _exit(signal);
- }
-
- //调用DoReboot()或LOG(FATAL)不是一个好的选择,因为这是一个信号处理程序。
- //RebootSystem使用syscall()
- InitFatalReboot(signal);
- };
- action.sa_flags = SA_RESTART;
- //sigaction(int signum, const struct sigaction *act,struct sigaction *oldact)
- //参数1:要捕获的信号
- //参数2:接收到信号之后对信号进行处理的结构体
- //参数3:接收到信号之后,保存原来对此信号处理的各种方式与信号(可用来做备份)。如果不需要备份,此处可以填NULL
- sigaction(SIGABRT, &action, nullptr);
- sigaction(SIGBUS, &action, nullptr);
- sigaction(SIGFPE, &action, nullptr);
- sigaction(SIGILL, &action, nullptr);
- sigaction(SIGSEGV, &action, nullptr);
- #if defined(SIGSTKFLT)
- sigaction(SIGSTKFLT, &action, nullptr);
- #endif
- sigaction(SIGSYS, &action, nullptr);
- sigaction(SIGTRAP, &action, nullptr);
- }
2)InitFatalReboot
InitFatalReboot做了以下几件事:
1. 判断init是否可以创建子进程,如果不能创建,则直接重启;如果能创建,则sleep 5ms,再重启子进程。
2. 如果是init进程,则获取异常的backtrace。
3. 如果init进程被标记为异常了,则往/proc/sysrq-trigger写c,让系统陷入崩溃,然后直接退出。
echo b > /proc/sysrq-trigger 立即重启
echo o > /proc/sysrq-trigger 立即关机
echo c > /proc/sysrq-trigger 立即让系统崩溃
echo t > /proc/sysrq-trigger 导出线程状态信息
echo u > /proc/sysrq-trigger 立即重新挂载所有的文件系统为只读
4.如果init没有被标记为异常,则重启init进程。
- void __attribute__((noreturn)) InitFatalReboot(int signal_number) {
- auto pid = fork();//创建子进程,成功0,失败-1
-
- if (pid == -1) {
- //不能创建子进程,不用尝试获取backtrace,直接重启
- RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
- } else if (pid == 0) {
- // 可以创建子进程,说明当前在子进程上,并且子进程需确保能重启
- sleep(5);
- //子进程重启
- RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
- }
-
- //尝试获取init的backtrace再关机重启
- LOG(ERROR) << __FUNCTION__ << ": signal " << signal_number;
- std::unique_ptr
backtrace( - Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
- if (!backtrace->Unwind(0)) {
- LOG(ERROR) << __FUNCTION__ << ": Failed to unwind callstack.";
- }
- for (size_t i = 0; i < backtrace->NumFrames(); i++) {
- LOG(ERROR) << backtrace->FormatFrameData(i);
- }
- //判断init是否被标记为异常
- //异常时:/proc/cmdline中有节点androidboot.init_fatal_panic,并且其值为true
- if (init_fatal_panic) {
- LOG(ERROR) << __FUNCTION__ << ": Trigger crash";
- //往/proc/sysrq-trigger写c
- android::base::WriteStringToFile("c", PROC_SYSRQ);
- LOG(ERROR) << __FUNCTION__ << ": Sys-Rq failed to crash the system; fallback to exit().";
- _exit(signal_number);
- }
- //init重启
- RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
- }
3)RebootSystem
走到这个函数,说明就要发送重启命令了,是通过syscall去发送的。
在用户空间和内核空间之间,通过syscall(系统调用, system call)的中间层来通信,连接用户态和内核态的桥梁。用户空间通过向内核空间发出Syscall,产生软中断,从而让程序陷入内核态,执行相应的操作。
RebootSystem如下:
- void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
- LOG(INFO) << "Reboot ending, jumping to kernel";
-
- if (!IsRebootCapable()) {
- // On systems where init does not have the capability of rebooting the
- // device, just exit cleanly.
- exit(0);
- }
-
- switch (cmd) {
- case ANDROID_RB_POWEROFF:
- reboot(RB_POWER_OFF);
- break;
- //前面传递的cmd是ANDROID_RB_RESTART2,发送重启命令
- //Syscall是连接用户态和内核态的桥梁。
- //用户空间通过向内核空间发出Syscall,产生软中断,从而让程序陷入内核态,执行相应的操作
- case ANDROID_RB_RESTART2:
- syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
- LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
- break;
-
- case ANDROID_RB_THERMOFF:
- if (android::base::GetBoolProperty("ro.thermal_warmreset", false)) {
- LOG(INFO) << "Try to trigger a warm reset for thermal shutdown";
- static constexpr const char kThermalShutdownTarget[] = "shutdown,thermal";
- syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
- LINUX_REBOOT_CMD_RESTART2, kThermalShutdownTarget);
- } else {
- reboot(RB_POWER_OFF);
- }
- break;
- }
- // In normal case, reboot should not return.
- PLOG(ERROR) << "reboot call returned";
- abort();
- }
第二步,主要是创建和挂载文件系统
- //设置允许当前进程创建文件或者目录最大可操作的权限
- umask(0);
-
- //清楚环境变量
- CHECKCALL(clearenv());
- //设置环境变量
- CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
- //设置/dev为tmpfs类型,并挂载,且权限为0755,本进程可读可写可执行,本组和其他组只能读写
- 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));
- //设置/dev/pts为devpts类型,并挂载
- 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
- // 读取操作系统的启动参数
- 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));
- //创建kernel log节点
- 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)));
-
- CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
- CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
-
- 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
查看机器的mount挂载详情表: adb shell mount
可以看到有很多种文件类型。
- $:/ # mount
- tmpfs on /dev type tmpfs (rw,seclabel,nosuid,relatime,size=7984680k,nr_inodes=1996170,mode=755)
- devpts on /dev/pts type devpts (rw,seclabel,relatime,mode=600,ptmxmode=000)
- proc on /proc type proc (rw,relatime,gid=3009,hidepid=invisible)
- sysfs on /sys type sysfs (rw,seclabel,relatime)
- selinuxfs on /sys/fs/selinux type selinuxfs (rw,relatime)
- tmpfs on /mnt type tmpfs (rw,seclabel,nosuid,nodev,noexec,relatime,size=7984680k,nr_inodes=1996170,mode=755,gid=1000)
- /dev/block/sda9 on /metadata type ext4 (rw,seclabel,nosuid,nodev,noatime,discard)
- /dev/block/dm-2 on / type ext4 (ro,seclabel,nodev,relatime,discard)
- /dev/block/dm-3 on /system_ext type ext4 (ro,seclabel,relatime,discard)
- /dev/block/dm-1 on /product type ext4 (ro,seclabel,relatime,discard)
- /dev/block/dm-4 on /vendor type ext4 (ro,seclabel,relatime,discard)
- /dev/block/dm-5 on /vendor_dlkm type ext4 (ro,seclabel,relatime,discard)
- /dev/block/dm-0 on /odm type ext4 (ro,seclabel,relatime,discard)
- /dev/block/dm-6 on /mnt/scratch type f2fs (rw,sync,lazytime,seclabel,noatime,background_gc=on,nodiscard,no_heap,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,extent_cache,mode=adaptive,active_logs=6,alloc_mode=reuse,checkpoint_merge,fsync_mode=posix,memory=normal)
- overlay on /system type overlay (ro,seclabel,noatime,lowerdir=/system,upperdir=/mnt/scratch/overlay/system/upper,workdir=/mnt/scratch/overlay/system/work,override_creds=off)
- overlay on /system_ext type overlay (ro,seclabel,noatime,lowerdir=/system_ext,upperdir=/mnt/scratch/overlay/system_ext/upper,workdir=/mnt/scratch/overlay/system_ext/work,override_creds=off)
- overlay on /product type overlay (ro,seclabel,noatime,lowerdir=/product,upperdir=/mnt/scratch/overlay/product/upper,workdir=/mnt/scratch/overlay/product/work,override_creds=off)
- overlay on /vendor type overlay (ro,seclabel,noatime,lowerdir=/vendor,upperdir=/mnt/scratch/overlay/vendor/upper,workdir=/mnt/scratch/overlay/vendor/work,override_creds=off)
- overlay on /vendor_dlkm type overlay (ro,seclabel,noatime,lowerdir=/vendor_dlkm,upperdir=/mnt/scratch/overlay/vendor_dlkm/upper,workdir=/mnt/scratch/overlay/vendor_dlkm/work,override_creds=off)
- overlay on /odm type overlay (ro,seclabel,noatime,lowerdir=/odm,upperdir=/mnt/scratch/overlay/odm/upper,workdir=/mnt/scratch/overlay/odm/work,override_creds=off)
- ...
- ...
讲一下这些都代表什么意思
tmpfs on /dev type tmpfs (rw,seclabel,nosuid,relatime,size=7984680k,nr_inodes=1996170,mode=755)
tmpfs 挂载点
tmpfs:文件系统类型
/dev:设备文件名
ro: readonly,只读挂载;
rw: read and write, 读写挂载nosuid 关闭set-user-identifier(设置用户ID)与set-group-identifer(设置组ID)设置位。
mode=755:挂载权限为0755,代表rwxr-xr-x.
权限说明:
| 权限: | 0 | 7 | 5 | 5 |
|---|---|---|---|---|
| 含义 | 当前用户 | 组用户group | 其他用户 | |
| 可拥有的权限 | rwx | r-x | r-x |
一般赋予目录0755权限,文件0644权限。
每个组group的权限范围为0-7,含义如下
| ls -l结果 | 值 | 二进制 | 含义 |
|---|---|---|---|
| --- | 0 | 000 | no excute , no write ,no read |
| --x | 1 | 001 | excute, (no write, no read) |
| -w- | 2 | 010 | write |
| -wx | 3 | 011 | write, excute |
| r-- | 4 | 100 | read |
| r-x | 5 | 101 | read, excute |
| rw- | 6 | 110 | read, write |
| rwx | 7 | 111 | read, write , excute |
查看设备节点内存,可以看到system,vendor,product都挂载到了哪里。
adb shell df -h
- $:/ # df -h
- Filesystem Size Used Avail Use% Mounted on
- tmpfs 7.6G 1.6M 7.6G 1% /dev
- tmpfs 7.6G 0 7.6G 0% /mnt
- /dev/block/sda9 11M 188K 11M 2% /metadata
- /dev/block/dm-2 914M 912M 2.7M 100% /
- /dev/block/dm-6 1.9G 230M 1.7G 12% /mnt/scratch
- overlay 1.9G 230M 1.7G 12% /system
- overlay 1.9G 230M 1.7G 12% /system_ext
- overlay 1.9G 230M 1.7G 12% /product
- overlay 1.9G 230M 1.7G 12% /vendor
- overlay 1.9G 230M 1.7G 12% /vendor_dlkm
- overlay 1.9G 230M 1.7G 12% /odm
- tmpfs 7.6G 8.0K 7.6G 1% /apex
- tmpfs 7.6G 576K 7.6G 1% /linkerconfig
- /dev/block/sda2 27M 1.1M 26M 5% /mnt/vendor/persist
- /dev/block/sde6 300M 54M 246M 19% /vendor/firmware_mnt
- /dev/block/sde11 59M 39M 20M 67% /vendor/dsp
- /dev/block/sde7 64M 2.9M 61M 5% /vendor/bt_firmware
- /dev/block/sde26 30M 0 30M 0% /mnt/vendor/qmcs
- /dev/block/dm-7 45G 4.7G 40G 11% /data
- tmpfs 7.6G 0 7.6G 0% /data_mirror
- SetStdioToDevNull(argv);
- //可以输出kernel log了,前面已经创建了/dev/dmsg
- InitKernelLogging(argv);
-
- 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系统已经可以用了
- LOG(INFO) << "init first stage started!";
这一部分的代码主要功能如下:
1)打开根目录/
2)加载/lib/moudles/下的ko文件
01-05 23:45:42.399 1 1 I init : Loading module /lib/modules/qcom_hwspinlock.ko with args ''
01-05 23:45:42.401 1 1 I init : Loaded kernel module /lib/modules/qcom_hwspinlock.ko
01-05 23:45:42.401 1 1 I init : Loading module /lib/modules/smem.ko with args ''
01-05 23:45:42.403 1 1 I init : Loaded kernel module /lib/modules/smem.ko
01-05 23:45:42.403 1 1 I init : Loading module /lib/modules/minidump.ko with args ''
01-05 23:45:42.408 1 1 I init : Loaded kernel module /lib/modules/minidump.ko
01-05 23:45:42.408 1 1 I init : Loading module /lib/modules/qcom-scm.ko with args ''
01-05 23:45:42.416 1 1 I init : Loaded kernel module /lib/modules/qcom-scm.ko
01-05 23:45:42.416 1 1 I init : Loading module /lib/modules/qcom_wdt_core.ko with args ''
3)打开串口log
- //打开根目录/
- auto old_root_dir = std::unique_ptr
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;
- //加载/lib/modules下的ko文件
- 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
( - 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;
- //根据ALLOW_FIRST_STAGE_CONSOLE(want_console)决定是否打开串口log
- 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);
- }
这一部分的代码主要功能如下:
1)打开/system/etc/ramdisk/build.prop文件,如果可以打开,则创建/second_stage_resources/system/etc/ramdisk/build.prop文件,并将/system/etc/ramdisk/build.prop拷贝到second_stage_resources/system/etc/ramdisk/build.prop下。
2)打开/force_debugable, 如果存在“/force_debugable”,则第二阶段init将使用userdebug sepolicy并加载adb_debug.prop以允许adb root
3)创建/first_stage_ramdisk并挂载,然后将根目录切换到/first_stage_ramdisk
4)挂载 system、vendor 、product等系统分区
5)free old ramdisk
- //打开/system/etc/ramdisk/build.prop文件
- if (access(kBootImageRamdiskProp, F_OK) == 0) {
- //生成/second_stage_resources/system/etc/ramdisk/build.prop
- //constexpr const char kSecondStageRes[] = "/second_stage_resources";
- //constexpr const char kBootImageRamdiskProp[] = "/system/etc/ramdisk/build.prop";
- //inline std::string GetRamdiskPropForSecondStage() {
- // return std::string(kSecondStageRes) + kBootImageRamdiskProp;
- //}
- 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.
- // 如果存在“/force_debugable”,则第二阶段init将使用userdebug sepolicy并加载adb_debug.prop以允许adb root
- // /userdebug_plat_sepolicy.cil属于selinux策略里的规则
- // 如果设备unlocked(解锁了),则会修改selinux规则,放大用户权限
- if (access("/force_debuggable", F_OK) == 0) {
- 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) << "Failed to setup debug ramdisk";
- } else {
- // setenv for second-stage init to read above kDebugRamdisk* files.
- //在second init阶段可以用到
- setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
- }
- }
-
- if (ForceNormalBoot(cmdline, bootconfig)) {
- mkdir("/first_stage_ramdisk", 0755);
- //挂载first_stage_ramdisk到first_stage_ramdisk
- if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
- LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
- }
- //将根目录切换到/first_stage_ramdisk
- SwitchRoot("/first_stage_ramdisk");
- }
- //挂载 system、vendor 、product等系统分区
- if (!DoFirstStageMount(!created_devices)) {
- LOG(FATAL) << "Failed to mount required partitions early ...";
- }
-
- struct stat new_root_info;
- //new_root_info是/first_stage_ramdisk
- if (stat("/", &new_root_info) != 0) {
- PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
- old_root_dir.reset();
- }
- //old_root_info是/
- 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);
- 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<char**>(args));
-
- // execv() only returns if an error happened, in which case we
- // panic and never fall through this conditional.
- PLOG(FATAL) << "execv(\"" << path << "\") failed";
-
- return 1;
后文接着讲后续流程