Skip to content

启动阶段

追踪 early-init、init、late-init 及文件系统、Zygote 和 boot 事件的队列与屏障。

基于android-17.0.0_r1
Androidinitinit.rcActionManagerboot

启动阶段 ​

本文面向已经读过 Init主循环 的读者。前文说明 ActionManager 如何逐命令消费事件;本文把这套机制放回 Android 17 的真实 init.rc,回答 early-init、init、late-init、post-fs-data、zygote-start 和 boot 究竟如何连接。

这里的“阶段”不是 PID 1 内部的枚举,也不是执行失败后可以整体回滚的事务。它们是事件名:C++ 或 rc builtin 把事件放进队列,所有匹配的 on <event> action 按解析顺序执行。本文会区分“事件已入队”“action 已匹配”“命令已执行”“服务已运行”四个时间点,并解释 charger、recovery、Microdroid、设备 rc 和属性条件如何改变主线。

本文不逐行解释数百条目录权限,也不替代后续 rc Parser、builtin 和服务专题。读完后,读者应能从 SecondStageMain() 还原标准启动事件链,找到每个阶段的 owner、consumer 与屏障,判断 Zygote、HAL、main class 分别在哪个事件启动,并用源码测试和设备日志验证阶段顺序。

1. 阶段本质 ​

1.1 事件模型 ​

ActionManager 的 event_queue_ 保存 EventTrigger、PropertyChange 或 BuiltinAction。early-init 等名称只是 EventTrigger 中的字符串,不存在统一的 BootStage 状态字段。

源码文件:system/core/init/action_manager.h

cpp
std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>>
        event_queue_ GUARDED_BY(event_queue_lock_);
std::queue<const Action*> current_executing_actions_;
std::size_t current_command_ = 0;

阶段状态分散在文件系统、property、服务 flags 和 action queue 中。因此看到 processing action (post-fs-data),只能证明对应 action 开始消费,不能证明 /data 上的全部工作、APEX 激活或后续服务已经完成。

1.2 匹配时机 ​

事件 action 的匹配条件由 Action::CheckEvent() 决定。事件名必须相等;若 action 还带 property 条件,则在事件被匹配的当下读取其他 property。

源码文件:system/core/init/action.cpp

cpp
bool Action::CheckEvent(const EventTrigger& event_trigger) const {
    return event_trigger == event_trigger_ && CheckPropertyTriggers();
}

bool Action::CheckEvent(const PropertyChange& property_change) const {
    const auto& [name, value] = property_change;
    return event_trigger_.empty() && CheckPropertyTriggers(name, value);
}

这给出一个重要区别:

写法触发方式条件检查时机
on bootboot 事件事件消费时
on boot && property:x=yboot 事件boot 消费时读取 x
on property:x=yproperty 变化或全量扫描property 事件消费时

组合事件 action 不会因为 x 日后变成 y 而补执行;它需要下一次同名事件。纯 property action 才能由后续属性变化触发。

2. 脚本顺序 ​

2.1 入口文件 ​

标准路径先解析 /system/etc/init/hw/init.rc,再依次解析 system、system_ext、vendor、odm 和 product 的 init 目录。ro.boot.init_rc 非空时则改用指定脚本。

源码文件:system/core/init/init.cpp

相关函数/类型:LoadBootScripts

cpp
if (bootscript.empty()) {
    parser.ParseConfig("/system/etc/init/hw/init.rc");
    parser.ParseConfig("/system/etc/init");
    parser.ParseConfig("/system_ext/etc/init");
    parser.ParseConfig("/vendor/etc/init");
    parser.ParseConfig("/odm/etc/init");
    parser.ParseConfig("/product/etc/init");
} else {
    parser.ParseConfig(bootscript);
}

目录内普通文件先排序再解析,所以同一目录的 action 顺序稳定。不同分区仍按上面的调用顺序进入 actions_。

2.2 Import顺序 ​

import 行并不在解析到该行时立刻递归。ImportParser 先记录路径,到当前文件 EOF 的 EndFile() 再依次 ParseConfig()。

源码文件:system/core/init/import_parser.cpp

cpp
void ImportParser::EndFile() {
    auto current_imports = std::move(imports_);
    imports_.clear();
    for (const auto& [import, line_num] : current_imports) {
        parser_->ParseConfig(import);
    }
}

