Skip to content

Zygote服务

追踪 init.zygote64.rc 与 init.zygote64_32.rc 从 ServiceParser 到 app_process、ZygoteServer 和 system_server 的真实启动链。

基于android-17.0.0_r1
AndroidinitZygoteapp_processService

Zygote服务 ​

本文面向已经读过内置命令下和init.rc主配置的读者。前文已经说明 start zygote 只调用 Service::Start(),本文继续追踪这个服务对象到底如何把一行 rc 变成一个带 fd、uid、nice、task profile 和 Java 参数的进程。

旧稿把 init.zygote64_32.rc 当成两份完整 service 定义,并把 audio、camera、media 等服务说成“fork 自 Zygote”。Android 17 的实际文件是“主配置 import + secondary service”结构;这些 native daemon 并不因为被 onrestart 重启就成为 Zygote 子进程。本文把每个结论拆成 owner、消费者和生效时机:init 负责创建资源和 fork,app_process 负责把参数交给 ZygoteInit,Java Zygote 负责预加载、system_server fork 和客户端连接。

1. 配置家族 ​

1.1 属性选择 ​

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

text
import /init.environ.rc
import /system/etc/init/hw/init.usb.rc
import /init.${ro.hardware}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /system/etc/init/hw/init.usb.configfs.rc
import /system/etc/init/hw/init.${ro.zygote}.rc

ro.zygote 不是服务名,而是决定要导入哪一个 rc 文件的属性值。它把“选择 64 位/32 位/双 Zygote”放在 Parser 输入层;真正的服务名仍由被导入文件中的 service zygote 和 service zygote_secondary 定义。导入成功只表示 service definition 进入 ServiceList,不表示进程已经 fork。

1.2 构建安装 ​

源码文件:system/core/rootdir/Android.bp

相关函数/类型:prebuilt_etc

make
prebuilt_etc {
    name: "init.zygote64.rc",
    src: "init.zygote64.rc",
    sub_dir: "init/hw",
}

prebuilt_etc {
    name: "init.zygote64_32.rc",
    src: "init.zygote64_32.rc",
    sub_dir: "init/hw",
}

运行时路径是 /system/etc/init/hw/init.zygote64.rc 和 /system/etc/init/hw/init.zygote64_32.rc。不要把源码目录 system/core/rootdir/ 当作设备上的路径,也不要用旧稿中的 init.zygote32_64.rc 作为 Android 17 AOSP 必然存在的文件;源码中明确列出的文件是 init.zygote32.rc、init.zygote64.rc 和 init.zygote64_32.rc。

1.3 双Zygote的复用 ​

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

text
import /system/etc/init/hw/init.zygote64.rc

service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote_secondary stream 660 root system
    socket usap_pool_secondary stream 660 root system
    onrestart restart zygote
    task_profiles ProcessCapacityHigh MaxPerformance
    rlimit nofile 32768 32768

主 64 位 Zygote 的完整定义来自被 import 的 init.zygote64.rc;这个文件只追加 32 位 secondary service。这样解析后的 ServiceList 有两个 service,但配置来源不是两份重复文本。--enable-lazy-preload 只传给 secondary 的 app_process32,是否预加载由 Java ZygoteInit 解析该参数决定。

2. Service解析 ​

2.1 主定义 ​

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

相关函数/类型:service zygote

text
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    socket usap_pool_primary stream 660 root system
    onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
    onrestart write /sys/power/state on
    onrestart write /sys/power/wake_lock zygote_kwl
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart --only-if-running media.tuner
    onrestart restart netd
    onrestart restart wificond
    task_profiles ProcessCapacityHigh MaxPerformance
    critical window=${zygote.critical_window.minute:-off} target=zygote-fatal

这一段不是一个 shell 脚本。service 首行的第一个 token 是 Service 名,后续 token 保存为 args_;缩进选项被 ServiceParser 分派到不同字段。比如 priority 写入 proc_attr_.priority,socket 追加 SocketDescriptor,onrestart 追加一个内部 Action,critical 设置崩溃策略字段。

