Android系统启动流程深度解析:从Bootloader到Zygote的完整旅程

引言

每次按下电源键,你的Android设备经历了什么?

从一片黑屏到看到开机Logo,从显示"Android"字样到进入锁屏界面,这短短几秒钟(或十几秒钟)背后,是一场精心编排的"启动大戏"。这场大戏有四幕:

makefile 复制代码
第一幕: Bootloader  →  加载并启动Kernel
第二幕: Kernel      →  初始化硬件,启动init进程(PID 1)
第三幕: init        →  挂载文件系统,启动核心服务
第四幕: Zygote      →  准备应用运行环境

本文将带你完整走完这段旅程,重点聚焦第二幕到第三幕------从Kernel启动init,到init启动Zygote之前的所有准备工作。

📖 系列前置阅读:建议先阅读第9-11篇(Binder IPC系列),理解进程间通信机制,这是理解系统服务启动的基础。


Android启动流程全景图

在深入细节之前,先建立整体认知:

图:Android 15系统启动的4个阶段,从Bootloader到Zygote的完整旅程,总耗时约6.5秒

本文聚焦范围 :从Kernel启动init到Zygote启动之前,即上图中的第3步:init进程


Bootloader与Kernel启动(概览)

虽然不是本文重点,但理解这两个阶段有助于建立完整认知。

Bootloader阶段

Bootloader(引导加载程序)是设备上电后执行的第一段代码,由芯片厂商或设备厂商提供。

主要任务:

  1. 硬件初始化:

    • CPU频率和电压配置
    • 内存初始化(DDR)
    • 外设初始化(Flash/eMMC/UFS)
  2. 加载Kernel:

    • 从boot分区读取Kernel镜像(boot.img)
    • 解压Kernel到内存
    • 读取Device Tree Blob(DTB,设备树)
  3. 安全验证:

    • Verified Boot(Android Verified Boot):验证Kernel签名
    • 如果验证失败,显示警告或拒绝启动
  4. 启动Kernel:

    • 设置Kernel启动参数(cmdline)
    • 跳转到Kernel入口地址(通常是0x80008000)

常见Bootloader:

  • LK(Little Kernel):Qualcomm芯片常用
  • U-Boot:开源,通用性强
  • UEFI:PC和部分高端设备

关键日志(通过串口或fastboot查看):

less 复制代码
[0.000000] Booting Linux on physical CPU 0x0
[0.000000] Linux version 5.15.123-android15 (build@hostname) ...

Kernel启动阶段

Linux Kernel负责硬件抽象和资源管理。

主要任务:

  1. 内存管理初始化:

    • 页表设置
    • 内存分配器(SLAB/SLUB)初始化
  2. 进程调度器初始化:

    • CFS调度器(Completely Fair Scheduler)
    • 实时调度器(RT)
  3. 驱动初始化:

    • 加载内置驱动(built-in)
    • 初始化设备树(Device Tree)中定义的硬件
  4. 文件系统初始化:

    • 挂载rootfs(根文件系统,通常是ramdisk)
    • 挂载/system、/vendor等分区(由init负责)
  5. 创建init进程:

    c 复制代码
    // kernel/init/main.c (简化)
    static int __ref kernel_init(void *unused) {
        // ...
        if (!ramdisk_execute_command)
            ramdisk_execute_command = "/init";
    
        return run_init_process(ramdisk_execute_command);
    }

关键Kernel参数(cmdline):

bash 复制代码
# 查看Kernel启动参数
adb shell cat /proc/cmdline

# 输出示例:
# androidboot.hardware=qcom
# androidboot.selinux=permissive  # 或 enforcing
# androidboot.verifiedbootstate=green
# init=/system/bin/init

init进程:Android的"创世神"

init进程是Android用户态的第一个进程(PID 1),它的职责是创建整个用户态环境

init的三阶段启动

Android 15的init分为三个阶段,每个阶段有独立的入口函数:

