关于安卓两阶段初始化的疑问 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
LovesAsuna
V2EX    Android

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

  •  
  •   LovesAsuna 214 天前 2850 次点击
    这是一个创建于 214 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

    在 linux 内核中通过 initrd 将 amdisk-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 了吗?
    10 条回复    2025-04-09 16:27:56 +08:00
    codehz
        1
    codehz  
       214 天前
    有没有可能在遍历 mounts 的同时,/system 也在 mount 的数组里,然后也一起被 move mount 了
    emptyqwer
        2
    emptyqwer  
       214 天前
    1 、执行 chroot(".") 原来的/system/bin/init 变成了/bin/init ,所以实际上系统是再找/bin/init
    2 、是的安卓自定义了这个流程,不需要执行 init_mount 、init_chroot 、try_to_run_init_process
    LovesAsuna
        3
    LovesAsuna  
    OP
       214 天前 via Android
    @emptyqwer 第一个问题,/system/bin/init 是代里写死的,系统是怎么找到/bin/init 的?我在 linux 上都模拟不出来这种情况
    LovesAsuna
        4
    LovesAsna  
    OP
       214 天前 via Android
    @codehz 不会,在 getmounts 里有排除
    emptyqwer
        5
    emptyqwer  
       211 天前
    符号链接 你执行下 ln -s / system ,就可以在 Linux 上模拟了
    LovesAsuna
        6
    LovesAsuna  
    OP
       211 天前
    @emptyqwer 我是看到过/init 是一个到/system/bin/init 的符号链接,你这个命令不是让 system 链接到/吗?感觉反过来了,不是真实情况
    LovesAsuna
        7
    LovesAsuna  
    OP
       211 天前
    我还试了一下 chroot 之后/proc/mounts 和 /proc/self/mountinfo 的信息都没了(我在 chroot 之后重新 mount 了 proc 来看)
    rev1si0n
        8
    rev1si0n  
       188 天前
    你解包一下 system.img 其实是可以看到,/system/system 目录的,你说的切换 root 后,system 目录就变成了 /,/system/system 就变成了 /system 。
    LovesAsuna
        9
    LovesAsuna  
    OP
       188 天前
    @rev1si0n 因为不是专门的安卓开发所以觉得解包很麻烦,我都是盲猜的。如果按照你说的是/system/system 那确实能解释,但这样设计感觉好搓啊,我还以为是什么更加高级的做法
    LovesAsuna
        10
    LovesAsuna  
    OP
       188 天前
    我从 ota 提取出的 system.img 想不到居然就是一个直接的 erofs 可以直接在 linux 上挂载,看了一下内容还真的是内部又有一个 system 文件夹哈哈
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2517 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 36ms UTC 15:31 PVG 23:31 LAX 08:31 JFK 11:31
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86