2.2 解析分派 ​

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

相关函数/类型:GetParserMap

cpp
const KeywordMap<ServiceParser::OptionParser>& ServiceParser::GetParserMap() const {
    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
    [[clang::no_destroy]] static const KeywordMap<OptionParser> parser_map = {
        {"class",        {1, kMax, &ServiceParser::ParseClass}},
        {"critical",     {0, 2,    &ServiceParser::ParseCritical}},
        {"group",        {1, NR_SVC_SUPP_GIDS + 1, &ServiceParser::ParseGroup}},
        {"onrestart",    {1, kMax, &ServiceParser::ParseOnrestart}},
        {"priority",     {1, 1,    &ServiceParser::ParsePriority}},
        {"rlimit",       {3, 3,    &ServiceParser::ParseProcessRlimit}},
        {"socket",       {3, 6,    &ServiceParser::ParseSocket}},
        {"task_profiles", {1, kMax, &ServiceParser::ParseTaskProfiles}},
        {"user",         {1, 1,    &ServiceParser::ParseUser}},
    };
    return parser_map;
}

代码是教学摘录,省略了同一 map 中与 Zygote 无关的 option;参数范围和函数指针来自 Android 17 实际 GetParserMap()。这张表解释了为什么 service option 错误会在解析阶段被发现,而不是等 Zygote fork 后才暴露。

2.3 结束校验 ​

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

相关函数/类型:EndSection

cpp
Result<void> ServiceParser::EndSection() {
    if (!service_) return {};

    if (service_->proc_attr_.parsed_uid == std::nullopt) {
        return Error() << "No user specified for service '"
                       << service_->name() << "', so it would have been root.";
    }

    if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
        if ((service_->flags() & SVC_CRITICAL) != 0 &&
            (service_->flags() & SVC_ONESHOT) != 0) {
            return Error() << "service '" << service_->name()
                           << "' can't be both critical and oneshot";
        }
    }

    service_list_->AddService(std::move(service_));
    return {};
}

user root 不是可有可无的注释,它让 service 定义通过“未指定 uid”校验;critical 与 oneshot 的互斥也在这里落地。Service 被加入 ServiceList 后,后续 class_start main 或 start zygote 才能找到它。

3. 命令行入口 ​

3.1 init保存什么 ​

相关源码:

  • system/core/init/service.cpp
  • Service
  • NotifyStateChange
cpp
Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
                 const std::string& filename, const std::vector<std::string>& args)
    : name_(name),
      classnames_({"default"}),
      flags_(0),
      pid_(0),
      crash_count_(0),
      subcontext_(subcontext_for_restart_commands),
      onrestart_(false, subcontext_for_restart_commands,
                 "<Service '" + name + "' onrestart>", 0,
                 "onrestart", {}),
      args_(args),
      filename_(filename) {}

void Service::NotifyStateChange(const std::string& new_state) const {
    std::string prop_name = "init.svc." + name_;
    SetProperty(prop_name, new_state);
    if (new_state == "running") {
        SetProperty("ro.boottime." + name_,
                    std::to_string(time_started_.time_since_epoch().count()));
    }
}

Service 保存的是“将来如何启动”的数据结构:名字、class、flags、进程属性、socket 描述、onrestart Action 和 argv。init.svc.zygote=running 是 init 在父进程确认启动状态后写出的状态,不是 Java ZygoteServer 已经开始 accept 的证明。

3.2 Service::Start ​

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

相关函数/类型:Service::Start

