关于安卓两阶段初始化的疑问

85 天前
 LovesAsuna

首先要说明一下题主不是做安卓的,只是目前在用的安卓主力机有去刷 magisk ,最近心血来潮想研读一下它的实现,顺便就折腾上了 linux 和安卓的初始化流程

最近在研究 magisk 是如何实现 systemless 的,然后就开始研究起了 linux 的启动流程和安卓 10+的两阶段初始化,我先说一下我对这个流程的理解:

在 linux 内核中通过 initrd 将 ramdisk-recovery.img 解压到了 rootfs ,并执行/init ,此时在用户空间进行两阶段初始化,这个/init 文件是编译自 system/core/init 目录。

在第一阶段中会判断 force_normal_boot,然后创建了 /first_stage_ramdisk ,进行了一些准备工作,然后通过 SwithRoot 将 /first_stage_ramdisk 切换为根目录。

后面执行 fsm->DoFirstStageMount(),这里是比较关键的一步,这里会调用 MountPartitions 进而到了 FirstStageMountVBootV2::TrySwitchSystemAsRoot(),这个函数做了下面一些事情:

// If system is in the fstab then we're not a system-as-root device, and in
// this case, we mount system first then pivot to it.  From that point on,
// we are effectively identical to a system-as-root device.
bool FirstStageMountVBootV2::TrySwitchSystemAsRoot() {
    UseDsuIfPresent();
    // Preloading all AVB keys from the ramdisk before switching root to /system.
    PreloadAvbKeys();

    auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
        return entry.mount_point == "/system";
    });

    if (system_partition == fstab_.end()) return true;

    if (use_snapuserd_) {
        SaveRamdiskPathToSnapuserd();
    }

    if (!MountPartition(system_partition, false /* erase_same_mounts */)) {
        PLOG(ERROR) << "Failed to mount /system";
        return false;
    }
    if (dsu_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {
        LOG(ERROR) << "check_at_most_once forbidden on external media";
        return false;
    }

    SwitchRoot("/system");

    return true;
}

void SwitchRoot(const std::string& new_root) {
    auto mounts = GetMounts(new_root);

    LOG(INFO) << "Switching root to '" << new_root << "'";

    for (const auto& mount_path : mounts) {
        auto new_mount_path = new_root + mount_path;
        mkdir(new_mount_path.c_str(), 0755);
        if (mount(mount_path.c_str(), new_mount_path.c_str(), nullptr, MS_MOVE, nullptr) != 0) {
            PLOG(FATAL) << "Unable to move mount at '" << mount_path << "' to "
                        << "'" << new_mount_path << "'";
        }
    }

    if (chdir(new_root.c_str()) != 0) {
        PLOG(FATAL) << "Could not chdir to new_root, '" << new_root << "'";
    }

    if (mount(new_root.c_str(), "/", nullptr, MS_MOVE, nullptr) != 0) {
        PLOG(FATAL) << "Unable to move root mount to new_root, '" << new_root << "'";
    }

    if (chroot(".") != 0) {
        PLOG(FATAL) << "Unable to chroot to new root";
    }
}

从这里可以看出是挂载了 system 分区到 /system 目录,然后执行了 SwitchRoot ,其逻辑是通过 /proc/mounts 读取挂载点然后通过 move mount 的方式将其他挂载点重新挂载到了 /system/xxx 下,然后再次通过 move mount 将 /system 切换到 /,最后执行 chroot(".")。


那基于上面的分析我有两点疑问:

  1. SwitchRoot("/system") 之后,为什么还能通过 /system/bin/init 来执行 后续的 selinux_setup ?按理说 /system move mount 到 / 后可以看成是将他的内容剪贴到了 / 下,还执行了 chroot("."),此时应该已经没有了 /system 目录了,那这里是怎么找到 init 文件的?
  2. 安卓的两阶段初始化都是在用户空间完成的,从 linux 内核的启动流程上看实际上都还是在 initramfs 。在安卓中已经不需要执行正常 linux 启动流程中的 init_mount 、init_chroot 、try_to_run_init_process 了吗?
1881 次点击
所在节点    Android
10 条回复
codehz
85 天前
有没有可能在遍历 mounts 的同时,/system 也在 mount 的数组里,然后也一起被 move mount 了
emptyqwer
85 天前
1 、执行 chroot(".") 原来的/system/bin/init 变成了/bin/init ,所以实际上系统是再找/bin/init
2 、是的安卓自定义了这个流程,不需要执行 init_mount 、init_chroot 、try_to_run_init_process
LovesAsuna
85 天前
@emptyqwer 第一个问题,/system/bin/init 是代里写死的,系统是怎么找到/bin/init 的?我在 linux 上都模拟不出来这种情况
LovesAsuna
85 天前
@codehz 不会,在 getmounts 里有排除
emptyqwer
83 天前
符号链接 你执行下 ln -s / system ,就可以在 Linux 上模拟了
LovesAsuna
82 天前
@emptyqwer 我是看到过/init 是一个到/system/bin/init 的符号链接,你这个命令不是让 system 链接到/吗?感觉反过来了,不是真实情况
LovesAsuna
82 天前
我还试了一下 chroot 之后/proc/mounts 和 /proc/self/mountinfo 的信息都没了(我在 chroot 之后重新 mount 了 proc 来看)
rev1si0n
59 天前
你解包一下 system.img 其实是可以看到,/system/system 目录的,你说的切换 root 后,system 目录就变成了 /,/system/system 就变成了 /system 。
LovesAsuna
59 天前
@rev1si0n 因为不是专门的安卓开发所以觉得解包很麻烦,我都是盲猜的。如果按照你说的是/system/system 那确实能解释,但这样设计感觉好搓啊,我还以为是什么更加高级的做法
LovesAsuna
59 天前
我从 ota 提取出的 system.img 想不到居然就是一个直接的 erofs 可以直接在 linux 上挂载,看了一下内容还真的是内部又有一个 system 文件夹哈哈

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://yangjunhui.monster/t/1118414

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX