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可能存在差异。 欢迎来我中的个人主页找到更多有用的知识和有趣的产品

相关推荐
泓博8 小时前
Android中仿照View selector自定义Compose Button
android·vue.js·elementui
zhangphil9 小时前
Android性能分析中trace上到的postAndWait
android
十里-9 小时前
vue2的web项目打包成安卓apk包
android·前端
p***19949 小时前
MySQL——内置函数
android·数据库·mysql
兆子龙10 小时前
我成了🤡, 因为不想看广告,花了40美元自己写了个鸡肋挂机脚本
android·javascript
儿歌八万首12 小时前
Android 全局监听神器:registerActivityLifecycleCallbacks 解析
android·kotlin·activity
弹幕教练宇宙起源12 小时前
cmake文件介绍及用法
android·linux·c++
&岁月不待人&12 小时前
一个Android高级开发的2025总结 【个人总结无大话】
android
吴声子夜歌13 小时前
RxJava——FlowableProcessor详解
android·echarts·rxjava