scss 复制代码
main() → FirstStageMain() → SetupSelinux() → SecondStageMain()
   ↓            ↓                  ↓                 ↓
 入口      第一阶段         SELinux设置        第二阶段(核心)

让我们分析Android 15的源码:

阶段判断逻辑

cpp 复制代码
// system/core/init/main.cpp (Android 15)
int main(int argc, char** argv) {
    // 提升优先级(后续会恢复)
    setpriority(PRIO_PROCESS, 0, -20);

    // 如果argv[0]是"ueventd",则以ueventd模式启动
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }

    if (argc > 1) {
        // 子上下文模式(用于隔离某些操作)
        if (!strcmp(argv[1], "subcontext")) {
            return SubcontextMain(argc, argv, &GetBuiltinFunctionMap());
        }

        // SELinux设置阶段
        if (!strcmp(argv[1], "selinux_setup")) {
            return SetupSelinux(argv);
        }

        // 第二阶段
        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);
        }
    }

    // 默认进入第一阶段
    return FirstStageMain(argc, argv);
}

关键设计:

  • init通过命令行参数判断当前应执行哪个阶段
  • 每个阶段完成后,会通过execv()重新执行自己,进入下一阶段
  • 这种设计允许在SELinux加载后"重启"进程,获得新的安全上下文

第一阶段:FirstStageMain()

核心目标:创建最基础的文件系统,加载SELinux策略。

cpp 复制代码
// system/core/init/first_stage_init.cpp (简化)
int FirstStageMain(int argc, char** argv) {
    // ========== 1. 挂载基础文件系统 ==========
    CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
    CHECKCALL(mkdir("/dev/pts", 0755));
    CHECKCALL(mkdir("/dev/socket", 0755));
    CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));

    // 挂载proc文件系统(读取内核信息)
    CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=3009"));

    // 挂载sysfs(设备和驱动信息)
    CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));

    // 挂载selinuxfs(SELinux策略文件系统)
    CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));

    // ========== 2. 创建必要的设备节点 ==========
    CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
    CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));  // 内核日志
    CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
    CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));

    // ========== 3. 初始化内核日志 ==========
    android::base::InitLogging(argv, &android::base::KernelLogger);

    LOG(INFO) << "init first stage started!";

    // ========== 4. 挂载early-mount分区 ==========
    // 这些分区包含boot阶段需要的关键文件
    auto fstab = ReadFirstStageFstab();
    if (!DoFirstStageMount(fstab)) {
        LOG(FATAL) << "Failed to mount early-mount partitions";
    }

    // ========== 5. 准备切换到SELinux设置阶段 ==========
    setenv("INIT_SECOND_STAGE", "true", 1);

    // 重新执行自己,进入selinux_setup阶段
    const char* path = "/system/bin/init";
    const char* args[] = {path, "selinux_setup", nullptr};
    execv(path, const_cast<char**>(args));

    // execv不会返回,除非失败
    PLOG(FATAL) << "execv(\"" << path << "\") failed";
    return 1;
}

关键点解析:

  1. 为什么要挂载tmpfs到/dev?

    • tmpfs是内存文件系统,读写速度快
    • /dev目录存放设备节点,启动阶段频繁访问
    • 使用内存可以避免等待存储设备初始化
  2. hidepid=2是什么?

    • /proc挂载选项,隐藏其他用户的进程信息
    • 提升安全性,防止进程窥探
  3. early-mount分区包含什么?

    • /system:系统分区,包含init二进制文件
    • /vendor:厂商分区,包含硬件相关库
    • /odm:OEM/ODM定制分区
  4. 为什么使用execv重新执行?

    • SELinux策略加载后,进程需要新的安全上下文
    • execv会替换当前进程,但保持PID不变(仍然是PID 1)

SELinux设置阶段:SetupSelinux()

核心目标:加载SELinux策略,初始化安全框架。

