首先要说明一下题主不是做安卓的,只是目前在用的安卓主力机有去刷 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
codehz 17 小时 39 分钟前
有没有可能在遍历 mounts 的同时,/system 也在 mount 的数组里,然后也一起被 move mount 了
|
2
emptyqwer 14 小时 49 分钟前
1 、执行 chroot(".") 原来的/system/bin/init 变成了/bin/init ,所以实际上系统是再找/bin/init
2 、是的安卓自定义了这个流程,不需要执行 init_mount 、init_chroot 、try_to_run_init_process |
3
LovesAsuna OP @emptyqwer 第一个问题,/system/bin/init 是代里写死的,系统是怎么找到/bin/init 的?我在 linux 上都模拟不出来这种情况
|
4
LovesAsuna OP @codehz 不会,在 getmounts 里有排除
|