因此主 init.rc 中已经完成提交的 action 位于其 imported 文件 action 之前。一个事件匹配多个文件时,ActionManager 遍历 actions_ 的顺序就是后续执行顺序;这也是设备厂商不能只看某个 rc 文件局部位置判断全局先后的原因。

3. 初始队列 ​

3.1 C++入口 ​

SecondStageMain() 在进入主循环前创建初始队列。Android 17 的真实顺序中,cgroup builtin 位于 early-init 之前;coldboot 等待、mmap、keychord 初始化位于 early-init 与 init 之间。

源码文件:system/core/init/init.cpp

cpp
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
am.QueueEventTrigger("early-init");
am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction,
                      "ConnectEarlyStageSnapuserd");
am.QueueBuiltinAction(wait_for_coldboot_done_action,
                      "wait_for_coldboot_done");
if (!IsMicrodroid()) {
    am.QueueBuiltinAction(CheckTradeInModeStatus,
                          "CheckTradeInModeStatus");
}
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
Keychords keychords;
am.QueueBuiltinAction(
        [&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {
            for (const auto& svc : ServiceList::GetInstance()) {
                keychords.Register(svc->keycodes());
            }
            keychords.Start(&epoll, HandleKeychord);
            return {};
        },
        "KeychordInit");
am.QueueEventTrigger("init");
am.QueueBuiltinAction(SetCopyRollbackLogsAction, "CopyRollbackLogs");

这纠正了“early-init 挂载 cgroup”的旧理解:rc 注释明确写着 cgroup 在 early-init 之前由 /etc/cgroups.json 对应的 builtin 建立,early-init 只消费已经存在的层级。

3.2 模式分流 ​

等待可选 UDC 线程后,只有 charger mode 改走 charger;normal 和 recovery 都进入 late-init。不过 recovery 构建加载的是 recovery rc 内容,不能把 system rootdir/init.rc 的命令原样套用。

源码文件:system/core/init/init.cpp

cpp
if (bootmode == BootMode::CHARGER_MODE) {
    am.QueueEventTrigger("charger");
} else {
    am.QueueEventTrigger("late-init");
}

am.QueueBuiltinAction(queue_property_triggers_action,
                      "queue_property_triggers");

queue_property_triggers_action 已经在初始队列里,但它实际执行时还会追加两个 builtin;这会影响启动早期 pure property action 的生效时机,后文单独展开。

4. EarlyInit ​

4.1 前置资源 ​

进入 on early-init 前,第二阶段已经建立属性区、SELinux label、Epoll、signal handler、property service、mount namespace,并执行 SetupCgroupsAction。所以 early-init 不是“系统什么都没有”的起点,而是“可执行 rc builtin 的最早公共事件”。

4.2 核心动作 ​

Android 17 根 init.rc 的 early-init 处理启动观测、内核接口、linkerconfig、ueventd、tracefs 和 bootstrap APEX。下面保留能建立依赖关系的真实片段。

源码文件:system/core/rootdir/init.rc

相关函数/类型:on early-init

text
on early-init
    mkdir /dev/bootchart 0755 root root
    bootchart start

    write /proc/sys/kernel/sysrq 0
    write /proc/sys/kernel/modprobe \n

    mkdir /linkerconfig/bootstrap 0755
    mkdir /linkerconfig/default 0755
    write /linkerconfig/bootstrap/ld.config.txt \#
    write /linkerconfig/default/ld.config.txt \#
    mount none /linkerconfig/bootstrap /linkerconfig bind rec

    start ueventd
    mount tracefs tracefs /sys/kernel/tracing gid=3012

    exec_start init_dev_config
    restorecon /metadata
    exec_start apexd-bootstrap
    perform_apex_config

exec_start 是同步屏障:服务退出前主循环暂停普通 action。因此 apexd-bootstrap 完成后,后续 early-init 命令和其他阶段才继续。start ueventd 则是异步启动;真正的 coldboot 完成由 C++ 队列中的 wait_for_coldboot_done_action 另行等待。

4.3 条件Action ​

同一事件还能匹配:

源码文件:system/core/rootdir/init.rc

text
on early-init && property:ro.boot.bootchart.enabled=""
    rmdir /dev/bootchart

属性为空时才删除 bootchart 目录。该判断发生在 early-init 事件消费时;以后再清空属性不会补执行这条 action。

5. Init事件 ​

5.1 基础结构 ​

on init 建立标准 fd symlink、cpuctl 子目录、挂载点和大量权限。它消费 early-init、coldboot 等前置结果,但 /data 尚未进入 post-fs-data 语义。

源码文件:system/core/rootdir/init.rc

text
on init
    sysclktz 0
    copy /proc/cmdline /dev/urandom
    copy /proc/bootconfig /dev/urandom

    symlink /proc/self/fd/0 /dev/stdin
    symlink /proc/self/fd/1 /dev/stdout
    symlink /proc/self/fd/2 /dev/stderr

    mkdir /dev/cpuctl/foreground
    mkdir /dev/cpuctl/background
    mkdir /dev/cpuctl/top-app
    mkdir /dev/cpuctl/system

这里的 mkdir 没有事务包装。中间某条权限命令失败时,已创建的目录继续存在,后续命令仍执行。

5.2 核心服务 ​

init 事件后段先启动日志和低内存服务,再启动三类 servicemanager。

源码文件:system/core/rootdir/init.rc

text
on init
    start logd

    write /proc/sys/vm/watermark_boost_factor 0
    start lmkd

    start servicemanager
    start hwservicemanager
    start vndservicemanager

    mkdir /mnt/vm 0755 root system
    mount tmpfs tmpfs /mnt/vm nosuid nodev noexec rw

start 返回成功表示 Service::Start() 已发起进程启动,不表示服务已注册 Binder 接口或完成业务初始化。后续消费者若需要完成信号,必须依赖 socket、property、binder 查询或显式 wait,而不能只依赖事件名。

6. LateInit调度 ​

6.1 Trigger链 ​

根 init.rc 的 late-init 几乎不直接配置资源,而是定义文件系统到 framework 前的事件顺序。

源码文件:system/core/rootdir/init.rc

text
on late-init
    trigger early-fs
    trigger fs
    trigger post-fs
    trigger late-fs
    trigger post-fs-data
    trigger load-bpf-programs
    trigger zygote-start
    trigger firmware_mounts_complete
    trigger early-boot
    trigger boot

do_trigger() 的实现只有入队:

源码文件:system/core/init/builtins.cpp

cpp
static Result<void> do_trigger(const BuiltinArguments& args) {
    ActionManager::GetInstance().QueueEventTrigger(args[1]);
    return {};
}

每条 trigger 命令执行时把事件追加到 FIFO 队尾。它不递归调用目标 action,也不等待目标 action 完成。

6.2 队列顺序 ​

ActionManager 只有在 current_executing_actions_ 为空时才消费下一个 event。因此 late-init 的所有匹配 action 都完成后,才会开始处理它追加的 early-fs。目标阶段的所有 action 又会在下一个事件前完成。

这是一种协作式阶段顺序,不是 C++ 栈上的函数嵌套。队列中可以穿插更早已存在的 builtin,目标 action 内也可以追加新事件。

7. 文件系统链 ​

7.1 EarlyFs ​

根配置的 early-fs 只启动 vold,为 metadata、userdata checkpoint 和后续加密挂载准备消费者。

源码文件:system/core/rootdir/init.rc

相关函数/类型:on early-fs

text
on early-fs
    start vold

设备 rc 通常在同一事件执行自身前置操作。所有匹配 action 的先后由解析顺序决定,而不是“root action 永远完成设备逻辑”。

7.2 Fs ​

标准根 init.rc 没有 on fs action;实际挂载通常由设备或产品 rc 提供。例如 goldfish 使用:

相关源码:

  • device/generic/goldfish/init/init.ranchu.rc
  • on fs
  • on late-fs
text
on fs
    mount_all /vendor/etc/fstab.ranchu --early

on late-fs
    mount_all /vendor/etc/fstab.ranchu --late

所以 fs 是扩展点,不是 root 文件中固定的一组 mount 命令。分析具体设备必须追踪其 init.${ro.hardware}.rc、vendor init 目录和 fstab。

7.3 PostFs ​

post-fs 表示早期 mount action 已完成,根配置开始只读 remount、storage bind、metadata 目录与安全标签处理。

源码文件:system/core/rootdir/init.rc

相关函数/类型:on post-fs

text
on post-fs
    exec - system system -- /system/bin/vdc checkpoint markBootAttempt
    mount rootfs rootfs / remount bind ro nodev
    mount none /mnt/user/0 /storage bind rec
    mount none none /storage slave rec
    restorecon_recursive /metadata
    mkdir /metadata/vold
    mkdir /metadata/apex 0700 root system

7.4 LateFs ​

late-fs 为带 latemount 的 fstab 项和解锁 userdata 前所需 HAL 预留。根配置启动 early_hal class,并根据内核版本配置 bootreceiver tracing。

源码文件:system/core/rootdir/init.rc

相关函数/类型:on late-fs

text
on late-fs
    chmod 0755 /sys/kernel/tracing
    chmod 0755 /sys/kernel/debug/tracing
    class_start early_hal

on late-fs && property:ro.kernel.version=4.19
    setprop bootreceiver.enable 0

on late-fs
    setprop bootreceiver.enable ${bootreceiver.enable:-1}

同一个 late-fs 事件会按 action 解析顺序执行多个段;带 property 条件的段在事件到达时决定是否加入执行队列。

8. Data屏障 ​

8.1 进入条件 ​

late-init 在 late-fs 后无条件入队 post-fs-data,但真正命令能否推进取决于 /data、vold、KeyMint、APEX 和 property 的运行结果。事件名本身不验证挂载状态。

8.2 前段资源 ​

post-fs-data 先处理 checkpoint、/data 权限、加密 key、tombstone、keystore 和 persistent property。

源码文件:system/core/rootdir/init.rc

相关函数/类型:on post-fs-data

text
on post-fs-data
    exec - system system -- /system/bin/vdc checkpoint prepareCheckpoint
    chown system system /data
    chmod 0771 /data
    restorecon /data
    installkey /data

    mkdir /data/tombstones 0775 system system encryption=Require
    start tombstoned

    enter_default_mount_ns
    mkdir /data/misc/keystore 0700 keystore keystore
    setprop keystore.boot_level 30

    mkdir /data/property 0700 root root encryption=Require
    load_persist_props
    trigger load_persist_props_action

load_persist_props_action 是兼容 vendor rc 的事件;它不代表 persistent property 此刻才开始加载,注释明确说明实际加载已由前一条 load_persist_props 完成。

8.3 等待点 ​

post-fs-data 中存在多种真正屏障:

源码文件:system/core/rootdir/init.rc

text
    wait_for_prop apexd.status activated
    wait_for_prop keystore.module_hash.sent true
    perform_apex_config

    exec_start mainline_aconfigd_init
    exec_start derive_sdk
    exec_start derive_classpath
    exec_start art_boot

    start odsign
    wait_for_prop odsign.key.done 1

wait_for_prop 设置全局 property waiter,主循环暂停普通 action;exec_start 设置 SVC_EXEC,直到子进程被 reap 才解除。这些命令把“队列顺序”升级成“结果依赖”,也是 post-fs-data 比普通目录创建更容易卡住的原因。

8.4 不可回滚 ​

若某条普通 builtin 返回错误,Action::ExecuteCommand() 记录失败与耗时,但返回类型是 void,下一轮仍推进 current_command_。

源码文件:system/core/init/action.cpp

cpp
void Action::ExecuteCommand(const Command& command) const {
    android::base::Timer t;
    auto result = command.InvokeFunc(subcontext_);
    auto duration = t.duration();

    if (!result.has_value() || duration > 50ms || /* debug logging */) {
        LOG(INFO) << "Command '" << command.BuildCommandString()
                  << "' action=" << BuildTriggersString()
                  << " took " << duration.count() << "ms and "
                  << (result.ok() ? "succeeded"
                                  : "failed: " + result.error().message());
    }
}

因此阶段没有统一 rollback。已创建目录、已设置属性、已启动服务会保留;后续命令是否还能成功取决于各自前置条件。fatal、shutdown、property wait 和 exec wait 是另外的控制路径,不能与普通 command error 混为一谈。

9. Zygote与Boot ​

9.1 ZygoteStart ​

Android 17 不在 on boot 中通过 class_start main 启动 Zygote。zygote-start 在 post-fs-data、BPF 之后单独等待 odsign verification,再直接启动两个 zygote service。

源码文件:system/core/rootdir/init.rc

text
on zygote-start
    wait_for_prop odsign.verification.done 1
    start statsd
    start zygote
    start zygote_secondary

Zygote 的 owner 是 ServiceList 中的 service 对象;事件完成只说明 start 命令已调用,不证明 system_server 已 fork 或 framework 已 ready。

9.2 Boot事件 ​

boot 事件负责网络、内核参数、设备权限和服务 class。根配置后段启动 binderized HAL 和 core class。

源码文件:system/core/rootdir/init.rc

text
on boot
    ifup lo
    hostname localhost
    domainname localdomain

    setprop net.tcp_def_init_rwnd 60
    class_start hal
    class_start core

on boot && property:ro.config.low_ram=true、debuggable 等条件 action 与普通 boot action 一同匹配。它们不是 boot 完成通知。

9.3 MainClass ​

main 和 late_start class 由 nonencrypted 事件启动:

源码文件:system/core/rootdir/init.rc

text
on nonencrypted
    class_start main
    class_start late_start

nonencrypted 由 mount_all 的 fs_mgr 返回码决定。未加密、file-encrypted 或 metadata-encrypted 且 vold 已完成准备时,queue_fs_event() 都会追加该事件;需要 recovery 时则请求 wipe/reboot。

源码文件:system/core/init/builtins.cpp

cpp
if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
    SetProperty("ro.crypto.state", "unsupported");
    ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
} else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED ||
           code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED ||
           code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
    SetProperty("ro.crypto.state", "encrypted");
    ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
}

