Featured image of post 安卓启动流程浅析

安卓启动流程浅析

安卓启动流程

先总的从下面这个图中来学习一下安卓的启动流程吧

Bootloader 引导阶段

当按下电源键后,芯片从固化在 ROM(Read-Only Memory)中预设的(BOOT ROM)开始执行,这个阶段是纯硬件操作,代码负责加载引导程序 BootLoader 到 RAM(Random Access Memory)中
BootLoader 是操作系统运行之前的一小段程序,类似于 PC 上的 BIOS,主要功能大致有:

  • 第一,检测并初始化硬件设备:如外部 RAM,时钟,主板等,为内核运行准备一个基本的硬件环境
  • 第二,拉起 Linux 内核:将内核映像加载到指定的内存地址,然后将系统控制权交给内核
  • 第三,安全验证:检查内核的数字签名(厂商通常会锁定 Bootloader)

Linux 内核启动阶段

安卓系统主要是运行再 Linux 内核之上的,在这里主要可以将在内核阶段做的事情分成 3 个阶段:

0 号进程:swapper(创世神)

这是整个 Android 系统的起点

  • 诞生:它是整个 Android 系统中唯一不被 fork 出来的进程,由内核代码直接创建的
  • 核心任务(初始化):
    • 执行 start_kernel():这个是内核执行的第一个 C 语言函数,他会初始化中断处理,内存页表映射,进程调度策略等
    • 点亮关键硬件:在这一阶段,内核会根据设备树(Device Tree)加载最核心的驱动:显示(Display),相机(Camera)以及 Android 的通信命脉–Binder 驱动
  • 结果:当 1 号进程和 2 号进程启动后,0 号进程就会变成 idle 进程(0 号进程的任务已经完成了,接下来就是 1 号和 2 号进程的事情了,但是又不能杀死 0 号进程,这个时候就把 0 号进程变成 idle 进程,变成低功耗状态)

2 号进程:kthreadd(内核态鼻祖)

在 0 号进程执行的过程中,它会调用 rest_init() 来孵化出 2 号进程

  • 身份:它是所有内核线程的鼻祖
  • 核心任务:
    • 运行在内核态:它负责管理那些不需要用户参与,直接在后台与硬件打交道的任务
    • 孵化线程:
      • kworker:内核的"勤杂工"(奴隶),处理各种异步 IO 任务
      • ksoftirqd:专门负责处理软中断,保证系统在高负载下的相应速度
      • thermal:温度守护进程,负责实时监控 CPU 温度并执行强制降频,防止手机被烧坏

注:什么是软中断呢?那硬中断是什么意思呢?
硬中断:当硬件(如网卡,触摸屏)产生信号时,CPU 会立即停下手里的活取处理,这就叫做硬中断

  • 特点:优先级最高,执行时会屏蔽其他中断
  • 问题:如果硬中断处理时间太长,系统就会卡死,因为它优先级高,没法干其他事情,为了解决这个问题,Linux 就把中断处理分成了两半
    • 上半部分:硬中断:
      • 只做最紧急的事情,干完赶紧走
    • 下半部分:软中断
      • 处理耗时其不那么紧急的事情,可以被抢占,不影响系统接收新的硬中断
        因为硬中断是立即做的事情,软中断延后,如果在处理硬中断任务的时候,接收到了很多的软中断任务,软中断任务越堆越多,这个时候 ksoftirqd 线程就是专门用来管理这些软中断任务的

1 号进程:init(用户态鼻祖)

这个是 0 号进程孵化出来的另一个进程

  • 身份:所有用户空间进程的鼻祖
  • 核心任务:
    • 权限切换:内核通过 run_init_process 试图在根分区寻找 init 文件,一旦找到,代码执行权立即从用户态(Kernel Space)切换到用户态(User Space)
    • 解析 init.rc:进入用户态后, init 进程会化身为一个"配置解析器",它会根据脚本里的指令,一个接一个地启动我们在桌面上能看到的那些服务

init 进程初始化阶段

init 进程的三个阶段