cpp
Result<void> Service::Start() {
    if (is_updatable() && !IsDefaultMountNamespaceReady()) {
        ServiceList::GetInstance().DelayService(*this);
        return Error() << "Cannot start an updatable service '" << name_
                       << "' before configs from APEXes are all loaded.";
    }

    bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET));
    ResetFlagsForStart();

    if (flags_ & SVC_RUNNING) {
        return {};
    }

    auto expanded_path = ExpandProps(args_[0]);
    if (!expanded_path.ok()) {
        flags_ |= SVC_DISABLED;
        return expanded_path.error();
    }
    args_[0] = *expanded_path;

    std::string scon = seclabel_;
    if (scon.empty()) {
        auto result = ComputeContextFromExecutable(args_[0]);
        if (!result.ok()) return result.error();
        scon = *result;
    }

    std::vector<Descriptor> descriptors;
    for (const auto& socket : sockets_) {
        if (auto result = socket.Create(scon); result.ok()) {
            descriptors.emplace_back(std::move(*result));
        }
    }

    pid_t pid = namespaces_.flags ? clone(nullptr, nullptr,
                                           namespaces_.flags | SIGCHLD, nullptr)
                                  : fork();

这段路径有三个重要的生效时机:APEX updatable service 可能先被 DelayService;socket 在 fork 前由 init 创建;fork() 返回父子两条路径。Service::Start() 返回成功时,父进程已经记录 pid 并进入 running 状态,但子进程是否成功 execv() 还要由 SIGCHLD/reap 路径继续确认。

3.3 子进程执行 ​

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

相关函数/类型:RunService

cpp
void Service::RunService(const std::vector<Descriptor>& descriptors,
                         InterprocessFifo cgroups_activated,
                         InterprocessFifo setsid_finished) {
    EnterNamespaces(namespaces_, name_, mount_namespace_);

    if (auto result = ApplyTaskProfiles(task_profiles_); !result.ok()) {
        LOG(ERROR) << "failed to set task profiles";
    }

    SetProcessAttributesAndCaps(std::move(setsid_finished));

    if (!ExpandArgsAndExecv(args_, sigstop_)) {
        PLOG(ERROR) << "cannot execv('" << args_[0] << "')";
    }
}

RunService 依次处理 namespace、task profile、uid/gid/capability/SELinux context 和 exec。priority -20 在 SetProcessAttributes 中变成进程调度属性;user root 只描述 service 初始 uid,不能据此推断所有由 Zygote fork 的 App 仍然是 root。

4. Socket交接 ​

4.1 rc参数 ​

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

相关函数/类型:ParseSocket

cpp
Result<void> ServiceParser::ParseSocket(std::vector<std::string>&& args) {
    SocketDescriptor socket;
    socket.name = std::move(args[1]);

    auto types = Split(args[2], "+");
    if (types[0] == "stream") {
        socket.type = SOCK_STREAM;
    } else if (types[0] == "dgram") {
        socket.type = SOCK_DGRAM;
    } else if (types[0] == "seqpacket") {
        socket.type = SOCK_SEQPACKET;
    } else {
        return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket'";
    }

    socket.perm = strtol(args[3].c_str(), nullptr, 8);
    if (args.size() > 4) socket.uid = DecodeUid(args[4]).value();
    if (args.size() > 5) socket.gid = DecodeUid(args[5]).value();
    socket.context = args.size() > 6 ? args[6] : "";
    service_->sockets_.emplace_back(std::move(socket));
    return {};
}

真实函数还检查 passcred/listen decoration、重复 socket、uid/gid 解析和错误路径;摘录突出数据流。socket zygote stream 660 root system 的消费者是后续 Java Zygote.createManagedSocketFromInitSocket(),不是 init 自己接收 fork 请求。

4.2 创建时机 ​

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

相关函数/类型:SocketDescriptor::Create

cpp
Result<Descriptor> SocketDescriptor::Create(const std::string& global_context) const {
    const auto& socket_context = context.empty() ? global_context : context;
    auto result = CreateSocket(name, type | SOCK_CLOEXEC, passcred, listen,
                               perm, uid, gid, socket_context);
    if (!result.ok()) return result.error();
    return Descriptor(ANDROID_SOCKET_ENV_PREFIX + name, unique_fd(*result));
}

