Android全栈9 分钟阅读4235 字
【全栈第19课】Android 内核特性(binder/dma-buf/lmkd)
Android 全栈工程师进阶教程 第19课。Android 内核特性(binder/dma-buf/lmkd)。基于公开 AOSP 知识整理,含原理、可编译示例、常见问题定位。
> 本文是《Android 全栈工程师进阶教程》系列第 19 课。完整 26 课见 GitHub 仓库。
【全栈第19课】Android 内核特性(binder/dma-buf/lmkd)
0. 这节课你将搞懂什么
Android 在标准 Linux 内核上加了哪些关键东西,以及它们和你日常问题的关系。 学完你能回答:Android 内核和服务器/PC 上的 Linux 内核有什么不一样?L1 的 Binder 在内核里长什么样?为什么某后台 App 会被系统杀掉?为什么有的 App 持锁不放导致待机掉电? 这一课是阶段三的"内核增量地图",呼应 L1。
1. 背景:Android 内核 = Linux + 一堆增量
Android 内核本质是 Linux 内核,但 Google 为了移动场景加了不少东西。理解这些增量,你才能判断一个问题是"标准 Linux 行为"还是"Android 特有行为",从而知道去哪查、怎么修。
2. 全局图:Android 内核增量
┌──────────── Android 内核 = 标准 Linux + 增量 ────────────┐
│ Binder 驱动 ← L1/L2 所有 IXxxManager 跨进程的内核底座 │
│ dma-buf/ION ← 图形/相机大内存零拷贝共享 │
│ lmkd + PSI/memcg ← 低内存杀进程(多实例被杀的元凶) │
│ wakelock ← 防系统休眠(待机功耗,L18/L25) │
│ cgroup ← 前台/后台进程分组调度限制 │
│ ashmem/memfd ← 匿名共享内存 │
│ SCHED_FIFO ← GPU 关键线程实时调度(新版本 要求) │
└─────────────────────────────────────────────────────────┘
3. 逐个讲(配合你日常)
3.1 Binder 驱动(呼应 L1)
- 设备节点
/dev/binder,驱动在drivers/android/binder.c。 - L1 学的
IPCThreadState->transact最终就是ioctl(/dev/binder, BINDER_WRITE_READ)。 - 内核做的事:一次拷贝(发送方 copy_from_user 到内核 mmap 区,接收方 mmap 直接映射,省一次)、盖身份戳(从 task_struct 取真实 uid/pid,这就是
getCallingUid的来源)、线程池、死亡通知、引用计数。 - 和你的关系:示例项目 里所有反射调
IXxxManager的内核底座就是它。binder 泄漏/线程耗尽会导致系统级问题。
3.2 dma-buf / ION
- 跨进程/跨设备零拷贝共享大块内存(显示 buffer、相机帧)。
- GPU 渲染出一帧 → 用 dma-buf fd 传给 SurfaceFlinger 显示,不拷贝整个 buffer(只传 fd)。
- ION 是老分配器(A12 前),现在用 dma-buf heaps。
- 和你的关系:触控/手写笔渲染相关的大 buffer 走这个。
3.3 lmkd + PSI + memcg(低内存杀手)
- 内存不足时杀进程释放内存。
- 老式:内核
lowmemorykiller(已移除)。现代:用户态 lmkd + 内核 PSI(压力失速信息) + memcg(内存 cgroup)。 - 新版本 要求 memcg v2(见 新版本 总结)。
- 和你的关系:示例项目 多实例的 App 后台被杀,常常是 lmkd 干的。查 lmkd 日志能看到为什么杀、杀了谁。
3.4 wakelock / wakeup source
- 防止系统进入休眠。某进程持 wakelock,系统就睡不下去。
- 和你的关系:某应用入口 或某后台 App 持
PARTIAL_WAKE_LOCK不放 → 待机掉电快(L18/L25)。
3.5 cgroup / SCHED_FIFO
- cgroup:把进程分组,限制 CPU/内存(前台 App 给多 CPU,后台限制)。
- SCHED_FIFO:实时调度策略,新版本 要求 GPU 关键路径线程用它(见 新版本 总结)。
4. 前置准备
- 依赖 L1(Binder)、L18(wakelock)。
- 本课偏理解,产出一份内核增量地图文档。
5. 动手:在设备上观察这些机制
不写代码,用命令观察真实系统里这些机制的状态:
# Binder
adb shell ls -l /dev/binder* # binder 设备节点
adb shell cat /sys/kernel/debug/binder/stats # binder 统计(事务数等)
# dma-buf
adb shell cat /sys/kernel/debug/dma_buf/bufinfo # dma-buf 分配情况
# lmkd
adb shell dumpsys activity lru # 进程 LRU(谁容易被杀)
adb logcat | grep lmkd # lmkd 杀进程日志
# wakelock
adb shell cat /sys/power/wake_lock # 谁持锁
adb shell cat /d/wakeup_sources # wakeup source 活跃情况
# cgroup
adb shell cat /dev/cpuset/foreground/tasks # 前台进程组
6. 看底层:这些增量怎么"嵌"进标准 Linux
- Binder/ashmem 是字符设备驱动(L14 那套 file_operations),只是实现了 Android 特有的 ioctl。
- lmkd 是用户态进程,通过读 PSI(
/proc/pressure/memory)和 memcg 来决策,通过/proc/<pid>/oom_score_adj给进程打分。 - wakelock 是内核 PM 框架的一部分(L18 的电源管理)。 所以它们不是"另一个内核",而是标准 Linux 机制(驱动/cgroup/PM)的 Android 定制。
7. 编译 & 运行(本课无编译产物)
见第 5 节命令,在真机上观察。 > 验证状态(诚实):本课为概念/观察课,无编译产物。命令为标准 Android 调试命令。
8. 踩坑提醒
- 把 Android 特有行为当标准 Linux 查:比如进程莫名被杀,在标准 Linux 思路里找不到,其实是 lmkd(Android 特有)。
- binder 调试节点路径:
/sys/kernel/debug/binder/需要 debugfs 挂载 + root,有的量产机关了。 - wakelock 名字看不懂:wakeup_sources 里的名字是各驱动/服务自己起的,要对照代码找是谁。
- lmkd 杀进程不是 bug:内存紧张时杀后台是正常机制,除非杀了不该杀的(前台/关键服务)。
9. 常见问题分析与定位(Android 内核机制实战)
9.1 常见问题清单
- App(尤其多实例/后台)莫名被杀
- 待机掉电快(系统睡不下去)
- binder 相关系统卡顿/ANR
- 大内存(图形/相机)相关 OOM
- 后台进程 CPU 被限制导致慢
9.2 分析与定位
① App 被杀(lmkd)
adb logcat | grep -iE "lmkd|lowmemory|kill" # lmkd 杀了谁、为什么
adb shell dumpsys activity lru # 进程优先级,oom_score_adj 高的先被杀
adb shell cat /proc/<pid>/oom_score_adj # 这个进程的"可杀分"
- 看 lmkd 日志的 reason(内存压力级别)和被杀进程的 oom_score_adj;某后台 App 后台分数高容易被杀。修复:提优先级/保活机制(示例项目 多实例保活)。
② 待机掉电(睡不下去) — 见 L18/L25:cat /d/wakeup_sources 找谁持着不放。
③ binder 卡顿/ANR
adb shell cat /sys/kernel/debug/binder/stats # 事务积压?
adb shell cat /sys/kernel/debug/binder/transactions # 当前事务
- binder 线程池耗尽、某服务处理慢导致调用方排队(接 L1 问题4)。
④ 大内存 OOM
adb shell cat /sys/kernel/debug/dma_buf/bufinfo # dma-buf 占了多少
adb shell dumpsys meminfo # 整体内存
- 图形/相机 buffer 泄漏。
⑤ 后台进程慢 — cgroup 限制。cat /dev/cpuset/background/tasks 看是不是被放后台组限了 CPU。
9.3 定位决策树
Android 内核机制问题
├─ App 被杀 → logcat lmkd + dumpsys activity lru + oom_score_adj → 提优先级/保活
├─ 待机掉电 → /d/wakeup_sources 谁持锁(L18/L25)
├─ binder 卡/ANR → /sys/kernel/debug/binder/stats 事务积压 → 线程池/对端慢(L1)
├─ 大内存 OOM → dma_buf/bufinfo + meminfo → 图形/相机 buffer 泄漏
└─ 后台慢 → cpuset/background 看 cgroup 限制
10. 小结
- Android 内核 = 标准 Linux + 增量(binder/dma-buf/lmkd/wakelock/cgroup)。
- Binder 驱动是 L1/L2 所有跨进程的内核底座(一次拷贝+盖 uid 戳)。
- lmkd 是某后台 App 被杀的元凶;wakelock 是待机掉电的元凶。
- 这些增量本质是标准 Linux 机制(驱动/cgroup/PM)的 Android 定制。
- 排查口诀:被杀查 lmkd+oom_score_adj;掉电查 wakeup_sources;binder 卡查 binder/stats;大内存查 dma_buf。
下节预告
L20:驱动→HAL→App 全链路 bring-up —— 把 L9-L18 串成完整竖线,一颗假 sensor 从 DTS 到 App(阶段三收官)。