第一篇:从电源键到上帝进程——硬件觉醒与 Init 的诞生

6 阅读5分钟

第一阶段:硬件的物理觉醒 (Power On → Bootloader)

当你按下电源键,屏幕亮起。这看似简单的动作背后,是一场精密的“代码交响曲”。
本篇我们将略去繁琐的代码细节,用带你快速穿越从物理电流用户空间第一个进程的旅程,并深入拆解 Android 系统的“上帝进程”——Init

硬件启动流程全景图

tongyi-mermaid-2026-03-11-232206.png

关键硬件动作解读:

  1. Boot ROM (信任根) :芯片出厂时写死的代码,负责验证下一级引导程序的签名,防止恶意篡改。
  2. DDR 初始化:Bootloader 最核心的任务。没有内存,操作系统无处安放。
  3. 控制权移交:Bootloader 设置好 CPU 寄存器和内存映射后,执行一条跳转指令,彻底退出历史舞台,CPU 开始执行内核代码。

第二阶段:内核冲刺与 Init 降临 (Kernel → PID 1)

内核接手后,迅速完成驱动加载和内存管理,最终目标只有一个:执行用户空间的第一个程序 /init

从内核态到用户态的跃迁 tongyi-mermaid-2026-03-11-232253.png

状态质变:此刻,CPU 从内核态 (Kernel Mode) 切换到了用户态 (User Mode) 。系统从“操作系统内核”变成了"Android 系统”。

第三阶段:PID 1 的“上帝进程”:init 与属性服务的诞生

此刻,PID 0(空闲进程)完成了它的历史使命,而 PID 1 —— init 进程,正式接管了整个世界。它是 Android 系统的“上帝”,负责孵化所有后续进程、管理硬件设备、并建立系统属性的共享空间。


第一步:init 的入口 —— main.cpp

当内核执行完 execve("/init") 后,CPU 开始执行 init 的第一行用户态代码。

代码位置system/core/init/init.cpp

// 文件:system/core/init/init.cpp
int main(int argc, char** argv) {
    // 1. 基础环境检查
    if (argc != 1) {
        fatal("init must be run as PID 1");
    }

    // 2. 挂载关键文件系统 (proc, sysfs, devtmpfs)
    MountFilesystems();

    // 3. 初始化日志系统 (logd 尚未启动,先写 kernel log)
    InitKernelLogging(argv);

    // 4. 【关键】设置 SELinux 为 Enforcing 模式
    SecurityInitialize();

    // 5. 初始化属性服务 (Property Service)
    PropertyInit();

    // 6. 解析 .rc 配置文件
    LoadBootScripts();

    // 7. 进入主循环 (Epoll 监听事件)
    am.Loop(); 
    
    return 0;
}

硬件视角:此时 CPU 运行在用户态(EL0),但拥有最高权限(CAP_SYS_ADMIN)。它开始操作文件系统节点,与硬件驱动(通过 /sys/dev)进行交互。


第二步:SELinux 的“铁笼” —— 安全策略加载

在 Android 10+ 中,SELinux 是强制开启的。init 必须在启动其他服务前加载策略,否则后续操作会被内核拦截。

代码位置system/core/init/selinux.cpp

// 文件:system/core/init/selinux.cpp
void SecurityInitialize() {
    // 1. 加载 sepolicy 二进制文件
    if (!selinux_android_load_policy()) {
        fatal("Failed to load sepolicy");
    }

    // 2. 将当前上下文设置为 init 域
    setcon("u:r:init:s0");

    // 3. 设置 enforce 模式
    security_setenforce(1); 
}

作用:从此之后,任何进程访问文件、Socket 或执行系统调用,都必须经过 SELinux 策略的检查。这是 Android 安全的基石。


第三步:属性服务 —— 全局共享内存的诞生

Android 有一个独特的机制:System Properties。这些属性存储在共享内存中,所有进程都可读取,但只有 init 和特定服务可写入。

代码位置system/core/init/property_service.cpp