这说明“nonencrypted”是历史事件名,不等于设备真的未加密。消费者应看 ro.crypto.state 和 fs_mgr 结果,而不是从事件名推断存储模式。

10. 属性补链 ​

10.1 延迟启用 ​

启动前已经入队 queue_property_triggers_action。它执行时并不立即修改开关,而是继续追加两个事件:先 enable,再用空 PropertyChange 全量检查当前属性。

源码文件:system/core/init/init.cpp

cpp
static Result<void> queue_property_triggers_action(const BuiltinArguments&) {
    ActionManager::GetInstance().QueueBuiltinAction(
            property_enable_triggers_action,
            "enable_property_trigger");
    ActionManager::GetInstance().QueueAllPropertyActions();
    return {};
}

10.2 队列交错 ​

初始 queue_property_triggers_action 排在 late-init 之后;late-init 执行时把 early-fs 到 boot 追加在它后面。随后 queue action 被消费,又把 enable 和全量 property scan 追加到这些阶段之后。

图表达的是标准初始队列关系,不是所有动态事件的绝对顺序。阶段中的 trigger、mount_all 和 property callback 还会继续向队尾追加事件。

10.3 BootCompleted ​

sys.boot_completed=1 是 framework 后期设置的 property,不是 init 的 boot 事件。property trigger 启用后,它匹配:

源码文件:system/core/rootdir/init.rc

text
on property:sys.boot_completed=1
    bootchart stop
    exec - system system -- /bin/rm -rf /data/per_boot
    mkdir /data/per_boot 0710 system system encryption=Require key=per_boot_ref

因此 processing action (boot) 与 sys.boot_completed=1 之间可能相隔大量 framework 工作。二者不能用同一个“启动完成”概念描述。

11. 分支路径 ​

11.1 Charger ​

charger mode 初始不入队 late-init,只执行:

源码文件:system/core/rootdir/init.rc

text
on charger
    class_start charger

healthd 决定转入完整启动时设置 sys.boot_from_charger_mode=1,对应 property action 停止 charger class 并触发 late-init。

源码文件:system/core/rootdir/init.rc

text
on property:sys.boot_from_charger_mode=1
    class_stop charger
    trigger late-init

恢复不是重新执行 early-init 和 init,而是从 late-init 文件系统链继续;因此 charger 阶段之前已建立的资源必须可复用。

11.2 Recovery ​

GetBootMode() 可以返回 recovery,但初始分支只特殊处理 charger,所以 recovery 也入队 late-init。实际 action 来自 recovery ramdisk 安装的 rc,而不是本文引用的 system 根配置;验证 recovery 必须切换到 bootable/recovery/etc/init.rc。

