• Android 12 S 系统开机流程分析(一)


    开机有好几种方式启动,本文主要讲的是按Power键开机流程。

    本文参考AOSP 12原生代码,链接为:AOSP 12 Searchicon-default.png?t=N7T8http://aospxref.com/android-12.0.0_r3/

    目录

    1. BootLoader加载

    2. kernel启动

    3. init进程启动

    3.1 FirstStageMain

    3.1.1 init若崩溃会重新启动BootLoader

    3.1.2 创建以及挂载文件系统

    3.1.3 kernel log初始化

    3.1.4 加载/lib/moudles/下的ko文件, 打开串口log

    3.1.5 拷贝ramdisk prop文件,创建new ramdisk, 删除old ramdisk

    3.1.6 重新执行init进程并携带参数selinux_setup


    1. BootLoader加载

    当按下设备电源键时,最先运行的就是 bootloader(固化在ROM的程序),bootloader 的主要作用就是硬件设备(如 CPU、flash、内存)的初始化并加载到RAM,通过建立内存空间映射,为装载 Linux 内核做好准备,。如果 bootloader 在运行期间,按下预定义的组合按键,可以进入系统fastboot模式 或者 Receiver 模式。

    1)当用户按下开机键时,引导芯片代码开始从预定义的地方(固定在ROM中)开始执行,加载BootLoader到内存中执行。

    2)BootLoader是在操作系统内核运行之前运行的一段小程序,通过这段小程序初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境,最终目标是将系统OS拉起并运行。

    3)整个系统的加载任务都是由BootLoader完成的。

    2. kernel启动

    在编译完AOSP时会生成boot.img或者boot_debug.img,该镜像就是 Linux 内核和根文件系统,bootloader 会把该镜像装载到内存中,然后 linux 内核会执行整个系统的初始化,完成后装载根文件系统,最后启动 init 进程。

    1)当内核启动时,设置缓存、被保护存储器、计划列表、加载驱动。

    2)在内核完成系统设置后,它首先在系统文件中寻找init.rc,并启动init进程

    kernel会去启动init,启动log如下

    1. 01-05 23:45:42.396     1     1 I         : Run /init as init process
    2. 01-05 23:45:42.396     1     1 D with arguments:  
    3. 01-05 23:45:42.396     1     1 D         : /init
    4. 01-05 23:45:42.396     1     1 D with environment:  
    5. 01-05 23:45:42.396     1     1 D         : HOME=/
    6. 01-05 23:45:42.396     1     1 D         : TERM=linux

    3. init进程启动

    init是被kernel拉起来的。

    init进程是Android系统启动后,由内核启动的第一个用户级进程,init的进程号为1.

    Android中所有进程都是由init进程创建并运行的。

    init启动主要有三大步骤,分别为

        1. FirstStageMain(第一阶段):主要挂载基本的文件系统,挂载特定分区等

        2. SetupSelinux(Selinux配置阶段)

        3. SecondStageMain(第二阶段)

    如果抓一份开机的log,会发现有如下log输出。分别对应各个步骤的log,且可以看到pid为1。

    1. 01-05 23:45:42.397     1     1 I init    : init first stage started!
    2. 01-05 23:45:42.812     1     1 I init    : Opening SELinux policy
    3. 01-05 23:45:42.819     1     1 I init    : Loading SELinux policy
    4. 01-05 23:45:42.943     1     1 I init    : init second stage started!

    init相关代码目录为:

    /system/core/init

    init进程启动后,首先会走它的main方法,首先会设置init进程优先级为最大,再根据携带的参数决定走哪个方法,由于kernel启动的时候没有携带参数,所以argc是1。

    代码如下

    1. /system/core/init/main.cpp
    2. int main(int argc, char** argv) {
    3. #if __has_feature(address_sanitizer)
    4. __asan_set_error_report_callback(AsanReportCallback);
    5. #endif
    6. //setpriority(int which, int who, int prio);
    7. //prio范围为:-20~20,prio越小,优先级越大。
    8. //which为PRIO_PROCESS时,含义为设置进程号为who的优先级为prio
    9. //设置init进程的优先级为最高优先级
    10. setpriority(PRIO_PROCESS, 0, -20);
    11. if (!strcmp(basename(argv[0]), "ueventd")) {
    12. return ueventd_main(argc, argv);
    13. }
    14. if (argc > 1) {
    15. if (!strcmp(argv[1], "subcontext")) {
    16. android::base::InitLogging(argv, &android::base::KernelLogger);
    17. const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
    18. return SubcontextMain(argc, argv, &function_map);
    19. }
    20. if (!strcmp(argv[1], "selinux_setup")) {
    21. return SetupSelinux(argv);
    22. }
    23. if (!strcmp(argv[1], "second_stage")) {
    24. return SecondStageMain(argc, argv);
    25. }
    26. }
    27. //由于kernel启动init的时候没有携带任何参数,所以会首先走入此方法
    28. return FirstStageMain(argc, argv);
    29. }

    3.1 FirstStageMain

    接着讲FirstStageMain函数具体做了什么。

    由于执行的步骤太多,下面一步步来分析具体做了什么

    3.1.1 init若崩溃会重新启动BootLoader

    当init崩溃时,为了防止内核进入panic状态,会重新启动BootLoader。

    1. int FirstStageMain(int argc, char** argv) {
    2. //1
    3. if (REBOOT_BOOTLOADER_ON_PANIC) {
    4. InstallRebootSignalHandlers();
    5. }

    1)InstallRebootSignalHandlers

    InstallRebootSignalHandlers中主要做了以下几件事:

    1. 初始化了一个自定义信号集,将其所有信号都填充满,即将信号集中的所有的标志位都置为1,使得这个集合包含所有可接受的信号,也就是阻塞所有信号。这个函数可以用于快速创建一个包含所有信号的信号集,然后可以根据需要删除其中的某些信号。

    2. init创建出来的子进程不做处理,直接exit;如果不是子进程,则代表是init进程,则执行InitFatalReboot

    3. 通过syscall向内核发送重启命令

    4. 捕获一些信号

    1. void InstallRebootSignalHandlers() {
    2. struct sigaction action;
    3. memset(&action, 0, sizeof(action));
    4. //用于初始化一个自定义信号集,将其所有信号都填充满,也就是将信号集中的所有的
    5. //标志位置为1,使得这个集合包含所有可接受的信号
    6. //这个函数可以用于快速创建一个包含所有信号的信号集,然后可以根据需要删除其中的某些信号。
    7. sigfillset(&action.sa_mask);
    8. action.sa_handler = [](int signal) {
    9. //从init派生的进程也会捕获这些信号处理程序,但是我们不希望它们触发重新启动,
    10. //所以我们在这里直接为子进程调用_exit()。
    11. if (getpid() != 1) {
    12. _exit(signal);
    13. }
    14. //调用DoReboot()或LOG(FATAL)不是一个好的选择,因为这是一个信号处理程序。
    15. //RebootSystem使用syscall()
    16. InitFatalReboot(signal);
    17. };
    18. action.sa_flags = SA_RESTART;
    19. //sigaction(int signum, const struct sigaction *act,struct sigaction *oldact)
    20. //参数1:要捕获的信号
    21. //参数2:接收到信号之后对信号进行处理的结构体
    22. //参数3:接收到信号之后,保存原来对此信号处理的各种方式与信号(可用来做备份)。如果不需要备份,此处可以填NULL
    23. sigaction(SIGABRT, &action, nullptr);
    24. sigaction(SIGBUS, &action, nullptr);
    25. sigaction(SIGFPE, &action, nullptr);
    26. sigaction(SIGILL, &action, nullptr);
    27. sigaction(SIGSEGV, &action, nullptr);
    28. #if defined(SIGSTKFLT)
    29. sigaction(SIGSTKFLT, &action, nullptr);
    30. #endif
    31. sigaction(SIGSYS, &action, nullptr);
    32. sigaction(SIGTRAP, &action, nullptr);
    33. }

    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进程。

    1. void __attribute__((noreturn)) InitFatalReboot(int signal_number) {
    2. auto pid = fork();//创建子进程,成功0,失败-1
    3. if (pid == -1) {
    4. //不能创建子进程,不用尝试获取backtrace,直接重启
    5. RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
    6. } else if (pid == 0) {
    7. // 可以创建子进程,说明当前在子进程上,并且子进程需确保能重启
    8. sleep(5);
    9. //子进程重启
    10. RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
    11. }
    12. //尝试获取init的backtrace再关机重启
    13. LOG(ERROR) << __FUNCTION__ << ": signal " << signal_number;
    14. std::unique_ptr backtrace(
    15. Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
    16. if (!backtrace->Unwind(0)) {
    17. LOG(ERROR) << __FUNCTION__ << ": Failed to unwind callstack.";
    18. }
    19. for (size_t i = 0; i < backtrace->NumFrames(); i++) {
    20. LOG(ERROR) << backtrace->FormatFrameData(i);
    21. }
    22. //判断init是否被标记为异常
    23. //异常时:/proc/cmdline中有节点androidboot.init_fatal_panic,并且其值为true
    24. if (init_fatal_panic) {
    25. LOG(ERROR) << __FUNCTION__ << ": Trigger crash";
    26. //往/proc/sysrq-trigger写c
    27. android::base::WriteStringToFile("c", PROC_SYSRQ);
    28. LOG(ERROR) << __FUNCTION__ << ": Sys-Rq failed to crash the system; fallback to exit().";
    29. _exit(signal_number);
    30. }
    31. //init重启
    32. RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
    33. }

    3)RebootSystem

        走到这个函数,说明就要发送重启命令了,是通过syscall去发送的。

        在用户空间和内核空间之间,通过syscall(系统调用, system call)的中间层来通信,连接用户态和内核态的桥梁。用户空间通过向内核空间发出Syscall,产生软中断,从而让程序陷入内核态,执行相应的操作。

    RebootSystem如下:

    1. void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
    2. LOG(INFO) << "Reboot ending, jumping to kernel";
    3. if (!IsRebootCapable()) {
    4. // On systems where init does not have the capability of rebooting the
    5. // device, just exit cleanly.
    6. exit(0);
    7. }
    8. switch (cmd) {
    9. case ANDROID_RB_POWEROFF:
    10. reboot(RB_POWER_OFF);
    11. break;
    12. //前面传递的cmd是ANDROID_RB_RESTART2,发送重启命令
    13. //Syscall是连接用户态和内核态的桥梁。
    14. //用户空间通过向内核空间发出Syscall,产生软中断,从而让程序陷入内核态,执行相应的操作
    15. case ANDROID_RB_RESTART2:
    16. syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
    17. LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
    18. break;
    19. case ANDROID_RB_THERMOFF:
    20. if (android::base::GetBoolProperty("ro.thermal_warmreset", false)) {
    21. LOG(INFO) << "Try to trigger a warm reset for thermal shutdown";
    22. static constexpr const char kThermalShutdownTarget[] = "shutdown,thermal";
    23. syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
    24. LINUX_REBOOT_CMD_RESTART2, kThermalShutdownTarget);
    25. } else {
    26. reboot(RB_POWER_OFF);
    27. }
    28. break;
    29. }
    30. // In normal case, reboot should not return.
    31. PLOG(ERROR) << "reboot call returned";
    32. abort();
    33. }

    3.1.2 创建以及挂载文件系统

    第二步,主要是创建和挂载文件系统

    1. //设置允许当前进程创建文件或者目录最大可操作的权限
    2. umask(0);
    3. //清楚环境变量
    4. CHECKCALL(clearenv());
    5. //设置环境变量
    6. CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
    7. //设置/dev为tmpfs类型,并挂载,且权限为0755,本进程可读可写可执行,本组和其他组只能读写
    8. CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
    9. CHECKCALL(mkdir("/dev/pts", 0755));
    10. CHECKCALL(mkdir("/dev/socket", 0755));
    11. CHECKCALL(mkdir("/dev/dm-user", 0755));
    12. //设置/dev/pts为devpts类型,并挂载
    13. CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
    14. #define MAKE_STR(x) __STRING(x)
    15. CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
    16. #undef MAKE_STR
    17. // 读取操作系统的启动参数
    18. CHECKCALL(chmod("/proc/cmdline", 0440));
    19. std::string cmdline;
    20. android::base::ReadFileToString("/proc/cmdline", &cmdline);
    21. // Don't expose the raw bootconfig to unprivileged processes.
    22. chmod("/proc/bootconfig", 0440);
    23. std::string bootconfig;
    24. android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
    25. gid_t groups[] = {AID_READPROC};
    26. CHECKCALL(setgroups(arraysize(groups), groups));
    27. CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
    28. CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
    29. //创建kernel log节点
    30. CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));
    31. if constexpr (WORLD_WRITABLE_KMSG) {
    32. CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
    33. }
    34. //随机数
    35. CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
    36. CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
    37. CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
    38. CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
    39. CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
    40. "mode=0755,uid=0,gid=1000"));
    41. // /mnt/vendor is used to mount vendor-specific partitions that can not be
    42. // part of the vendor partition, e.g. because they are mounted read-write.
    43. CHECKCALL(mkdir("/mnt/vendor", 0755));
    44. // /mnt/product is used to mount product-specific partitions that can not be
    45. // part of the product partition, e.g. because they are mounted read-write.
    46. CHECKCALL(mkdir("/mnt/product", 0755));
    47. // /debug_ramdisk is used to preserve additional files from the debug ramdisk
    48. CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
    49. "mode=0755,uid=0,gid=0"));
    50. // /second_stage_resources is used to preserve files from first to second
    51. // stage init
    52. CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
    53. "mode=0755,uid=0,gid=0"))
    54. #undef CHECKCALL

    查看机器的mount挂载详情表:   adb shell mount

    可以看到有很多种文件类型。

    1. $:/ # mount
    2. tmpfs on /dev type tmpfs (rw,seclabel,nosuid,relatime,size=7984680k,nr_inodes=1996170,mode=755)
    3. devpts on /dev/pts type devpts (rw,seclabel,relatime,mode=600,ptmxmode=000)
    4. proc on /proc type proc (rw,relatime,gid=3009,hidepid=invisible)
    5. sysfs on /sys type sysfs (rw,seclabel,relatime)
    6. selinuxfs on /sys/fs/selinux type selinuxfs (rw,relatime)
    7. tmpfs on /mnt type tmpfs (rw,seclabel,nosuid,nodev,noexec,relatime,size=7984680k,nr_inodes=1996170,mode=755,gid=1000)
    8. /dev/block/sda9 on /metadata type ext4 (rw,seclabel,nosuid,nodev,noatime,discard)
    9. /dev/block/dm-2 on / type ext4 (ro,seclabel,nodev,relatime,discard)
    10. /dev/block/dm-3 on /system_ext type ext4 (ro,seclabel,relatime,discard)
    11. /dev/block/dm-1 on /product type ext4 (ro,seclabel,relatime,discard)
    12. /dev/block/dm-4 on /vendor type ext4 (ro,seclabel,relatime,discard)
    13. /dev/block/dm-5 on /vendor_dlkm type ext4 (ro,seclabel,relatime,discard)
    14. /dev/block/dm-0 on /odm type ext4 (ro,seclabel,relatime,discard)
    15. /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)
    16. 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)
    17. 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)
    18. 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)
    19. 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)
    20. 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)
    21. 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)
    22. ...
    23. ...

    讲一下这些都代表什么意思

    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.

    权限说明:

    权限:0755
    含义当前用户组用户group其他用户
    可拥有的权限rwxr-xr-x

     一般赋予目录0755权限,文件0644权限。

    每个组group的权限范围为0-7,含义如下

    ls -l结果二进制含义
    ---0000no excute , no write ,no read
    --x1001excute, (no write, no read)
    -w-2010write 
    -wx3011write, excute
    r--4100read
    r-x5101read, excute
    rw-  6110read, write
    rwx7111read, write , excute

    查看设备节点内存,可以看到system,vendor,product都挂载到了哪里。

     adb shell df -h

    1. $:/ # df -h
    2. Filesystem       Size Used Avail Use% Mounted on
    3. tmpfs            7.6G 1.6M  7.6G   1% /dev
    4. tmpfs            7.6G    0  7.6G   0% /mnt
    5. /dev/block/sda9   11M 188K   11M   2% /metadata
    6. /dev/block/dm-2  914M 912M  2.7M 100% /
    7. /dev/block/dm-6  1.9G 230M  1.7G  12% /mnt/scratch
    8. overlay          1.9G 230M  1.7G  12% /system
    9. overlay          1.9G 230M  1.7G  12% /system_ext
    10. overlay          1.9G 230M  1.7G  12% /product
    11. overlay          1.9G 230M  1.7G  12% /vendor
    12. overlay          1.9G 230M  1.7G  12% /vendor_dlkm
    13. overlay          1.9G 230M  1.7G  12% /odm
    14. tmpfs            7.6G 8.0K  7.6G   1% /apex
    15. tmpfs            7.6G 576K  7.6G   1% /linkerconfig
    16. /dev/block/sda2   27M 1.1M   26M   5% /mnt/vendor/persist
    17. /dev/block/sde6  300M  54M  246M  19% /vendor/firmware_mnt
    18. /dev/block/sde11  59M  39M   20M  67% /vendor/dsp
    19. /dev/block/sde7   64M 2.9M   61M   5% /vendor/bt_firmware
    20. /dev/block/sde26  30M    0   30M   0% /mnt/vendor/qmcs
    21. /dev/block/dm-7   45G 4.7G   40G  11% /data
    22. tmpfs            7.6G    0  7.6G   0% /data_mirror 

    3.1.3 kernel log初始化

    1. SetStdioToDevNull(argv);
    2. //可以输出kernel log了,前面已经创建了/dev/dmsg
    3. InitKernelLogging(argv);
    4. if (!errors.empty()) {
    5. for (const auto& [error_string, error_errno] : errors) {
    6. LOG(ERROR) << error_string << " " << strerror(error_errno);
    7. }
    8. LOG(FATAL) << "Init encountered errors starting first stage, aborting";
    9. }
    10. //此时log系统已经可以用了
    11. LOG(INFO) << "init first stage started!";

    3.1.4 加载/lib/moudles/下的ko文件, 打开串口log

    这一部分的代码主要功能如下:

    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

    1. //打开根目录/
    2. auto old_root_dir = std::unique_ptrdecltype(&closedir)>{opendir("/"), closedir};
    3. if (!old_root_dir) {
    4. PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";
    5. }
    6. struct stat old_root_info;
    7. if (stat("/", &old_root_info) != 0) {
    8. PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
    9. old_root_dir.reset();
    10. }
    11. auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0;
    12. boot_clock::time_point module_start_time = boot_clock::now();
    13. int module_count = 0;
    14. //加载/lib/modules下的ko文件
    15. if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,
    16. module_count)) {
    17. if (want_console != FirstStageConsoleParam::DISABLED) {
    18. LOG(ERROR) << "Failed to load kernel modules, starting console";
    19. } else {
    20. LOG(FATAL) << "Failed to load kernel modules";
    21. }
    22. }
    23. if (module_count > 0) {
    24. auto module_elapse_time = std::chrono::duration_cast(
    25. boot_clock::now() - module_start_time);
    26. setenv(kEnvInitModuleDurationMs, std::to_string(module_elapse_time.count()).c_str(), 1);
    27. LOG(INFO) << "Loaded " << module_count << " kernel modules took "
    28. << module_elapse_time.count() << " ms";
    29. }
    30. bool created_devices = false;
    31. //根据ALLOW_FIRST_STAGE_CONSOLE(want_console)决定是否打开串口log
    32. if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {
    33. if (!IsRecoveryMode()) {
    34. created_devices = DoCreateDevices();
    35. if (!created_devices){
    36. LOG(ERROR) << "Failed to create device nodes early";
    37. }
    38. }
    39. StartConsole(cmdline);
    40. }

    3.1.5 拷贝ramdisk prop文件,创建new ramdisk, 删除old ramdisk

    这一部分的代码主要功能如下:

    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

    1. //打开/system/etc/ramdisk/build.prop文件
    2. if (access(kBootImageRamdiskProp, F_OK) == 0) {
    3. //生成/second_stage_resources/system/etc/ramdisk/build.prop
    4. //constexpr const char kSecondStageRes[] = "/second_stage_resources";
    5. //constexpr const char kBootImageRamdiskProp[] = "/system/etc/ramdisk/build.prop";
    6. //inline std::string GetRamdiskPropForSecondStage() {
    7. // return std::string(kSecondStageRes) + kBootImageRamdiskProp;
    8. //}
    9. std::string dest = GetRamdiskPropForSecondStage();
    10. std::string dir = android::base::Dirname(dest);
    11. std::error_code ec;
    12. if (!fs::create_directories(dir, ec) && !!ec) {
    13. LOG(FATAL) << "Can't mkdir " << dir << ": " << ec.message();
    14. }
    15. if (!fs::copy_file(kBootImageRamdiskProp, dest, ec)) {
    16. LOG(FATAL) << "Can't copy " << kBootImageRamdiskProp << " to " << dest << ": "
    17. << ec.message();
    18. }
    19. LOG(INFO) << "Copied ramdisk prop to " << dest;
    20. }
    21. // If "/force_debuggable" is present, the second-stage init will use a userdebug
    22. // sepolicy and load adb_debug.prop to allow adb root, if the device is unlocked.
    23. // 如果存在“/force_debugable”,则第二阶段init将使用userdebug sepolicy并加载adb_debug.prop以允许adb root
    24. // /userdebug_plat_sepolicy.cil属于selinux策略里的规则
    25. // 如果设备unlocked(解锁了),则会修改selinux规则,放大用户权限
    26. if (access("/force_debuggable", F_OK) == 0) {
    27. std::error_code ec; // to invoke the overloaded copy_file() that won't throw.
    28. if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) ||
    29. !fs::copy_file("/userdebug_plat_sepolicy.cil", kDebugRamdiskSEPolicy, ec)) {
    30. LOG(ERROR) << "Failed to setup debug ramdisk";
    31. } else {
    32. // setenv for second-stage init to read above kDebugRamdisk* files.
    33. //在second init阶段可以用到
    34. setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
    35. }
    36. }
    37. if (ForceNormalBoot(cmdline, bootconfig)) {
    38. mkdir("/first_stage_ramdisk", 0755);
    39. //挂载first_stage_ramdisk到first_stage_ramdisk
    40. if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
    41. LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
    42. }
    43. //将根目录切换到/first_stage_ramdisk
    44. SwitchRoot("/first_stage_ramdisk");
    45. }
    46. //挂载 system、vendor 、product等系统分区
    47. if (!DoFirstStageMount(!created_devices)) {
    48. LOG(FATAL) << "Failed to mount required partitions early ...";
    49. }
    50. struct stat new_root_info;
    51. //new_root_info是/first_stage_ramdisk
    52. if (stat("/", &new_root_info) != 0) {
    53. PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
    54. old_root_dir.reset();
    55. }
    56. //old_root_info是/
    57. if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) {
    58. FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);
    59. }
    60. SetInitAvbVersionInRecovery();
    61. //设置环境变量
    62. setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),
    63. 1);

    3.1.6 重新执行init进程并携带参数selinux_setup

    1. const char* path = "/system/bin/init";
    2. const char* args[] = {path, "selinux_setup", nullptr};
    3. auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
    4. dup2(fd, STDOUT_FILENO);
    5. dup2(fd, STDERR_FILENO);
    6. close(fd);
    7. //重新执行/system/bin/init,并携带了参数selinux_setup
    8. execv(path, const_cast<char**>(args));
    9. // execv() only returns if an error happened, in which case we
    10. // panic and never fall through this conditional.
    11. PLOG(FATAL) << "execv(\"" << path << "\") failed";
    12. return 1;

    后文接着讲后续流程

  • 相关阅读:
    微信小程序前端生成动态海报图
    微突发丢包的艺术
    【MySQL】存储引擎
    C++模板
    【Linux】权限管理与相关指令
    java装修设计管理系统设计与实现计算机毕业设计MyBatis+系统+LW文档+源码+调试部署
    UWB高精度定位系统:引领精准定位技术的新纪元
    abap关于COMMIT WORK (提交工作)和 COMMIT WORK AND WAIT 的区别
    【码蹄集新手村600题】强制类型转换的重要性
    在M1Mac上为GIMP安装G‘MIC插件
  • 原文地址:https://blog.csdn.net/weixin_41028555/article/details/134183395