// 文件:system/core/init/property_service.cpp
void PropertyInit() {
    // 1. 创建共享内存区域 (/dev/__properties__)
    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
    
    // 2. 映射共享内存
    property_area = CreatePropertyArea();

    // 3. 加载默认属性
    LoadPropertiesFromFiles();

    // 4. 启动属性服务器 Socket
    start_property_service();
}

伪代码:属性读取流程

// 任何进程读取属性时,直接映射这块共享内存
const char* prop_get(const char* name) {
    // 计算 hash -> 查找共享内存中的 offset -> 返回字符串
    // 无需系统调用,速度极快!
    return lookup_in_shared_mem(name); 
}

硬件视角:这块共享内存位于物理 RAM 中,被映射到所有进程的虚拟地址空间。它是内核与用户空间、进程与进程之间最高效的通信通道之一。


第四步:解析 .rc 脚本 —— Action 与 Service

init 的核心工作是解析 .rc 文件。这些文件定义了Action(动作)和Service(服务)。

.rc 语法示例 (init.rc)

# 定义一个服务
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system

# 定义一个动作
on boot
    start surfaceflinger
    start servicemanager
    write /proc/sys/kernel/panic_on_oops 1

执行逻辑

  1. 解析所有 .rc 文件,构建 Service 列表和 Action 列表。
  2. 触发 on boot 动作,按顺序执行队列中的命令。
  3. 当遇到 start zygote 时,init 调用 fork() + exec() 启动 Zygote。

第五步:启动 Zygote —— Java 世界的摇篮

Zygote 是 Android 最关键的进程,它预加载了 Java 框架和资源。所有 App 进程都是它的副本。

时序图:从 init 到 Zygote

tongyi-mermaid-2026-03-11-235048.png

代码位置system/core/init/service.cpp

// 文件:system/core/init/service.cpp
Result<void> Service::Start() {
    pid_t pid = fork();
    if (pid == 0) {
        // 子进程:设置 UID/GID, Capabilities, SELinux context
        SetUidGid();
        SetSelinuxContext();
        
        // 执行真正的二进制文件
        execvp(args_[0], (char**) args_.c_str());
        exit(-1); 
    }
    // 父进程 (init): 记录 PID
    pid_ = pid;
    state_ = SVC_RUNNING;
    return {};
}

第六步:Epoll 主循环 —— 永不停歇的守护者

启动完关键服务后,init 并没有退出,而是进入了一个无限循环,监听各种事件。

代码位置system/core/init/epoll.cpp

// 文件:system/core/init/epoll.cpp
void ActionManager::Loop() {
    while (true) {
        // 1. 等待事件 (Socket, 文件变化, 信号)
        int nr_events = epoll_wait(epoll_fd_, events_, EPOLL_MAX_EVENTS, timeout);

        for (int i = 0; i < nr_events; i++) {
            if (events_[i].data.fd == property_fd_) {
                HandlePropertySet(); // 处理属性修改
            } else if (IsServiceFd(events_[i].data.fd)) {
                ReapService(); // 某个服务退出了
            }
        }
        // 2. 执行待处理的 Action 队列
        ExecuteActions();
    }
}

本篇小结

模块核心作用
硬件引导供电、DDR 初始化、加载 Kernel
入口挂载文件系统,初始化环境
安全加载 SELinux 策略,开启沙箱
属性创建共享内存,提供全局配置读写
解析器解析 .rc 脚本,构建 Action/Service 树
服务管理Fork/Exec 启动 Zygote 等核心服务
主循环监听事件,守护系统运行

硬件与系统状态变化

  • 内存:共享内存区域被分配,供全系统读取属性。
  • 进程树init (PID 1) -> Zygote -> SystemServer
  • 权限:SELinux 生效,系统从“裸奔”进入“受控”状态。

下篇预告

Zygote 启动了,SystemServer 也跑起来了。但这只是 Java 世界的开始。
敬请期待第二篇:《Java 世界的“创世神”:Zygote 如何一秒孵化一个 App?》