1.1 课程导论与Android内核架构
欢迎进入《Android内核驱动之字符设备驱动开发与ioctl实战》课程。本章作为课程的开篇,将为你构建Android系统软件栈的整体视图,厘清Linux内核与Android内核的关系,并介绍驱动开发的核心概念与环境。理解这些基础是后续编写字符设备驱动、实现ioctl接口的基石。
1.1.1 Android系统软件栈概述
Android系统采用分层架构设计,从上到下依次为应用层、应用框架层、系统运行库层与Linux内核层。每一层都为其上层提供特定的服务与抽象。
核心要点: Android软件栈的分层设计使得上层开发与底层硬件解耦,驱动开发者主要工作在内核层与HAL层之间。
1.1.2 Linux内核与Android内核的关系
Android内核基于Linux内核主线版本,但并非完全等同。Google在Linux内核基础上增加了针对移动设备和Android生态的特定功能与补丁,形成了所谓的“Android内核”或“Common Kernel”。
| 对比维度 | 标准 Linux 内核 | Android 内核 |
|---|---|---|
| 基础来源 | Linus Torvalds 维护的主线内核 | 基于 Linux LTS 分支 + Android 特定补丁 |
| 核心调度器 | CFS (完全公平调度器) | CFS + 额外优化 (如 CPU 调频、组调度) |
| IPC 机制 | 标准 System V IPC, POSIX 消息队列 | 增加 Binder 驱动作为核心 IPC |
| 电源管理 | 标准 ACPI/DT 电源管理 | 增加 wakelocks、early suspend 机制 |
| 内存管理 | 标准 VMA, OOM killer | 增加 Low Memory Killer (LMK) / lmkd |
| 驱动模型 | 标准设备驱动模型 | 增加 Android 特有的 HAL 与驱动接口 |
开发提示: 在Android内核中编写驱动时,除了遵循Linux驱动框架外,还需要注意Android特有的机制,如Binder IPC、wakelock、以及SELinux策略对设备节点的访问控制。
1.1.3 内核空间与用户空间
现代操作系统通过硬件MMU实现内核空间与用户空间的隔离。这是驱动开发必须理解的核心概念。
- 内核空间 (Kernel Space): 操作系统内核运行的特权模式,可以访问所有硬件资源和内存地址。设备驱动运行在内核空间。
- 用户空间 (User Space): 应用程序运行的非特权模式,只能访问受限的内存区域,通过系统调用陷入内核执行特权操作。
- 系统调用 (System Call): 用户空间访问内核服务的唯一入口,如 open(), read(), write(), ioctl() 等。
安全提醒: 驱动运行在内核空间,一个错误的指针操作(如空指针解引用、越界访问)会导致内核崩溃(Kernel Panic),进而引起整个系统重启。开发时必须严格验证来自用户空间的所有数据。
1.1.4 驱动开发环境概览
进行Android内核驱动开发,需要搭建一套完整的交叉编译与部署环境。以下是典型的环境组件与工作流程。
环境组件清单
| 组件 | 说明 | 常见选择 |
|---|---|---|
| 主机操作系统 | 开发编译环境运行的操作系统 | Ubuntu 20.04 / 22.04 LTS |
| 交叉编译工具链 | 用于在x86主机上编译ARM架构内核代码 | aarch64-linux-android- (NDK) 或 gcc-arm-linux-gnueabihf |
| Android内核源码 | 包含所有驱动框架与Android补丁的内核树 | common kernel (android-4.19-stable 等) |
| 目标设备 | 运行Android系统的硬件平台 | Pixel 设备 / 开发板 (如 Hikey, DragonBoard) |
| 调试工具 | 用于查看内核日志、调试驱动 | adb, dmesg, ftrace, kgdb |
典型开发流程
- 获取源码: 从
https://android.googlesource.com/kernel/common克隆对应分支的内核源码。 - 配置内核: 使用
make ARCH=arm64 defconfig或make ARCH=arm64 menuconfig生成并修改内核配置。 - 编写驱动: 在内核源码的
drivers/目录下创建字符设备驱动源码文件。 - 编译驱动: 使用交叉编译工具链编译为内核模块 (
.ko文件) 或直接编译进内核。 - 部署与测试: 通过
adb push将模块推送到设备,使用insmod加载,并通过dmesg查看日志。