首先要说明一下题主不是做安卓的,只是目前在用的安卓主力机有去刷 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(".")。
那基于上面的分析我有两点疑问:
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.