第一阶段:FirstStageMain 基础挂载
内核刚刚把控制权交给 init 进程,此时系统还很简陋,init 需要做一些工作

  • 挂载文件系统:挂载 /dev, /proc, /sys 等虚拟文件系统,让进程能够访问硬件设备和内核信息。
  • 加载 First Stage Console:初始化早期的日志输出,这样如果出错了,开发者才能够在串口看到 log 下面来详细解释一下 /dev,/proc,/sys 这三个虚拟文件系统:
  • /dev: 设备驱动的入口
    • 作用:在 Linux 中,“万物皆文件”,我们常常访问手机的屏幕,摄像头或是 Binder,都得通过这个目录
    • 在启动中的角色:init 进程会在这里创建设备的节点。比如,没有 /dev/binder 这个文件,Android 的所有进程通信都得瘫痪;没有 /dev/graphics/fb0,屏幕就没法显示图像
  • /proc:内核与进程信息的实时快照
    • 作用:它让我们能"窥探"内核当前的运行状态
    • 常见操作:
      • /proc/cpuinfo:查看 CPU 有几个核
      • /proc/meminfo:查看当前内存还剩多少
      • /proc/[pid]/stack:查看某个进程现在的调用栈
  • /sys:硬件设备树的结构化体现
    • 作用:如果说 /dev 是为了设备的读写数据,那 /sys 就是为了修改配置
    • 在启动中的角色:init 会通过往 /sys 里的文件写"1"或"0"来控制硬件
      • /sys/class/leds/red/brightness 写一个数字,手机的呼吸灯就亮了
      • 控制 CPU 的频率,调节屏幕亮度,都是通过这个目录来完成的 第二阶段:SetupSelinux 安全加固
        Android 是极其安全的系统,这一步是给系统"上锁"
  • 加载 SELinux 策略:定义那些进程可以访问哪些文件。
  • 权限转交:一旦策略加载完成,init 就会重新执行自己,进入受安全保护的第二阶段 第三阶段:SecondStageMain 配置解析
    这是我们最常讨论的 init 核心逻辑:
  • 初始化属性服务:启动 Property Service,可以把它理解为"全局变量数据库"
    • Q1:它是做什么的?
    • A1:在安卓系统中,很多配置信息 (比如手机型号,系统版本,当前网络状态)需要在不同的进程之间共享
      • 存储键值对:它以 key=value 的形式存储信息(例如: ro.product.model=pixel 6)
      • 跨进程访问:无论是在 C++层还是 Java 层,所有的进程都可以读取这些属性,从而了解系统当前的状况
    • Q2:它是如何工作的?
    • A2:属性服务在 init 进程的第二阶段(SecondStageMain)启动。
      • 内存共享:init 进程会开辟一段特殊的共享内存(称为 __system_property_area__)来存放这些属性
      • 权限控制:
        • 读取:任何进程都可以读取属性。
        • 写入:普通进程不能直接修改共享内存。如果一个进程想修改属性,必须向 init 进程发送请求,由 init 检查 SELinux 权限后代为修改。
    • Q3:这些属性是如何命名的呢?
    • A3:在 Android 中,属性的名字是有特殊含义的
      • ro.(Read-Only):只读属性,一旦设定,除非重启系统,否则无法修改。例如: ro.build.version.release (系统版本)
      • persist.:持久化属性。修改后会存入硬盘,下次开机依然有效。例如: persist.sys.timezone (用户设置的时区)
      • ctl(Control):控制属性。用于启动或停止服务,例如: setprop ctl.start zygote (让 init 启动 zygote)
      • 其他:临时属性,关机即消失,例如: gsm.operator.alpha (运营商名称)
  • 解析 init.rc:这是 init 的灵魂,它会根据脚本指令去拉起所有系统服务

核心任务:解析 init.rc 脚本

1.什么是.rc 文件
rc 代表 Run Commands(运行命令) 。这是一种从 Unix 时代传承下来的习惯,专门用于存放系统启动时需要自动执行的脚本。在 Android 中,它采用了一种特殊的文本格式,易于阅读但也非常严谨
2.init.rc 的四大核心组成部分

  • Actions
    • 语法:以 on 开头,后跟一个 Trigger(触发器)
    • 例子: on booton early-init
    • 理解:它定义了"什么时候干活"。当系统状态达到触发器要求时,它下面的命令就会被依次执行
  • Commands
    • Action 下面缩进的一行行指令
    • 例子: mount 挂载, chmod 改权限, mkdir 建文件夹, write 往文件写值
    • 理解:它定义了"具体干什么活"
  • Services
    • 语法:以 service 开头,后跟服务名和执行程序路径
    • 例子: service zygote /system/bin/cpp/app_process...
    • 理解:它定义了"谁来干活",这些服务通常是常驻后台的守护进程
  • Options
    • 语法: Service 下面所进的修饰符
    • 例子:
      • class main:把服务归类到"主服务"
      • user system:以系统用户身份运行
      • critical:如果这个服务挂了,系统必须立刻重启进入 Recovery
      • onrestart:当服务重启时,顺便把别的服务也重启了 3.解析的流程
        在源码中, init 并不是简单地从头读到尾,它有一个解析=>排序=>执行的过程
  • 解析文件: init 进程会递归读取 /system/etc/init/, /vendor/etc/init/ 等目录下所有的 .rc 文件,并将他们加载到内存
  • 分装 Action:将所有的 Action 按照触发器的时间顺序排好队
  • 启动 Service:当某个 Action 执行了 class_start main 命令时,属于 main 类的所有服务(如 zygote)才会排队启动