cpp 复制代码
// system/core/init/selinux.cpp (简化)
int SetupSelinux(char** argv) {
    // ========== 1. 加载SELinux策略文件 ==========
    // 策略文件位置:
    // - /system/etc/selinux/plat_sepolicy.cil
    // - /vendor/etc/selinux/vendor_sepolicy.cil
    LOG(INFO) << "Loading SELinux policy";

    if (!LoadPolicy()) {
        LOG(FATAL) << "Unable to load SELinux policy";
    }

    // ========== 2. 设置SELinux为Enforcing模式 ==========
    // 根据Kernel参数决定:
    // - enforcing: 强制模式,违规操作会被拒绝
    // - permissive: 宽松模式,只记录违规但不阻止
    bool is_enforcing = IsEnforcing();
    if (is_enforcing) {
        selinux_status_open(true);
        if (security_setenforce(1) != 0) {
            LOG(FATAL) << "Failed to set SELinux enforcement";
        }
    }

    // ========== 3. 设置init进程的SELinux上下文 ==========
    if (selinux_android_setcontext(getpid(), true, nullptr, nullptr) != 0) {
        LOG(FATAL) << "Failed to setcon for init";
    }

    // ========== 4. 重新执行init,进入第二阶段 ==========
    const char* path = "/system/bin/init";
    const char* args[] = {path, "second_stage", nullptr};
    execv(path, const_cast<char**>(args));

    PLOG(FATAL) << "execv(\"" << path << "\") failed";
    return 1;
}

SELinux核心概念:

scss 复制代码
Type Enforcement (类型强制)
    ↓
每个进程和文件都有一个"安全标签"(Security Context)
    ↓
示例: u:r:init:s0
      ↑ ↑  ↑   ↑
      │ │  │   └─ MLS/MCS等级
      │ │  └───── Type(init类型)
      │ └──────── Role(角色)
      └────────── User(用户)

SELinux策略文件示例:

perl 复制代码
# /system/etc/selinux/plat_sepolicy.cil (简化)

# 允许init进程创建socket
(allow init self (unix_stream_socket (create bind listen accept)))

# 允许init挂载文件系统
(allow init kernel (system (module_load)))
(allow init tmpfs (filesystem (mount)))

# 允许init执行系统二进制文件
(allow init system_file (file (read execute)))

为什么SELinux对Android很重要?

  • 即使进程以root权限运行,也受SELinux约束
  • 防止恶意应用利用漏洞提权
  • Android 15强制所有设备启用SELinux Enforcing模式

第二阶段:SecondStageMain()------核心重点!

这是init最关键的阶段,负责启动整个Android系统。