CreateSocket() 创建 /dev/socket/<name> 并设置权限、所有者和 SELinux context;返回的 fd 通过 ANDROID_SOCKET_<name> 环境变量交给子进程。这个 fd 继承发生在 exec 前,ZygoteServer 才能从 init 创建的 fd 接管 socket。若 socket 创建失败,Service::Start() 会记录失败并继续处理其他 descriptor;不能把“rc 中有 socket 行”当成“设备上 socket 一定存在”。

4.3 Java接管 ​

源码文件:frameworks/base/core/java/com/android/internal/os/Zygote.java

相关函数/类型:createManagedSocketFromInitSocket

java
static LocalServerSocket createManagedSocketFromInitSocket(String socketName) {
    String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
    FileDescriptor fd = new FileDescriptor();
    fd.setInt$(Integer.parseInt(System.getenv(fullSocketName)));
    return new LocalServerSocket(fd);
}

源码文件:frameworks/base/core/java/com/android/internal/os/ZygoteServer.java

java
public ZygoteServer(boolean isPrimaryZygote) {
    mIsPrimaryZygote = isPrimaryZygote;
    mZygoteSocket = Zygote.createManagedSocketFromInitSocket(
            isPrimaryZygote ? Zygote.PRIMARY_SOCKET_NAME
                           : Zygote.SECONDARY_SOCKET_NAME);
    if (isPrimaryZygote) {
        mUsapPoolSocket = Zygote.createManagedSocketFromInitSocket(
                Zygote.USAP_POOL_PRIMARY_SOCKET_NAME);
    } else {
        mUsapPoolSocket = Zygote.createManagedSocketFromInitSocket(
                Zygote.USAP_POOL_SECONDARY_SOCKET_NAME);
    }
}

这里的“交接”不是重新 bind 一个同名 Unix socket:Java 侧消费的是 init 已经打开的 fd。若环境变量缺失或 fd 无效,Java 侧构造 LocalServerSocket 会失败;这类故障发生在 Service 已被 init 标记 running 之后,因此 init.svc.zygote 与“Zygote 可接受请求”是两个不同时间点。

5. 主次协作 ​

5.1 主Zygote ​

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

text
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    socket usap_pool_primary stream 660 root system
    task_profiles ProcessCapacityHigh MaxPerformance

主 Zygote 的 --start-system-server 进入 app_process 参数列表,后续由 Java ZygoteInit 决定是否 fork system_server。class main 与 on zygote-start 的 start zygote 是两个不同层次:前者是 class 归属,后者是实际启动触发。

5.2 Secondary ​

相关源码:

  • system/core/rootdir/init.zygote64_32.rc
  • system/core/rootdir/init.zygote32.rc
text
service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote_secondary stream 660 root system
    socket usap_pool_secondary stream 660 root system
    onrestart restart zygote
    task_profiles ProcessCapacityHigh MaxPerformance
    rlimit nofile 32768 32768

secondary 没有 --start-system-server,也使用独立的 socket 和 USAP socket;32 位 ABI 还把 nofile 上限固定为 32768,因为 ilp32 stdio 实现使用 short 保存 fd。它不是“主 Zygote 的低配副本”,而是另一套 Java ZygoteServer,承担支持 32 位 ABI 的 fork 请求。

5.3 等待另一端 ​

相关源码:

  • frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
  • forkSystemServer
java
if (pid == 0) {
    if (hasSecondZygote(abiList)) {
        waitForSecondaryZygote(socketName);
    }

    zygoteServer.closeServerSocket();
    return handleSystemServerProcess(parsedArgs);
}

system_server 子进程在进入自己的处理前,若设备存在另一套 ABI Zygote,会等待另一端连接可用。这个等待发生在 Java 子进程路径,不是 init 的 start zygote_secondary 同步等待;两者的 owner 和日志位置不同。

