引言
每次按下电源键,你的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(引导加载程序)是设备上电后执行的第一段代码,由芯片厂商或设备厂商提供。
主要任务:
-
硬件初始化:
- CPU频率和电压配置
- 内存初始化(DDR)
- 外设初始化(Flash/eMMC/UFS)
-
加载Kernel:
- 从boot分区读取Kernel镜像(
boot.img) - 解压Kernel到内存
- 读取Device Tree Blob(DTB,设备树)
- 从boot分区读取Kernel镜像(
-
安全验证:
- Verified Boot(Android Verified Boot):验证Kernel签名
- 如果验证失败,显示警告或拒绝启动
-
启动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负责硬件抽象和资源管理。
主要任务:
-
内存管理初始化:
- 页表设置
- 内存分配器(SLAB/SLUB)初始化
-
进程调度器初始化:
- CFS调度器(Completely Fair Scheduler)
- 实时调度器(RT)
-
驱动初始化:
- 加载内置驱动(built-in)
- 初始化设备树(Device Tree)中定义的硬件
-
文件系统初始化:
- 挂载rootfs(根文件系统,通常是ramdisk)
- 挂载/system、/vendor等分区(由init负责)
-
创建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;
}
关键点解析:
-
为什么要挂载tmpfs到/dev?
- tmpfs是内存文件系统,读写速度快
- /dev目录存放设备节点,启动阶段频繁访问
- 使用内存可以避免等待存储设备初始化
-
hidepid=2是什么?
- /proc挂载选项,隐藏其他用户的进程信息
- 提升安全性,防止进程窥探
-
early-mount分区包含什么?
/system:系统分区,包含init二进制文件/vendor:厂商分区,包含硬件相关库/odm:OEM/ODM定制分区
-
为什么使用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),包含三种类型:
- Service(服务定义)
- Action(动作序列)
- 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>
...
触发器类型:
-
事件触发器: on early-init on init on late-init on boot on property:sys.boot_completed=1
-
属性触发器: 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% |
优化技术:
-
延迟加载非关键服务:
- 将不影响开机的服务延后到
late_start类别
- 将不影响开机的服务延后到
-
压缩SELinux策略:
- 使用LZMA压缩,减少加载时间
-
优化属性系统:
- 使用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进程的三阶段启动。
核心要点回顾:
-
启动链路:
- Bootloader加载Kernel
- Kernel启动init进程(PID 1)
- init分三阶段:FirstStage → SELinux设置 → SecondStage
-
init的核心职责:
- 挂载文件系统
- 初始化SELinux
- 启动属性系统
- 解析init.rc配置
- 启动系统服务(包括Zygote)
-
属性系统:
- 全局键值数据库
- 通过共享内存高效访问
- 权限由SELinux控制
- 支持触发器机制
-
init.rc配置:
- 定义服务(Service)和动作(Action)
- 通过触发器控制启动流程
- 支持分片配置(vendor/odm)
-
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可能存在差异。 欢迎来我中的个人主页找到更多有用的知识和有趣的产品