cpp 复制代码
// system/core/init/init.cpp (Android 15,简化约350行)
int SecondStageMain(int argc, char** argv) {
    LOG(INFO) << "init second stage started!";

    // ========== 1. 初始化属性系统 ==========
    // 属性系统是Android的"全局配置中心"
    PropertyInit();

    // 创建属性服务的Socket
    // 其他进程通过这个Socket与init通信,读写属性
    StartPropertyService(&property_fd);

    // ========== 2. 解析init.rc配置文件 ==========
    // init.rc定义了所有需要启动的服务和执行的动作
    Parser parser;
    parser.AddSectionParser("service", std::make_unique<ServiceParser>(
        &ServiceList::GetInstance(), nullptr));
    parser.AddSectionParser("on", std::make_unique<ActionParser>(
        &ActionManager::GetInstance(), nullptr));
    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));

    // 解析主配置文件
    if (!parser.ParseConfig("/system/etc/init/init.rc")) {
        LOG(FATAL) << "Failed to parse init.rc";
    }

    // 解析vendor和odm的init片段
    parser.ParseConfig("/vendor/etc/init");
    parser.ParseConfig("/odm/etc/init");

    // ========== 3. 设置Action触发器 ==========
    ActionManager& am = ActionManager::GetInstance();

    // 触发early-init阶段
    am.QueueEventTrigger("early-init");

    // 创建基础目录
    am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");

    // 触发init阶段
    am.QueueEventTrigger("init");

    // ========== 4. 启动ueventd(设备管理守护进程) ==========
    // ueventd监听内核的uevent事件,动态创建/删除设备节点
    am.QueueBuiltinAction(StartUeventd, "StartUeventd");

    // ========== 5. 初始化SELinux完整策略 ==========
    am.QueueBuiltinAction(SelinuxInitMount, "SelinuxInitMount");

    // ========== 6. 触发late-init阶段 ==========
    // 这个阶段会启动Zygote等关键服务
    am.QueueEventTrigger("late-init");

    // ========== 7. 进入事件循环 ==========
    // init不会退出,一直监听以下事件:
    // - 子进程退出(SIGCHLD信号)
    // - 属性变更
    // - init.rc中定义的触发器
    Epoll epoll;
    if (!epoll.Open()) {
        PLOG(FATAL) << "Failed to open epoll";
    }

    // 注册信号处理器
    InstallSignalFdHandler(&epoll);
    InstallInitNotifier(&epoll);  // 属性变更通知

    // 启动所有Action
    while (true) {
        // 执行待执行的Action
        if (!(waiting_for_prop || Service::is_exec_service_running())) {
            am.ExecuteOneCommand();
        }

        // 重启已崩溃的关键服务
        restart_processes();

        // 等待事件(超时时间取决于待执行任务)
        auto pending_functions = epoll.Wait(epoll_timeout);
        if (!pending_functions.ok()) {
            LOG(ERROR) << pending_functions.error();
        } else {
            for (const auto& function : *pending_functions) {
                (*function)();
            }
        }

        // 处理子进程退出事件
        ReapAnyOutstandingChildren();
    }

    return 0;
}

关键函数解析:

属性系统初始化

cpp 复制代码
// system/core/init/property_service.cpp
void PropertyInit() {
    // 创建共享内存区域(/dev/__properties__)
    // 所有进程映射同一块内存,实现高效的属性读取
    if (__system_property_area_init()) {
        LOG(FATAL) << "Failed to initialize property area";
    }

    // 加载默认属性
    // - /system/build.prop
    // - /vendor/build.prop
    // - /odm/build.prop
    load_properties_from_file("/system/build.prop", nullptr);
    load_properties_from_file("/vendor/build.prop", nullptr);
}

void StartPropertyService(int* property_fd) {
    // 创建Unix Domain Socket: /dev/socket/property_service
    *property_fd = create_socket("property_service", SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                 0666, 0, 0, nullptr);

    listen(*property_fd, 8);

    // 注册到epoll,当有属性读写请求时处理
    register_epoll_handler(*property_fd, handle_property_set_fd);
}

属性系统内存布局:

sql 复制代码
/dev/__properties__ (共享内存)
    ↓
┌────────────────────────────────────┐
│  Header                            │
│  - Version                         │
│  - Num Entries                     │
├────────────────────────────────────┤
│  Property Entry 1                  │
│  - Name: "ro.build.version.sdk"    │
│  - Value: "35"                     │
│  - Offset: 0x1000                  │
├────────────────────────────────────┤
│  Property Entry 2                  │
│  - Name: "persist.sys.timezone"    │
│  - Value: "Asia/Shanghai"          │
│  - Offset: 0x1100                  │
├────────────────────────────────────┤
│  ...                               │
└────────────────────────────────────┘

所有进程通过mmap映射此共享内存
→ 读取属性: 直接内存访问,无需IPC
→ 写入属性: 必须通过Socket发送到init

init.rc配置文件详解

init.rc是init的"剧本",定义了启动流程的每一步。

init.rc语法结构

init.rc使用Android Init Language(AIL),包含三种类型:

  1. Service(服务定义)
  2. Action(动作序列)
  3. Import(导入其他配置)
bash 复制代码
# /system/etc/init/init.rc (Android 15片段)

# ==================== 导入其他配置 ====================
import /system/etc/init/hw/init.${ro.hardware}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /system/etc/init/init.zygote64_32.rc