图中 Z64/Z32 是 init 的 Service,J64/J32 是 Java Zygote;两者不能用同一个“zygote 进程”概念代替。system_server 只从带 --start-system-server 的主 Zygote fork,secondary 主要提供另一 ABI 的应用 fork 能力。

6. Java入口 ​

6.1 app_process解析 ​

源码文件:frameworks/base/cmds/app_process/app_main.cpp

相关函数/类型:main

cpp
bool zygote = false;
bool startSystemServer = false;
String8 niceName;

while (i < argc) {
    const char* arg = argv[i++];
    if (strcmp(arg, "--zygote") == 0) {
        zygote = true;
        niceName = ZYGOTE_NICE_NAME;
    } else if (strcmp(arg, "--start-system-server") == 0) {
        startSystemServer = true;
    } else if (strncmp(arg, "--nice-name=", 12) == 0) {
        niceName = (arg + 12);
    }
}

if (zygote) {
    if (startSystemServer) {
        args.add(String8("start-system-server"));
    }
    args.add(String8("--abi-list=") + abiList);
    runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
}

摘录省略了真实代码中的 option 扫描、ABI property 读取和剩余参数复制;保留的是 rc 参数如何变成 Java main 参数。-Xzygote 是 VM 选项,--zygote 和 --start-system-server 是 app_process 内部参数,--socket-name=... 则作为剩余参数继续传给 ZygoteInit。

6.2 ZygoteInit参数 ​

源码文件:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

相关函数/类型:main

java
boolean startSystemServer = false;
String zygoteSocketName = "zygote";
String abiList = null;
boolean enableLazyPreload = false;

for (int i = 1; i < argv.length; i++) {
    if ("start-system-server".equals(argv[i])) {
        startSystemServer = true;
    } else if ("--enable-lazy-preload".equals(argv[i])) {
        enableLazyPreload = true;
    } else if (argv[i].startsWith("--abi-list=")) {
        abiList = argv[i].substring("--abi-list=".length());
    } else if (argv[i].startsWith("--socket-name=")) {
        zygoteSocketName = argv[i].substring("--socket-name=".length());
    } else {
        throw new RuntimeException("Unknown command line argument: " + argv[i]);
    }
}

这段代码把两个容易混淆的选择拆开:start-system-server 决定是否进入 forkSystemServer();--enable-lazy-preload 决定是否跳过启动时 preload();socket name 决定连接哪一组 init fd。secondary 的“32 位”来自 app_process32 和 ABI list,不是 Java 代码里硬编码一个 secondary=true。

6.3 预加载与循环 ​

源码文件:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

相关函数/类型:main

java
if (!enableLazyPreload) {
    bootTimingsTraceLog.traceBegin("ZygotePreload");
    preload(bootTimingsTraceLog);
    bootTimingsTraceLog.traceEnd();
}

Zygote.initNativeState(isPrimaryZygote);
ZygoteHooks.stopZygoteNoThreadCreation();
zygoteServer = new ZygoteServer(isPrimaryZygote);

if (startSystemServer) {
    Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
    if (r != null) {
        r.run();
        return;
    }
}

caller = zygoteServer.runSelectLoop(abiList);

父进程在 forkSystemServer() 返回 null 后继续进入 runSelectLoop();子进程得到非 null Runnable,关闭 Zygote server socket 并进入 system_server 处理。start zygote 的 init 成功、app_process 进入 Java、完成 preload、开始 accept、system_server fork 是四个不同时间点。

图中需要区分 init.svc.zygote 的写入点和 Java runSelectLoop():前者位于 init 父进程的 Start() 路径,后者位于更后的子进程执行路径。现场只看到前者时,不能直接判断 Zygote socket 已经可用。

7. 失败重启 ​

7.1 onrestart命令 ​

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

相关函数/类型:ParseOnrestart