11.3 Microdroid ​

C++ 队列在 Microdroid 下跳过部分设备检查,实际脚本也来自 minidroid/Microdroid 配置。本文的标准 root init.rc 命令不能外推到 Microdroid;可复用的是事件队列与匹配机制。

12. 失败定位 ​

阶段问题应按层次判断:

现象优先检查尚不能证明
没有 processing action事件是否入队、脚本是否已解析、条件是否匹配builtin 是否成功
有 action 无后续阶段当前命令、property waiter、exec service目标事件未入队
command failed参数、权限、SELinux、文件状态整个阶段已停止
service restartingService::Reap() 和 restart deadlinestart 从未调用
zygote running 无界面system_server、boot animation、AMS/WMSinit 阶段未完成
boot action 已执行HAL/core class start 已发起sys.boot_completed=1

取消/中断路径也不是阶段 rollback。sys.powerctl 会让主循环优先进入 shutdown;正在执行的 builtin 仍需返回。关机处理会修改 action queue,但已产生的文件系统副作用不会自动撤销。

13. 启动测试 ​

13.1 Action顺序 ​

init_test.cpp 的 EventTriggerOrder 构造三个 on boot action,中间一个带 property:ro.hardware=*,三个 builtin 分别断言执行序号为 0、1、2。输入只有一次 QueueEventTrigger("boot")。

