Android系统启动流程之lmkd进程详解
Android系统启动流程之lmkd进程详解
本文详细介绍了Android系统中lmkd进程的启动流程、工作原理以及其在系统资源紧张时如何选择和杀死进程。文章内容深入且技术性强,涉及多个关键组件和机制,如PSI、vmpressure、lowmemorykiller等,并通过代码示例和详细解释帮助读者理解复杂的系统内部运作。
lmkd进程创建
当CPU、IO、内存资源出现紧张的时候,需要lmkd从所有进程中把一些不重要、不干活但又占据大量内存的用户空间进程杀掉,以释放公共资源供大家使用。
启动过程
lmkd进程的启动过程如下:
- 在
/system/core/rootdir/init.rc
文件中,通过on init
触发器启动lmkd init.rc
文件中引用了lmkd.rc
配置lmkd.rc
文件定义了lmkd服务的相关配置- 最终执行
/system/bin/lmkd
可执行程序,调用lmkd.cpp
中的main()
方法
代码示例
// 文件路径:/system/core/rootdir/init.rc
on init
write /proc/sys/vm/watermark_boost_factor 0
chown root system /sys/module/lowmemorykiller/parameters/adj
chmod 0664 /sys/module/lowmemorykiller/parameters/adj
chown root system /sys/module/lowmemorykiller/parameters/minfree
chmod 0664 /sys/module/lowmemorykiller/parameters/minfree
start lmkd
// 文件路径:/system/memory/lmkd/Android.bp
cc_binary {
name: "lmkd",
init_rc: ["lmkd.rc"],
}
// 文件路径:/system/memory/lmkd/lmkd.rc
service lmkd /system/bin/lmkd
class core
user lmkd
group lmkd system readproc
capabilities DAC_OVERRIDE KILL IPC_LOCK SYS_NICE SYS_RESOURCE
critical
socket lmkd seqpacket+passcred 0660 system system
task_profiles ServiceCapacityLow
// 文件路径:system/memory/lmkd/lmkd.cpp
int main(int argc, char **argv) {
if (!init()) {
if (init_reaper()) {
ALOGI("Process reaper initialized with %d threads in the pool", reaper.thread_cnt());
}
if (!watchdog.init()) {
ALOGE("Failed to initialize the watchdog");
}
mainloop();
}
android_log_destroy(&ctx);
ALOGI("exiting");
return 0;
}
何时去杀进程
lmkd进程本身无权直接检测系统资源使用情况,因此需要借助其他工具来监听内核空间的资源使用情况。在Android高版本中,主要使用PSI(Pressure Stall Information)来监控CPU、内存及IO性能异常。
PSI监控机制
PSI是一种可以监控CPU、内存及IO性能异常的内核功能。lmkd通过PSI来监听系统资源紧张情况,并在资源紧张时触发进程杀戮机制。
代码示例
// 文件路径:system/memory/lmkd/lmkd.cpp
static int init(void) {
if (use_inkernel_interface) {
// 使用内核接口
} else {
if (!init_monitors()) {
return -1;
}
property_set("sys.lmk.reportkills", "1");
}
return 0;
}
static bool init_monitors() {
use_psi_monitors = GET_LMK_PROPERTY(bool, "use_psi", true) && init_psi_monitors();
if (!use_psi_monitors && (!init_mp_common(VMPRESS_LEVEL_LOW) || !init_mp_common(VMPRESS_LEVEL_MEDIUM) || !init_mp_common(VMPRESS_LEVEL_CRITICAL))) {
ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
return false;
}
return true;
}
static bool init_psi_monitors() {
bool use_new_strategy = GET_LMK_PROPERTY(bool, "use_new_strategy", low_ram_device || !use_minfree_levels);
if (!use_new_strategy && memcg_version() != MemcgVersion::kV1) {
ALOGE("Old kill strategy can only be used with v1 cgroup hierarchy");
return false;
}
if (use_new_strategy) {
psi_thresholds[VMPRESS_LEVEL_LOW].threshold_ms = 0;
psi_thresholds[VMPRESS_LEVEL_MEDIUM].threshold_ms = psi_partial_stall_ms;
psi_thresholds[VMPRESS_LEVEL_CRITICAL].threshold_ms = psi_complete_stall_ms;
}
if (!init_mp_psi(VMPRESS_LEVEL_LOW, use_new_strategy)) {
return false;
}
if (!init_mp_psi(VMPRESS_LEVEL_MEDIUM, use_new_strategy)) {
destroy_mp_psi(VMPRESS_LEVEL_LOW);
return false;
}
if (!init_mp_psi(VMPRESS_LEVEL_CRITICAL, use_new_strategy)) {
destroy_mp_psi(VMPRESS_LEVEL_MEDIUM);
destroy_mp_psi(VMPRESS_LEVEL_LOW);
return false;
}
return true;
}
static bool init_mp_psi(enum vmpressure_level level, bool use_new_strategy) {
int fd;
if (!psi_thresholds[level].threshold_ms) {
return true;
}
fd = init_psi_monitor(psi_thresholds[level].stall_type, psi_thresholds[level].threshold_ms * US_PER_MS, PSI_WINDOW_SIZE_MS * US_PER_MS);
if (fd < 0) {
return false;
}
vmpressure_hinfo[level].handler = use_new_strategy ? mp_event_psi : mp_event_common;
vmpressure_hinfo[level].data = level;
if (register_psi_monitor(epollfd, fd, &vmpressure_hinfo[level]) < 0) {
destroy_psi_monitor(fd);
return false;
}
maxevents++;
mpevfd[level] = fd;
return true;
}
哪些进程需要杀
lmkd需要收集可能被杀的进程信息,并为杀进程提供目标。这些进程信息通过特定的数据结构存储,并通过特定的通信机制传递给lmkd。
数据结构设计
lmkd使用一个数组procadjslot_list
来存储进程信息,数组的每个元素是一个双向循环链表,链表的每一个节点指向一个进程。进程的分数通过ADJTOSLOT(adj)
算法转换为数组的索引。
// 文件路径:system/memory/lmkd/lmkd.cpp
#define ADJTOSLOT(adj) ((adj) + -OOM_SCORE_ADJ_MIN)
#define ADJTOSLOT_COUNT (ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1)
struct adjslot_list {
struct adjslot_list *next;
struct adjslot_list *prev;
};
struct proc {
struct adjslot_list asl;
int pid;
int pidfd;
uid_t uid;
int oomadj;
pid_t reg_pid;
bool valid;
struct proc *pidhash_next;
};
static struct adjslot_list procadjslot_list[ADJTOSLOT_COUNT];
进程信息传递
进程信息主要由init进程和system_server进程传递给lmkd。init进程负责管理系统native进程,而system_server进程负责管理运行Java代码的进程。它们通过socket通信将进程信息传递给lmkd。
// 文件路径:system/memory/lmkd/lmkd.cpp
static int init(void) {
ctrl_sock.sock = android_get_control_socket("lmkd");
if (ctrl_sock.sock < 0) {
ALOGE("get lmkd control socket failed");
return -1;
}
return 0;
}
数据协议
通信渠道上传递的数据协议非常简单,格式如下:

cmd xxx xxx xxx
其中,LMK_PROCPRIO
用于注册进程并设置其oom_adj_score
,LMK_PROCREMOVE
用于注销进程,LMK_SUBSCRIBE
用于订阅事件。
如何选择和杀死进程
lmkd在收到资源紧张的通知后,会根据进程的分数(oom_adj_score
)来选择需要杀死的进程。分数越高,被杀的优先级越高。
查找和杀死进程
lmkd从最高分数开始查找,直到找到可以杀死的进程。如果所有分数大于等于0的进程都被杀死后,系统资源仍然紧张,lmkd会继续尝试杀死分数更低的进程,但不会杀死分数为负值的进程。
// 文件路径:system/memory/lmkd/lmkd.cpp
static int find_and_kill_process(int min_score_adj, struct kill_info *ki, union meminfo *mi,
struct wakeup_info *wi, struct timespec *tm,
struct psi_data *pd) {
int i;
int killed_size = 0;
bool choose_heaviest_task = kill_heaviest_task;
for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
struct proc *procp;
if (!choose_heaviest_task && i <= PERCEPTIBLE_APP_ADJ) {
choose_heaviest_task = true;
}
while (true) {
procp = choose_heaviest_task ? proc_get_heaviest(i) : proc_adj_tail(i);
if (!procp)
break;
killed_size = kill_one_process(procp, min_score_adj, ki, mi, wi, tm, pd);
if (killed_size >= 0) {
break;
}
}
if (killed_size) {
break;
}
}
return killed_size;
}
min_score_adj的取值
- PSI条件下,一般情况下
min_score_adj
的值是201(PREVIOUS_APP_ADJ + 1
),内存极度紧张时为0 - watchdog条件下,
min_score_adj
的值为0
总结
lmkd进程在Android系统中扮演着重要的角色,负责在系统资源紧张时选择并杀死合适的进程以释放资源。通过PSI监控系统资源使用情况,结合进程的oom_adj_score
分数,lmkd能够智能地选择需要杀死的进程,确保系统稳定运行。