Zygote 进程启动阶段

Q1:为什么叫做 Zygote(受精卵)?

在 Android 中,几乎所有的应用程序进程以及 SystemServer 进程,都是由 Zygote 进程 fork 出来的
为什么要 fork 呢?
如果每个 APP 启动都要重新加载一遍基础类库和资源,手机会卡死,而如果 Zygote 预先加载好了这些东西,新进程只需要"复制"一份,效率贼高

Q2:Zygote 是如何被拉起来的?

在之前分析的过程中,init 进程解析到 init.rc 文件的时候,会有一个语句 service zygote /system/bin/app_process ...

  • App_main.cpp 启动:首先运行的是 C++编写的 app_process 程序
  • 创建 AndroidRuntime:它会启动 Android 运行时环境
  • 启动虚拟机(ART/Dalvik):这是关键的一步, Zygote 亲手打开了 Java 虚拟机

Q3:Zygote 启动后干了啥?

当启动后,它主要会干一下的几件事情
1.预加载资源
Zygote 会把上千个常用的 Java 类(如 Activity, view)和系统资源(如图标,主题)加载到内存里

2.启动 SystemServer
这是 Zygote 孵化的第一个孩子
地位: SystemServer 进程是整个 Android 框架的重心,管理着 ActivityManagerService(AMS),WindowManagerService(WMS) 等核心服务
关系:没有这个孩子,Android 只是一个空的 Java 环境,没有任何手机功能
下面总结了一些常见的服务:

服务名称 简称 核心职责 例子
ActivityManagerService AMS 管理组件生命周期(启动,停止)、进程调度、权限审批 当你从"微信"跳转到"朋友圈"时,是它在切换界面并保持后台运行
WindowManagerService WMS 管理窗口位置,大小,层级(谁在上面),转场动画 当你开启"小窗模式"或者看到 App 打开时的缩放动画时,是它在计算位置
PackageManagerService PKMS 管理 App 安装,卸载,解析 AndroidManifet.xml,权限检查 当你安装一个 APK 时,是它在检查报名是否冲突,签名是否合法
PowerManagerService PMS 管理屏幕亮度,待机状态,电源策略(省电模式) 当你半分钟不操作,屏幕慢慢变暗,是它在下达指令
InputManagerService IMS 接收触摸屏,物理按键的原始信号,并配合 WMS 进行分发 当你点击屏幕上的"确认"按钮,是它最先捕捉到信号并判断你点击的是哪个地方

3.建立 Socket
Zygote 会创建一个 LocalServerSocket
它会从此进入死循环,开始待命。当 AMS 想要启动一个新的 App 时,会通过这个 Socket 给 Zygote 发消息,Zygote 收到消息之后,会立即 fork 出一个新的进程给 App 使用

SystemServer 阶段

SystemServer 启动后,它的 run() 方法会像一个精密的流水线,依次点名启动 80 多个 系统服务。这些服务被归类为三大步:

引导服务

首先会启动系统运行必不可少的底层服务

  • ActivityManagerService(AMS):管理四大组件和进程
  • PackageManagerService(PKMS):解析所有已安装的 App 信息
  • PowerManagerService(PMS):电源管理

核心服务

这些服务虽然不是第一优先级的,但是后续的所有交互都会依赖他们

  • BatteryService:监控电量
  • UsageStatsService:统计应用使用情况

其他服务

启动各种与交互相关的复杂服务

  • WMS (WindowManagerService):管理屏幕上的窗口。
  • IMS (InputManagerService):处理触摸和按键。
  • 蓝牙、Wifi、振动服务等。

所有服务启动完毕后,调用 ActivityManagerService.systemReady(),标志着系统核心准备就绪。此后,AMS 会发送 BOOT_COMPLETED 广播,并启动桌面 Launcher,完成开机。

Launcher 启动阶段

当所有服务都已经初始化完成了,最后就会启动 launcher 了,也就是平常的桌面 app,当在启动这个桌面 app 的过程中,依然会走一遍 App 启动的标准流程:

  • AMS 决策:AMS 发现桌面进程还没启动,于是再次求助 Zygote
  • Zygote 孵化:Zygote 收到指令, fork 出一个新的进程给 Launcher
  • 加载页面:Launcher 进程启动后,开始加载手机上所有安装好的 app 图标
  • WMS 渲染:Launcher 告诉 WMS,“我需要一个空间”,WMS 分配好空间,画面呈现

至此,安卓启动完成

参考文章

Android 操作系统架构开篇 - Gityuan博客 | 袁辉辉的技术博客