它证明同一事件的匹配 action 按解析进入 actions_ 的顺序执行,且满足的组合 property 条件不会另建并行队列;它不证明跨分区的最终文件清单,因为测试只解析一段临时 rc。

13.2 延迟加载 ​

LazilyLoadedActionsCantBeTriggeredByTheSameTrigger 在 on boot 中动态加载一个同样定义 on boot 的文件,断言新 action 不会被当前 boot 事件追溯匹配。LazilyLoadedActionsCanBeTriggeredByTheNextTrigger 则加载 on next,再执行 trigger next,断言新 action 可以被下一事件消费。

两组测试共同证明 action 集合在某个事件匹配时形成边界:当前事件不会回头扫描之后新增的同名 action,但未来事件会看到新增内容。这也是 APEX/late import rc 设计必须明确触发时机的原因。

13.3 设备边界 ​

这些 host 测试使用自定义 builtin map,并循环执行直到 queue 清空;它们不启动 vold、apexd 或 Zygote,也不覆盖设备 fstab。真实启动依赖仍要由固定设备配置和运行日志验证。

14. 现场验证 ​

设备上可同时观察事件、服务与最终 property:

sh
adb shell 'logcat -b all -d -s init:I | grep "processing action"'
adb shell 'getprop | grep "^\[ro.boottime.event\."'
adb shell 'getprop init.svc.zygote'
adb shell 'getprop init.svc.servicemanager'
adb shell 'getprop sys.boot_completed'
adb shell 'getprop ro.crypto.state'

若构建未启用 event timestamp,ro.boottime.event.* 为空不代表 action 未执行。应以 processing action (<trigger>) from (<file>:<line>) 找到真实消费者,再回源码检查同名 action 的解析顺序。

复现本文源码关系可按下面顺序搜索:

sh
rg -n "QueueEventTrigger|queue_property_triggers_action" system/core/init/init.cpp
rg -n "^on (early-init|init|late-init|post-fs-data|zygote-start|boot)" \
  system/core/rootdir/init.rc
rg -n "do_trigger|queue_fs_event|do_mount_all" system/core/init/builtins.cpp
rg -n "CheckEvent|ExecuteOneCommand|ParseConfigDir" system/core/init

面对某个阶段卡住,先记录最后一个 action 的文件和行号,再区分它是普通失败、wait_for_prop、exec_start 还是 service restart。随后检查目标事件是在初始 C++ 队列、late-init trigger、mount_all 返回码还是 property callback 中产生。完成这条反向追踪,才能知道问题属于 rc 顺序、设备配置还是消费者状态。

本文没有展开 rc tokenizer、mount_all 的 fs_mgr 算法、class 内服务排序和 framework 设置 boot completed 的路径。下一篇将沿 init 启动的几个关键 daemon,说明 servicemanager、surfaceflinger、Zygote 与 system_server 如何跨越这些事件建立依赖;本文保留的是阶段事件本身的真实队列与屏障。