# ==================== 定义early-init阶段 ====================
on early-init
    # 启动ueventd
    start ueventd

    # 创建cgroup挂载点
    mkdir /dev/cpuctl
    mount cgroup none /dev/cpuctl cpu
    chown system system /dev/cpuctl
    chmod 0660 /dev/cpuctl/tasks

    # 设置内存管理器
    write /proc/sys/vm/overcommit_memory 1
    write /proc/sys/vm/min_free_order_shift 4

# ==================== 定义init阶段 ====================
on init
    # 创建基础目录
    mkdir /system
    mkdir /data 0771 system system
    mkdir /cache 0770 system cache
    mkdir /config 0500 root root

    # 挂载tmpfs到/mnt
    mount tmpfs tmpfs /mnt mode=0755,uid=0,gid=1000

    # 启动logd(日志守护进程)
    start logd

    # 设置全局环境变量
    export ANDROID_ROOT /system
    export ANDROID_DATA /data
    export EXTERNAL_STORAGE /sdcard

# ==================== 定义late-init阶段 ====================
on late-init
    # 触发zygote启动
    trigger zygote-start

# 定义zygote-start触发器
on zygote-start
    # 启动Zygote(根据设备位数选择不同配置)
    start zygote

# ==================== Zygote服务定义 ====================
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    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 restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks

# ==================== logd服务定义 ====================
service logd /system/bin/logd
    class core
    user logd
    group logd system readproc
    writepid /dev/cpuset/system-background/tasks
    socket logd stream 0666 logd logd
    socket logdr seqpacket 0666 logd logd
    socket logdw dgram 0222 logd logd
    file /proc/kmsg r
    file /dev/kmsg w

Service语法详解

xml 复制代码
service <name> <pathname> [ <argument> ]*
    <option>
    <option>
    ...

常用选项:

选项 说明 示例
class <name> 服务类别(用于批量控制) class main
user <username> 运行用户 user system
group <groupname> 运行用户组 group system audio
onrestart <command> 重启时执行的命令 onrestart restart media
socket <name> <type> <perm> 创建Socket socket zygote stream 660 root system
critical 关键服务(崩溃4次将重启系统) critical
disabled 默认不启动(需手动start) disabled
oneshot 只运行一次(不自动重启) oneshot
writepid <file> 将PID写入文件 writepid /dev/cpuset/foreground/tasks

Service类别:

kotlin 复制代码
class main       # 主要服务(Zygote, SurfaceFlinger等)
class core       # 核心服务(logd, healthd等)
class hal        # HAL服务
class late_start # 延迟启动服务

批量控制:

bash 复制代码
# 启动所有main类别的服务
class_start main

# 停止所有core类别的服务
class_stop core

Action语法详解

xml 复制代码
on <trigger> [&& <trigger>]*
    <command>
    <command>
    ...

触发器类型:

  1. 事件触发器: on early-init on init on late-init on boot on property:sys.boot_completed=1

  2. 属性触发器: on property:persist.sys.usb.config=mtp,adb setprop sys.usb.state ${persist.sys.usb.config}

常用命令:

命令 说明 示例
mkdir <path> [mode] [owner] [group] 创建目录 mkdir /data/misc 0771 system misc
chmod <octal-mode> <path> 修改权限 chmod 0660 /sys/power/state
chown <owner> <group> <path> 修改所有者 chown system system /dev/cpuctl
mount <type> <device> <path> <flags> <options> 挂载文件系统 mount ext4 /dev/block/by-name/system /system
write <path> <string> 写入文件 write /proc/sys/vm/overcommit_memory 1
setprop <name> <value> 设置属性 setprop ro.debuggable 0
start <service> 启动服务 start zygote
stop <service> 停止服务 stop zygote
restart <service> 重启服务 restart media

属性系统(Property System)深度解析

属性系统是Android的"全局键值数据库",贯穿整个系统生命周期。

属性的作用

ini 复制代码
1. 系统配置
   ro.build.version.sdk = 35          (SDK版本)
   ro.build.version.release = 15      (Android版本)
   ro.product.manufacturer = Google   (制造商)

