2. Android系统架构概览
Android 并不是一个“从零重写”的操作系统,而是在 Linux 内核之上叠加了一整套面向移动设备的运行时、系统服务与应用框架。理解它的分层结构,是后续做内核裁剪、HAL 适配、设备树移植的前提。很多适配问题之所以“看起来在应用层,根因却在内核或设备树”,正是因为开发者没有建立清晰的分层调用链认知。
一、五层架构的核心原理
从下往上,Android 系统可以划分为五个主要层次,每一层只通过明确定义的接口向上提供服务:
- Linux 内核层:负责进程调度、内存管理、文件系统、网络协议栈、电源管理,以及最关键的设备驱动。显示、触摸、音频、摄像头、Wi-Fi、传感器等硬件能力,最终都由内核驱动暴露为设备节点或子系统接口。
- HAL 层(硬件抽象层):以一组标准接口(如
hardware/libhardware定义的模块)屏蔽不同芯片与板级的差异。Framework 只调用 HAL 接口,不直接操作驱动节点,从而实现“同一套 Android 跑在不同硬件上”。 - Native 层:由 C/C++ 实现的库与守护进程,包括 Bionic libc、SurfaceFlinger、AudioFlinger、MediaCodec、OpenGL ES/Vulkan 等,是性能敏感与图形音视频处理的主战场。
- Framework 层(Java API 框架):以 Java/Kotlin 实现,提供 ActivityManager、WindowManager、PackageManager、SensorManager 等系统服务,是 App 直接调用的 API 来源。
- App 层:系统应用与第三方应用,运行在 ART 虚拟机之上,通过 Binder IPC 与系统服务交互。
分层的关键价值在于“接口稳定、实现可替换”。设备树正是把“硬件描述”从内核代码中解耦出来的机制,它让同一份内核镜像可以适配不同板级硬件,而无需为每块板子重新编译内核。
二、设备树在架构中的位置
设备树(Device Tree,DT)位于Linux 内核层的最底层入口。Bootloader 在启动内核时,把编译好的 .dtb 文件地址传给内核,内核在启动早期解析它,据此创建 platform_device、注册 i2c_client、配置引脚复用与时钟,并完成驱动与设备的匹配(match)。
因此设备树并不“属于”HAL 或 Framework,但它的描述结果会一路向上传导:设备树里写错 I2C 地址,HAL 就读不到传感器;设备树里漏配显示时序,SurfaceFlinger 就无法正常合成画面。这条链路可以用下面的信号与依赖关系图表示。
三、一次“传感器读取”的完整调用链
以读取加速度传感器为例,可以清楚看到各层如何协作,以及设备树在何处介入:
- App 调用
SensorManager.getDefaultSensor()与registerListener()。 - Framework 的
SensorService通过 Binder 接收请求,并转发给 Native 层的sensors HAL。 - HAL 实现(如
android.hardware.sensors@2.1-service)打开/dev/iio:device0或对应字符设备。 - 内核 IIO 驱动从 I2C 总线读取芯片寄存器,而该 I2C 设备的地址、中断引脚、供电 regulator 全部来自设备树节点。
- 数据沿原路返回,最终回调到 App 的
onSensorChanged()。
// 设备树中描述一个 I2C 加速度计(示意)
&i2c3 {
status = "okay";
clock-frequency = <400000>;
accel@68 {
compatible = "bosch,bmi160";
reg = <0x68>;
interrupt-parent = <&gpio1>;
interrupts = <12 IRQ_TYPE_EDGE_RISING>;
vdd-supply = <&vdd_1v8>;
};
};
如果 reg = <0x68> 写错,内核不会报“应用崩溃”,而是驱动 probe 失败,HAL 打开设备失败,最终表现为 App 收不到传感器数据。这就是典型的“上层现象、底层根因”。
四、各层职责与设备树关联度对比
| 层次 | 主要语言/形态 | 核心职责 | 与设备树的关联 |
|---|---|---|---|
| App 层 | Java/Kotlin | 业务逻辑与界面 | 无直接关联 |
| Framework 层 | Java + JNI | 系统服务与 API | 无直接关联 |
| Native 层 | C/C++ | 图形、音视频、运行时 | 间接(通过 HAL) |
| HAL 层 | C/C++ 模块 | 屏蔽硬件差异 | 间接(依赖驱动节点) |
| Linux 内核层 | C + 汇编 | 驱动、调度、内存 | 直接解析与使用 |
| 设备树 | DTS/DTB | 描述板级硬件 | 内核启动入口 |
五、常见误区与复盘要点
误区一:把设备树当成“配置文件”随意改。设备树节点必须与驱动中的
误区二:认为 HAL 可以绕过内核直接访问硬件。除极少数用户态驱动外,HAL 仍通过内核暴露的设备节点工作,设备树错误会直接传导到 HAL。
误区三:只改 Framework 层“打补丁”。若根因在设备树或驱动,上层补丁只会掩盖问题,换一块板子立刻复现。
compatible、reg、中断类型严格匹配,否则 probe 静默失败。误区二:认为 HAL 可以绕过内核直接访问硬件。除极少数用户态驱动外,HAL 仍通过内核暴露的设备节点工作,设备树错误会直接传导到 HAL。
误区三:只改 Framework 层“打补丁”。若根因在设备树或驱动,上层补丁只会掩盖问题,换一块板子立刻复现。
复盘方法:遇到硬件功能异常时,按“App → Framework → HAL → 内核驱动 → 设备树”自下而上或自上而下逐层验证。常用命令包括
dmesg | grep -i probe 查看驱动匹配、ls /sys/firmware/devicetree/base/ 查看运行时设备树、cat /proc/device-tree/... 核对节点属性。