cpp
Result<void> ServiceParser::ParseOnrestart(std::vector<std::string>&& args) {
    args.erase(args.begin());
    int line = service_->onrestart_.NumCommands() + 1;
    if (auto result = service_->onrestart_.AddCommand(std::move(args), line);
        !result.ok()) {
        return Error() << "cannot add Onrestart command: " << result.error();
    }
    return {};
}

onrestart 不是“启动后立即执行”的 action,而是存进 Service 自己的 onrestart_ Action。只有服务被 reap 并进入重启路径时,Service::Reap() 才会调用 onrestart_.ExecuteAllCommands()。

7.2 Zygote的清理 ​

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

相关函数/类型:Service::Reap

cpp
for (const auto& socket : sockets_) {
    if (socket.persist) continue;
    auto path = ANDROID_SOCKET_DIR "/" + socket.name;
    unlink(path.c_str());
}

if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) &&
    on_failure_reboot_target_) {
    trigger_shutdown(*on_failure_reboot_target_);
}

flags_ &= (~SVC_RESTART);
flags_ |= SVC_RESTARTING;
onrestart_.ExecuteAllCommands();
NotifyStateChange("restarting");

重启路径先清理非 persistent socket,再执行 failure reboot 或 onrestart 命令,最后更新 init.svc.*。Zygote rc 的 onrestart restart audioserver 等是 init 层的恢复动作,不表示这些服务是 Zygote 的 Unix 子进程;它们是独立 Service,重启是为了恢复跨进程依赖状态。

7.3 临界崩溃 ​

相关源码:

  • system/core/init/service_parser.cpp
  • ParseCritical
  • system/core/init/service.cpp
  • Service::Reap
cpp
Result<void> ServiceParser::ParseCritical(std::vector<std::string>&& args) {
    std::optional<std::string> fatal_reboot_target;
    std::optional<std::chrono::minutes> fatal_crash_window;

    for (auto it = args.begin() + 1; it != args.end(); ++it) {
        auto arg = android::base::Split(*it, "=");
        if (arg[0] == "target") {
            fatal_reboot_target = arg[1];
        } else if (arg[0] == "window") {
            auto window = ExpandProps(arg[1]);
            if (!window.ok()) return window.error();
            if (*window == "off") return {};
            int minutes;
            if (!ParseInt(*window, &minutes, 0)) return Error() << "invalid window";
            fatal_crash_window = std::chrono::minutes(minutes);
        }
    }

    service_->fatal_reboot_target_ = *fatal_reboot_target;
    service_->fatal_crash_window_ = *fatal_crash_window;
    service_->flags_ |= SVC_CRITICAL;
    return {};
}

这段摘录省略了 Android 17 对未知参数、可选值和空 optional 的完整错误处理。实际 critical window=${zygote.critical_window.minute:-off} target=zygote-fatal 默认窗口为 off;只有属性展开为整数时才启用窗口。Reap() 在服务异常退出、未完成启动或崩溃次数超过阈值时,结合 sys.boot_completed、窗口和 24 小时节流决定是否触发 fatal reboot target。

8. 启动检查 ​

8.1 解析测试 ​

相关源码:

  • system/core/init/service_test.cpp
  • Service::Start

读者可以用以下断言检查自己是否真正读懂了配置:

检查项源码或测试入口未覆盖内容
socket zygote 变成 descriptorParseSocket → SocketDescriptor::CreateJava 已经 accept
priority -20 影响子进程属性ParsePriority → SetProcessAttributes一定获得 CPU 时间
onrestart 只在 reap 后执行ParseOnrestart → Service::Reap所有依赖服务恢复成功
critical ... target 保存到 ServiceParseCritical → Reap一定触发 reboot
secondary 使用 lazy preloadrc argv → ZygoteInitsecondary 启动更快