2. 运行时状态
   sys.boot_completed = 1             (启动完成标志)
   init.svc.zygote = running          (服务状态)
   sys.usb.state = mtp,adb            (USB连接状态)

3. 调试开关
   persist.sys.usb.config = adb       (USB调试)
   ro.debuggable = 1                  (可调试)
   log.tag.MyApp = DEBUG              (日志级别)

4. 触发器
   persist.sys.timezone = Asia/Shanghai  (修改后触发时区更新)
   sys.powerctl = reboot                 (触发重启)

属性类型

根据前缀划分:

前缀 可变性 说明 示例
ro.* 只读 启动时设置,之后不可修改 ro.build.version.sdk
persist.* 持久 修改后写入/data/property,重启后保留 persist.sys.timezone
sys.* 临时 运行时状态,重启后丢失 sys.boot_completed
init.svc.* 只读 服务状态(由init自动管理) init.svc.zygote
ctl.* 触发 写入后触发服务控制(start/stop/restart) ctl.start=zygote

属性读写权限

权限由/system/etc/selinux/plat_property_contexts定义:

shell 复制代码
# /system/etc/selinux/plat_property_contexts (简化)

# 只有init可以设置ro.*属性
ro.                    u:object_r:default_prop:s0

# 只有system_server可以设置sys.*属性
sys.                   u:object_r:system_prop:s0

# 任何进程可以读取,只有特定进程可以写入persist.*
persist.               u:object_r:persistent_properties_ready_prop:s0
persist.sys.timezone   u:object_r:system_prop:s0

# debug.*属性只在userdebug/eng版本可写
debug.                 u:object_r:debug_prop:s0

属性读写API

Java层(frameworks/base):

java 复制代码
import android.os.SystemProperties;

// 读取属性
String sdkVersion = SystemProperties.get("ro.build.version.sdk", "0");
boolean bootCompleted = SystemProperties.getBoolean("sys.boot_completed", false);

// 写入属性(需要权限)
SystemProperties.set("persist.sys.timezone", "Asia/Shanghai");

Native层(system/core/libcutils):

cpp 复制代码
#include <cutils/properties.h>

// 读取属性
char value[PROPERTY_VALUE_MAX];
property_get("ro.build.version.sdk", value, "0");

// 写入属性
property_set("sys.boot_completed", "1");

Shell命令:

bash 复制代码
# 读取单个属性
adb shell getprop ro.build.version.sdk

# 读取所有属性
adb shell getprop

# 写入属性(需要root权限)
adb shell setprop persist.sys.usb.config adb

# 监听属性变化
adb shell watchprops

启动性能优化(Android 15改进)

Android 15在启动性能上有显著优化。

并行化启动

传统启动(串行):

csharp 复制代码
init → ueventd → logd → servicemanager → ...
  ↓      ↓        ↓         ↓
1000ms 500ms   300ms     400ms

总时间: 2200ms

Android 15并行启动:

scss 复制代码
          ┌→ ueventd (500ms)
          ├→ logd (300ms)
init →    ├→ servicemanager (400ms)
(1000ms)  ├→ healthd (200ms)
          └→ lmkd (300ms)
              ↓
         最长耗时服务: ueventd (500ms)

总时间: 1000ms + 500ms = 1500ms
节省: 700ms (32%)

实现方式:

bash 复制代码
# init.rc (Android 15)
on init
    # 并行启动核心服务
    start ueventd
    start logd
    start servicemanager
    start healthd
    start lmkd

    # 等待关键服务就绪
    wait /dev/socket/logd
    wait /dev/socket/property_service

冷启动时间对比

设备 Android 14 Android 15 提升
Pixel 8 18.2s 15.1s 17%
Pixel 8 Pro 17.5s 14.8s 15%
通用设备(平均) 20-25s 16-20s 20%

