用户空间的LMKD

用户空间的LMKD

 

https://www.jianshu.com/p/1ef6e8a1c773

 

原文:https://source.android.com/devices/tech/perf/lmkd

 

本文描述了Android 9中添加的用户空间lowmemorykiller守护程序(lmkd)功能以及如何配置它们。

以前,Android使用内核lowmemorykiller驱动程序终止不必要的进程来缓解内存压力。这种机制死板且依赖于硬编码值,而且从内核4.12开始,lowmemorykiller驱动程序被排除在上游内核之外。

用户空间lmkd进程实现同样的功能,用已有的内核机制来检测和估计内存压力。它使用内核生成的vmpressure事件来获取有关内存压力级别的通知。它还可以使用内存cgroup功能根据其重要性限制分配给每个进程的内存资源。

如何切换到用户空间lmkd

从Android 9开始,如果未检测到内核中的lowmemorykiller驱动程序,则用户空间lmkd会激活。注:用户空间lmkd需要内核支持内存cgroup。因此,要切换到用户空间 lmkd,应配置以下设置编译内核:

CONFIG_ANDROID_LOW_MEMORY_KILLER=n
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y

lmkd终止策略

lmkd同时支持新的和传统模式的终止策略,新的终止策略基于vmpressure事件,进程的重要性以及交换利用率等其他提示;传统模式的lmkd 终止策略就像内核lowmemorykiller 驱动程序那样做出终止决定。

新的终止策略因低内存和高性能设备而异。在低内存设备的情况下,系统应该比正常操作模式承受更高的内存压力; 在高性能设备上,内存压力应被视为异常情况,在影响整体性能之前应予以修复。ro.config.low_ram属性允许选择一种模式而非另一种。有关设置此属性的说明,请参阅低RAM配置

在传统模式下,lmkd根据可用内存和文件缓存阈值进行终止决策。通过将ro.lmk.use_minfree_levels 属性设置为true来启用此模式。

为特定设备配置lmkd

配置lmkd以下属性:

属性使用默认值
ro.config.low_ram在低内存和高性能设备之间进行选择。false
ro.lmk.use_minfree_levels使用空闲内存和文件缓存阈值来决定何时终止。此模式与内核lowmemorykiller驱动程序的工作方式相同。false
ro.lmk.low在低压力水平下有资格杀死的进程的最小oom_adj得分。1001(禁用)
ro.lmk.medium适合在中等压力水平下杀死的进程的最小oom_adj分数。800(缓存或非必要服务)
ro.lmk.critical有资格在关键压力水平下被杀死的进程的最小oom_adj分数。0(任何过程)
ro.lmk.critical_upgrade可以升级到关键级别。false
ro.lmk.upgrade_pressure由于系统交换太多,将升级哪个级别的最大mem_pressure。100(禁用)
ro.lmk.downgrade_pressurevmpressure事件将被忽略的最小mem_pressure *,因为仍有足够的可用内存。100(禁用)
ro.lmk.kill_heaviest_task终止最重要的合格任务(最佳决策)与任何符合条件的任务(快速决策)。true
ro.lmk.kill_timeout_ms终止后的持续时间(毫秒),不会进行额外的终止。0(禁用)
ro.lmk.debug启用lmkd调试日志。false

注: mem_pressure = RAM使用率/ RAM_and_swap使用率%

以下是一个设备配置示例:

PRODUCT_PROPERTY_OVERRIDES += \
    ro.lmk.low=1001 \
    ro.lmk.medium=800 \
    ro.lmk.critical=0 \
    ro.lmk.critical_upgrade=false \
    ro.lmk.upgrade_pressure=100 \
    ro.lmk.downgrade_pressure=100 \
    ro.lmk.kill_heaviest_task=true

 

