AI 技术博客
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. 踩坑提醒

  1. 把 Android 特有行为当标准 Linux 查:比如进程莫名被杀,在标准 Linux 思路里找不到,其实是 lmkd(Android 特有)。
  2. binder 调试节点路径:/sys/kernel/debug/binder/ 需要 debugfs 挂载 + root,有的量产机关了。
  3. wakelock 名字看不懂:wakeup_sources 里的名字是各驱动/服务自己起的,要对照代码找是谁。
  4. lmkd 杀进程不是 bug:内存紧张时杀后台是正常机制,除非杀了不该杀的(前台/关键服务)。

9. 常见问题分析与定位(Android 内核机制实战)

9.1 常见问题清单

  1. App(尤其多实例/后台)莫名被杀
  2. 待机掉电快(系统睡不下去)
  3. binder 相关系统卡顿/ANR
  4. 大内存(图形/相机)相关 OOM
  5. 后台进程 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. 小结

  1. Android 内核 = 标准 Linux + 增量(binder/dma-buf/lmkd/wakelock/cgroup)。
  2. Binder 驱动是 L1/L2 所有跨进程的内核底座(一次拷贝+盖 uid 戳)。
  3. lmkd 是某后台 App 被杀的元凶;wakelock 是待机掉电的元凶
  4. 这些增量本质是标准 Linux 机制(驱动/cgroup/PM)的 Android 定制。
  5. 排查口诀:被杀查 lmkd+oom_score_adj;掉电查 wakeup_sources;binder 卡查 binder/stats;大内存查 dma_buf。

下节预告

L20:驱动→HAL→App 全链路 bring-up —— 把 L9-L18 串成完整竖线,一颗假 sensor 从 DTS 到 App(阶段三收官)。

评论