优化技术:

  1. 延迟加载非关键服务:

    • 将不影响开机的服务延后到late_start类别
  2. 压缩SELinux策略:

    • 使用LZMA压缩,减少加载时间
  3. 优化属性系统:

    • 使用Trie树加速属性查找
    • 减少属性文件解析时间

启动流程调试技巧

1. 查看init日志

bash 复制代码
# 方法1: 通过logcat查看
adb logcat -b all | grep init

# 方法2: 读取init日志文件
adb shell cat /dev/kmsg | grep init

# 方法3: 使用dmesg(Kernel消息)
adb shell dmesg | grep init

# 输出示例:
# [    1.234567] init: init first stage started!
# [    1.567890] init: Loading SELinux policy
# [    2.123456] init: init second stage started!
# [    2.456789] init: processing action (early-init)

2. 使用Bootchart分析启动耗时

Bootchart是Linux的启动性能分析工具,Android已集成。

bash 复制代码
# 1. 启用bootchart
adb shell setprop persist.debug.bootchart.enabled 1
adb reboot

# 2. 等待设备启动完成,导出数据
adb pull /data/bootchart bootchart/
cd bootchart/

# 3. 生成图表(需要bootchart工具)
bootchart bootchart.tgz

# 输出: bootchart.png (时间轴图表)

bootchart.png示例解读:

csharp 复制代码
时间轴(横轴) →
    0s     1s     2s     3s     4s     5s
────┴──────┴──────┴──────┴──────┴──────┴────
init        ████████████████████████████████
ueventd              ██████████
logd                    ████
servicemanager             ████████
zygote                            ██████████████████████
system_server                           ██████████████████████████

关键指标:

  • init阶段: 0-2s (目标小于2s)
  • Zygote启动: 2-4s (目标小于2s)
  • SystemServer就绪: 4-6s (目标小于3s)
  • 系统启动完成: 6-8s (目标小于7s)

3. 检查服务状态

bash 复制代码
# 查看所有服务状态
adb shell getprop | grep "init.svc"

# 输出示例:
# [init.svc.adbd]: [running]
# [init.svc.audioserver]: [running]
# [init.svc.bootanim]: [stopped]
# [init.svc.zygote]: [running]

# 查看特定服务
adb shell getprop init.svc.zygote
# 输出: running

# 手动启动/停止服务
adb shell start zygote
adb shell stop zygote
adb shell restart zygote

4. 分析init.rc解析错误

bash 复制代码
# 如果init.rc有语法错误,会记录在日志中
adb logcat -s init:E

# 输出示例:
# E init    : Could not import file '/system/etc/init/missing.rc' from '/system/etc/init/init.rc'
# E init    : Invalid service name 'my service' (contains space)
# E init    : Unknown command 'invalid_command'

常见问题与解答

Q1: 为什么init需要三个阶段?

A : 主要是为了SELinux加载

makefile 复制代码
FirstStageMain:
  - 挂载基础文件系统
  - 此时SELinux尚未加载
    ↓
SetupSelinux:
  - 加载SELinux策略
  - 设置init的安全上下文
  - execv()重启自己
    ↓
SecondStageMain:
  - 现在init运行在正确的SELinux上下文中
  - 可以安全地启动其他服务

如果不重启,init会运行在错误的安全上下文中,后续启动的所有进程都会受影响。

Q2: 属性名称有长度限制吗?

A: 有!

cpp 复制代码
// system/core/libcutils/include/cutils/properties.h
#define PROP_NAME_MAX   32    // 属性名最大32字符
#define PROP_VALUE_MAX  92    // 属性值最大92字符

如果超出限制:

bash 复制代码
adb shell setprop my.very.long.property.name.that.exceeds.the.limit 1
# 报错: Failed to set property 'my.very.long...' (name too long)

Q3: 为什么有些属性以persist.开头?

A : persist.*属性会持久化存储/data/property/目录:

bash 复制代码
adb shell ls /data/property/
# 输出:
# persistent_properties
# persist.sys.timezone
# persist.sys.usb.config

重启后,init会重新加载这些文件,恢复属性值:

cpp 复制代码
// system/core/init/property_service.cpp
void LoadPersistentProperties() {
    DIR* dir = opendir("/data/property");
    while ((entry = readdir(dir)) != nullptr) {
        if (startswith(entry->d_name, "persist.")) {
            LoadPersistentPropertyFile(entry->d_name);
        }
    }
}

Q4: init崩溃会发生什么?

A : 如果init(PID 1)崩溃,整个系统会重启到Bootloader

scss 复制代码
init进程(PID 1)是内核启动的第一个用户态进程
    ↓
如果PID 1退出,内核认为用户态已无法运行
    ↓
内核执行kernel_panic()
    ↓
系统重启(通常进入Recovery或Bootloader)

防护措施:

  • init代码经过严格审查
  • 关键操作有FATAL检查
  • 崩溃时会记录tombstone到/data/tombstones/
bash 复制代码
# 查看init崩溃日志(如果有)
adb shell ls /data/tombstones/tombstone_init*

Q5: 如何添加自定义启动脚本?

A : 在/vendor/etc/init//odm/etc/init/目录添加自定义.rc文件:

bash 复制代码
# vendor/etc/init/my_custom_service.rc
service my_service /vendor/bin/my_daemon
    class late_start
    user system
    group system
    oneshot

on property:sys.boot_completed=1
    start my_service

最佳实践:

  • 不要修改系统的/system/etc/init/init.rc
  • 使用vendor或odm分区的init片段
  • 服务命名使用厂商前缀(避免冲突)

总结

本文深入分析了Android系统从Bootloader到Zygote启动前的完整流程,重点聚焦init进程的三阶段启动。

核心要点回顾:

  1. 启动链路:

    • Bootloader加载Kernel
    • Kernel启动init进程(PID 1)
    • init分三阶段:FirstStage → SELinux设置 → SecondStage
  2. init的核心职责:

    • 挂载文件系统
    • 初始化SELinux
    • 启动属性系统
    • 解析init.rc配置
    • 启动系统服务(包括Zygote)
  3. 属性系统:

    • 全局键值数据库
    • 通过共享内存高效访问
    • 权限由SELinux控制
    • 支持触发器机制
  4. init.rc配置:

    • 定义服务(Service)和动作(Action)
    • 通过触发器控制启动流程
    • 支持分片配置(vendor/odm)
  5. Android 15优化:

    • 并行化启动核心服务
    • 冷启动时间减少15-20%
    • 优化SELinux策略加载

下一篇预告

第13篇: Zygote进程孵化与应用启动机制

在下一篇中,我们将深入探索:

  • Zygote的fork机制和COW(写时复制)
  • 预加载Class、Resource、Library的细节
  • SystemServer启动与100+系统服务初始化
  • 应用进程创建的完整流程
  • 进程优先级(adj)管理和LMK(Low Memory Killer)

从init完成"开天辟地",到Zygote开始"繁衍生息",Android系统的启动之旅还在继续! 🚀


参考资料

系列文章


本文基于Android 15 (API Level 35)源码分析,不同厂商的定制ROM可能存在差异。 欢迎来我中的个人主页找到更多有用的知识和有趣的产品

相关推荐
Kapaseker3 小时前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
黄林晴3 小时前
你的 Android App 还没接 AI?Gemini API 接入全攻略
android
恋猫de小郭14 小时前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter
冬奇Lab15 小时前
PowerManagerService(上):电源状态与WakeLock管理
android·源码阅读
BoomHe20 小时前
Now in Android 架构模式全面分析
android·android jetpack
二流小码农1 天前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos
鹏程十八少1 天前
4.Android 30分钟手写一个简单版shadow, 从零理解shadow插件化零反射插件化原理
android·前端·面试
Kapaseker1 天前
一杯美式搞定 Kotlin 空安全
android·kotlin
三少爷的鞋1 天前
Android 协程时代,Handler 应该退休了吗?
android
火柴就是我2 天前
让我们实现一个更好看的内部阴影按钮
android·flutter