`lmkd`(**Low Memory Killer Daemon**)是 Android 系统中一个关键的 **用户空间内存管理守护进程**,其主要职责是在系统内存紧张时主动杀死低优先级的后台进程,以释放内存资源、防止触发内核 OOM Killer,从而提升系统的整体响应速度和用户体验。 虽然名字叫 “Low Memory Killer”,但它与内核的 `OOM Killer` 有本质区别。`lmkd` 是一种 **预防性查杀机制**,它在系统真正耗尽内存之前就介入清理。 --- ## 一、lmkd 的作用与定位 | 特性 | 说明 | |------|------| | 运行位置 | 用户空间(`/system/bin/lmkd`) | | 启动方式 | 由 `init` 在启动阶段拉起 | | 工作机制 | 监听 memory cgroup 的压力事件,评估进程重要性,选择性 kill() | | 目标 | 提前回收内存,避免进入 OOM 状态 | > ✅ 核心目标:**永远不让 OOM Killer 被触发** --- ## 二、lmkd 架构图 ```text +---------------------+ | 内存子系统 (memcg) | <---> 持续监控内存使用情况 +----------+----------+ | v +-----------------------+ | lmkd (守护进程) | | - epoll 监听 memcg | | - 计算各进程 adj 值 | | - 按需发送 SIGKILL | +----------+------------+ | v +------------------------+ | Process (App) 被杀死 | | logcat 可见死亡记录 | +------------------------+ ``` --- ## 三、核心组件详解 ### 1. Memory Cgroup(内存控制组) Android 将应用划分到不同的内存控制组中: ```bash # 示例路径 /sys/fs/cgroup/memory/apps/ ├── uid_10079/ │ ├── pid_1234/ │ └── pid_5678/ └── uid_10100/ └── pid_9012/ ``` 每个 group 都可以设置: - `memory.limit_in_bytes`: 内存上限 - `memory.usage_in_bytes`: 当前使用量 - `memory.pressure_level`: 内存压力等级(low/medium/critical) ### 2. Pressure Level 通知机制 通过 `memory.pressure_level` 文件注册监听: ```c // lmkd.cpp int open_pressure_handler(unsigned int nr) { std::string level; switch(nr) { case 1: level = "low"; break; case 2: level = "medium"; break; case 3: level = "critical"; break; } std::string path = "/sys/fs/cgroup/memory/apps/memory.pressure_level"; int fd = open(path.c_str(), O_RDONLY); if (fd >= 0) { write(fd, level.c_str(), level.size()); close(fd); } return fd; } ``` 当系统内存下降到阈值以下时,kernel 会通过 eventfd 发送通知给 `lmkd`。 --- ## 四、查杀策略:什么是 `oom_adj`? Android 引入了扩展的 `oom_score_adj` 概念,称为 **`oom_adj` 或 `min_score_adj`**,表示一个进程被杀的倾向。 | oom_adj 值 | 进程类型 | 是否容易被杀 | |-----------|----------|--------------| | -1000 | native system processes (e.g., `surfaceflinger`) | ❌ 不可杀 | | -900 | system_server, zygote | ⚠️ 很难杀 | | 0 | Foreground App(前台正在使用的 App) | ✅ 一般不杀 | | 100 | Visible App(可见但非前台) | ✅ 可能杀 | | 200~300 | Secondary server | ✅ 易杀 | | 900 | Background App(后台服务) | ✅✅✅ 最优先杀 | | 906 | Cached App(已退出缓存) | ✅✅✅ 极易杀 | > 数值越大,越容易被 `lmkd` 选中杀死。 这些值通常由 `ActivityManagerService` 设置,并写入 `/proc/<pid>/oom_score_adj` --- ## 五、配置文件与参数(Android 10+) lmkd 的行为由系统属性控制: ```bash # 查看当前配置 getprop | grep lmk # 输出示例: [ro.lmk.low]: [1536] # < 6MB free → 触发 low [ro.lmk.medium]: [3072] # < 12MB [ro.lmk.critical]: [4608] # < 18MB [ro.lmk.critical_upgrade]: true [ro.lmk.upgrade_pressure]: 40 [ro.lmk.downgrade_pressure]: 60 [ro.lmk.kill_heaviest_task]: true [ro.lmk.swap_free_low_percentage]: 20 ``` 单位是 **pages(每页 4KB)** 例如:`1536 pages = 1536 × 4KB ≈ 6MB` --- ## 六、lmkd 主要工作流程代码解析 以下是简化版 `lmkd` 的主循环逻辑: ```cpp // lmkd.cpp void handle_event(int timeout) { struct epoll_event events[EPOLL_MAX_EVENTS]; int nevents = epoll_wait(epollfd, events, EPOLL_MAX_EVENTS, timeout); for (int i = 0; i < nevents; ++i) { if (events[i].data.ptr == &pressure_handler) { read_pressure_levels(); // 读取 pressure_level reclaim_memory(); // 开始查杀 } } } void reclaim_memory() { std::vector<Process*> procs = get_running_processes(); sort(procs.begin(), procs.end(), [](Process* a, Process* b) { return a->oom_score_adj > b->oom_score_adj; // 分数高的先杀 }); long freed = 0; long target = get_target_free_kbytes(); for (auto& proc : procs) { if (proc->oom_score_adj < MIN_FREEZABLE_OOM_ADJ) continue; kill(proc->pid, SIGKILL); // 发送致命信号 waitpid(proc->pid, nullptr, 0); freed += proc->rss_in_kb; log("lmkd killed PID=%d (%s), freed %ldKB", proc->pid, proc->name.c_str(), proc->rss_in_kb); if (freed >= target) break; } } ``` --- ## 七、如何查看 lmkd 日志? ### 方法一:使用 `logcat` ```bash logcat | grep -i "lmkd" ``` 输出示例: ```log I lmkd : Kill pid 12345 (com.browser) because cached, min_score_adj=900, rss=5432kB I lmkd : Reclaimed 5432KB, now free=8.2MB ``` ### 方法二:检查 dmesg(如果没有启用 lmkd,则可能看到 OOM) ```bash dmesg | grep -i "out of memory\|kill process" ``` 如果看到这个日志: ```log Out of memory: Kill process 1234 (com.chrome) score 305 ... ``` 👉 说明 `lmkd` 失效或未及时响应,已经触发了 **内核 OOM Killer**! --- ## 八、厂商定制增强(如 Athena、MIUI LMK) 很多手机厂商会对 `lmkd` 进行深度改造,比如: | 厂商 | 定制名称 | 改进点 | |------|---------|--------| | 小米 | Athena / MemoryDaemon | 加入 AI 预测模型判断哪些 App 不会马上用 | | OPPO | AutoKill Manager | 结合用户行为学习冻结策略 | | 华为 | AppControl | 白名单保护微信、钉钉等高频应用 | | vivo | iManager | 图形化展示“被清理”记录 | 这些系统本质上还是基于 `lmkd` 的框架,但加入了更智能的评分引擎。 --- ## 九、常见问题排查 ### Q1: 为什么我的 App 总是被 lmkd 杀死? **原因分析:** - App 处于后台状态(`oom_score_adj ≥ 900`) - 占用了大量内存(RSS 高) - 没有使用前台服务或锁屏白名单 **解决方案:** - 使用 `startForegroundService()` 提升优先级 - 在 `AndroidManifest.xml` 中声明 `<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>` - 加入厂商白名单(如 MIUI 安全中心 → 自启动管理) --- ### Q2: 如何调试 lmkd 行为? #### 步骤 1:获取内存信息 ```bash dumpsys meminfo ``` 查看每个 App 的 PSS、USS 和 oom_adj 值。 #### 步骤 2:模拟内存压力 ```bash # 手动生成内存负载 malloc_stress_test --size=500M --duration=60s ``` 观察是否触发 lmkd 查杀。 #### 步骤 3:修改阈值测试(需 root) ```bash setprop ro.lmk.low 512 # 更激进地查杀 setprop ro.lmk.critical 1024 ``` --- ## 十、总结对比表 | 对比项 | lmkd | OOM Killer | |--------|------|-------------| | 所在层级 | 用户空间 | 内核空间 | | 触发时机 | 内存压力升高时(预防) | 内存完全耗尽(最后手段) | | 决策依据 | oom_score_adj + RSS + 类型 | 主要看内存占用 | | 是否可定制 | ✅ 可通过 prop 修改策略 | ❌ 固定算法 | | 对用户体验影响 | 较小(只杀后台) | 极大(可能导致前台卡死) | | 是否应被触发 | ✅ 正常行为 | ❌ 应尽量避免 | > ✅ **理想状态:lmkd 成功拦截所有内存危机,OOM Killer 从不触发。** ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值