service_test.cpp 的测试包括 service 解析、临时 oneshot service、root 权限下真实启动/停止和 namespace/cgroup 行为;它不是 Android 设备上完整 Zygote 启动测试。尤其是 StartConsole 等测试需要 root 或特定 build,不能把 host parser 断言扩大为 SELinux policy、ART preload 或 system_server 证明。

8.2 现场检查 ​

在 userdebug/eng 设备上执行只读检查:

bash
adb shell getprop ro.zygote
adb shell getprop init.svc.zygote
adb shell getprop init.svc.zygote_secondary
adb shell ls -lZ /dev/socket/zygote /dev/socket/usap_pool_primary
adb shell ps -A -o PID,PPID,USER,NAME,ARGS | grep -E 'zygote|system_server'
adb shell logcat -b all -d -s init:I Zygote:I | tail -n 120

这些命令分别观察配置选择、init Service 状态、socket inode/标签、父子进程关系和 Java 日志。init.svc.zygote=running 只能证明 init 父进程完成了启动登记;需要同时看到 socket 存在、Zygote Java 日志进入 runSelectLoop,才能把“可接收 fork 请求”作为更强结论。system_server 出现也不等于全部 framework service 已注册。

8.3 可复现搜索 ​

bash
rg -n "service zygote|zygote_secondary|usap_pool" system/core/rootdir
rg -n "ParseSocket|ParseCritical|ParseOnrestart|Service::Start|Service::Reap" system/core/init
rg -n "createManagedSocketFromInitSocket|forkSystemServer|runSelectLoop" frameworks/base/core/java/com/android/internal/os
rg -n "--start-system-server|--socket-name|enable-lazy-preload" frameworks/base/cmds/app_process frameworks/base/core/java/com/android/internal/os

第一组搜索回答“哪份 rc 被安装和导入”,第二组回答“init 如何消费 service option”,第三、四组回答“fd 和 argv 如何进入 Java”。如果四条链不能闭合,说明你看到的只是配置文本,而不是可验证的启动过程。

8.4 失败边界 ​

Zygote 启动失败至少有四种不同位置:

  1. Parser 失败:service option、uid/gid、critical 或 socket 参数错误,Service 根本不会进入可启动状态;
  2. init Start 失败:可执行文件、SELinux context、socket、fork、cgroup 或 namespace 失败;
  3. app_process/Java 失败:ABI list、未知参数、预加载、ZygoteServer 接管 fd 失败;
  4. fork 后消费者失败:system_server 或应用请求处理失败,Zygote 本身可能仍在 accept。

恢复也分层:init 的 onrestart 只处理 Service 重启动作,Java Zygote 的内部异常由进程退出交给 init reap;没有一条“重启 Zygote 就自动恢复所有 framework 状态”的事务路径。

9. 启动追踪 ​

建议读者从 init.svc.zygote=running 但 /dev/socket/zygote 不可用的现象开始验证:

  1. 检查 ro.zygote,确认导入的是 init.zygote64.rc、init.zygote64_32.rc 还是 init.zygote32.rc;
  2. 回到 ServiceParser::ParseSocket(),确认 socket 名、类型、权限、uid/gid 和 context;
  3. 回到 Service::Start(),确认 descriptor 创建发生在 fork 前;
  4. 查看 service_utils.cpp 的 ANDROID_SOCKET_ 环境变量交接;
  5. 查看 ZygoteServer 是否按 primary/secondary 名称接管 fd;
  6. 最后区分 init 状态、Java Zygote 状态和 system_server 状态,不用单个 property 代替三者。

本文证明了 Android 17 Zygote rc 家族、ServiceParser 字段、init fork/socket/属性交接、app_process 参数转换、Java Zygote 预加载和 system_server fork 的主线;没有证明具体设备的 ABI 选择、SELinux allow 规则、ART preload 完整耗时、AMS 启动完成或应用 ABI 路由策略。下一篇将继续处理 vendor、odm、product rc 的分区加载边界,而不是把所有 service 定义继续堆进 Zygote 文档。