在手机开机的时候,会调用lmkd.rc(system/memory/lmkd)初始化lmkd。
lmkd是系统一个非常重要的服务,开机是由init进程启动,如下所示:system/core/lmkd/lmkd.rc
system/memory/lmkd/lmkd.rc
手机运行,LMKD全程都在。
camera在后台,使用"sys.lmk.minfree_levels" 系统的水线
wj@wj:~/SSD_1T/M1_Stable$ adb shell getprop | grep -aEi "minfree"
[dalvik.vm.heapminfree]: [2m]
[sys.lmk.minfree_levels]: [18432:0,23040:100,27648:200,32256:250,55296:900,80640:950]
camera在前台,使用"persist.sys.lmk.camera_minfree_levels" 相机的水线
当前系统剩余内存小于80640,会查杀oom_score_adj=950及以上的进程
这张图简单地展示了 lmkd 的基本工作流程。
lmkd(Low Memory Killer Daemon)是低内存终止守护进程,用来监控运行中android系统内存的状态,通过终止最不必要的进程来应对内存压力较高的问题,使系统以可接受的水平运行。
Android 版本 |
所处空间 |
杀进程时机 |
8.1 之前 |
kernel |
监听 kswapd 触发的 shrink 回调 |
8.1 - 9.0 |
userspace |
监听 vmpressure |
10 |
userspace |
监听 psi |
wj@wj:~/SSD_1T/M1_Stable$ adb shell
ishtar:/ # cd /proc/pressure/
ishtar:/proc/pressure # cat memory
some avg10=0.00 avg60=0.00 avg300=0.00 total=3083343
full avg10=0.00 avg60=0.00 avg300=0.00 total=2506968
ishtar:/proc/pressure # cat io
some avg10=0.00 avg60=0.01 avg300=0.04 total=38859160
full avg10=0.00 avg60=0.00 avg300=0.00 total=26192413
ishtar:/proc/pressure # cat cpu
some avg10=2.32 avg60=3.10 avg300=5.89 total=3776585362
full avg10=0.00 avg60=0.00 avg300=0.00 total=0
vg10 、avg60 、avg300分别代表 10s、60s、300s 的时间周期内的阻塞时间百分比。total 是总累计时间,以毫秒为单位。
some 这一行,代表至少有一个任务在某个资源上阻塞的时间占比,full 这一行,代表所有的非idle任务同时被阻塞的时间占比.
some : 至少有一个任务在某个资源上阻塞时间占比
full : 所有任务同时阻塞的时间占比
参考:纯干货,PSI 原理解析与应用_psi原理_内核工匠的博客-CSDN博客
在Android中,进程主要分为以下几种:
ADJ级别 |
取值 |
解释 |
UNKNOWN_ADJ |
1001 |
一般指将要会缓存进程,无法获取确定值 |
CACHED_APP_MAX_ADJ |
999 |
不可见进程的adj最大值 |
CACHED_APP_MIN_ADJ |
900 |
不可见进程的adj最小值 |
SERVICE_B_ADJ |
800 |
B List中的Service(较老的、使用可能性更小) |
PREVIOUS_APP_ADJ |
700 |
上一个App的进程(往往通过按返回键) |
HOME_APP_ADJ |
600 |
Home进程 |
SERVICE_ADJ |
500 |
服务进程 |
HEAVY_WEIGHT_APP_ADJ |
400 |
后台的重量级进程,system/rootdir/init.rc文件中设置 |
BACKUP_APP_ADJ |
300 |
备份进程 |
PERCEPTIBLE_APP_ADJ |
200 |
可感知进程,比如后台音乐播放 |
VISIBLE_APP_ADJ |
100 |
可见进程 |
ADJ级别 |
取值 |
解释 |
FOREGROUND_APP_ADJ |
0 |
前台进程 |
PERSISTENT_SERVICE_ADJ |
-700 |
关联着系统或persistent进程 |
PERSISTENT_PROC_ADJ |
-800 |
系统persistent进程,比如telephony |
SYSTEM_ADJ |
-900 |
系统进程 |
NATIVE_ADJ |
-1000 |
native进程(不被系统管理) |
相关进程级别的定义在文件:frameworks/base/services/core/java/com/android/server/am/ProcessList.java中。
从上面定义的adj数值来看:adj越小表示进程类型就越重要,系统进程的默认oom_adj 为-900,这类进程被杀的概率很低。
在AMS初始化时,通过调用ProcessList.java中updateOomLevels方法,计算出阈值adj 和 minfree ,通过socket与lmkd进行通信,传送数据(LMK_TARGET、minfree、adj),在lmkd中将adj 和minfree写入sys.lmk.minfree_levels中保存。
AMS调整进程的adj相关接口(OomAdjuster.java):
服务启动后,入口在system/memory/lmkd/lmkd.c文件的main函数中,主要做了如下几件事:
system/memory/lmkd/lmkd.cpp
int main(int argc, char **argv) {
if ((argc > 1) && argv[1] && !strcmp(argv[1], "--reinit")) {
if (property_set(LMKD_REINIT_PROP, "")) {
ALOGE("Failed to reset " LMKD_REINIT_PROP " property");
}
return issue_reinit();
}
update_props();
ctx = create_android_logger(KILLINFO_LOG_TAG);
if (!init()) {
if (!use_inkernel_interface) {
/*
* MCL_ONFAULT pins pages as they fault instead of loading
* everything immediately all at once. (Which would be bad,
* because as of this writing, we have a lot of mapped pages we
* never use.) Old kernels will see MCL_ONFAULT and fail with
* EINVAL; we ignore this failure.
*
* N.B. read the man page for mlockall. MCL_CURRENT | MCL_ONFAULT
* pins ⊆ MCL_CURRENT, converging to just MCL_CURRENT as we fault
* in pages.
*/
/* CAP_IPC_LOCK required */
if (mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT) && (errno != EINVAL)) {
ALOGW("mlockall failed %s", strerror(errno));
}
/* CAP_NICE required */
struct sched_param param = {
.sched_priority = 1,
};
if (sched_setscheduler(0, SCHED_FIFO | SCHED_RESET_ON_FORK, ¶m)) {
ALOGW("set SCHED_FIFO failed %s", strerror(errno));
}
}
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");
}
if(!low_free_kill_init()) {
ALOGE("Failed to initialize the low memory kill");
}
mainloop();
}
android_log_destroy(&ctx);
close_handle_for_perf_iop();
ALOGI("exiting");
return 0;
}
static void update_props() {
// step 1 :设置vmpressure level对应的oom_adj,这部分应该是mp_event_common用,目前弃用
// low level vmpressure events : low 1001 ; medium 800 ; critical 0 ;super_critical 606
// 现调用mp_event_psi
level_oomadj[VMPRESS_LEVEL_LOW] =
GET_LMK_PROPERTY(int32, "low", OOM_SCORE_ADJ_MAX + 1);
level_oomadj[VMPRESS_LEVEL_MEDIUM] =
GET_LMK_PROPERTY(int32, "medium", 800);
level_oomadj[VMPRESS_LEVEL_CRITICAL] =
GET_LMK_PROPERTY(int32, "critical", 0);
#ifdef QCOM_FEATURE_ENABLE
/* This will gets updated through perf_wait_get_prop. */
level_oomadj[VMPRESS_LEVEL_SUPER_CRITICAL] = 606;
#endif
.....
#ifdef QCOM_FEATURE_ENABLE
// step 2 : Update Perf Properties LmkdImpl::update_perf_props
// 更新很多信息:
LmkdStub::update_perf_props();
#endif
...
#if defined(QCOM_FEATURE_ENABLE) && defined(MI_PERF_FEATURE)
//step 3 : XM_update props LmkdImpl::mi_update_props
// AndoridS后,目前会走这部分逻辑
LmkdStub::mi_update_props();
#endif
}
static int init(void) {
static struct event_handler_info kernel_poll_hinfo = { 0, kernel_event_handler };
struct reread_data file_data = {
.filename = ZONEINFO_PATH,
.fd = -1,
};
struct epoll_event epev;
int pidfd;
#ifdef QCOM_FEATURE_ENABLE
union meminfo info;
#endif
int i;
int ret;
page_k = sysconf(_SC_PAGESIZE);
if (page_k == -1)
page_k = PAGE_SIZE;
page_k /= 1024;
update_psi_window_size();
#if defined(QCOM_FEATURE_ENABLE) && defined(MI_PERF_FEATURE)
if (!meminfo_parse(&info)) {
LmkdStub::mi_init(page_k, info);
} else {
ULMK_LOG(E, "Failed to parse the meminfo\n");
}
#endif
/*
* Ensure min polling period for supercritical event is no less than
* PSI_POLL_PERIOD_SHORT_MS.
*/
#ifdef QCOM_FEATURE_ENABLE
if (psi_poll_period_scrit_ms < PSI_POLL_PERIOD_SHORT_MS) {
psi_poll_period_scrit_ms = PSI_POLL_PERIOD_SHORT_MS;
}
#endif
epollfd = epoll_create(MAX_EPOLL_EVENTS);
if (epollfd == -1) {
ALOGE("epoll_create failed (errno=%d)", errno);
return -1;
}
// mark data connections as not connected
for (int i = 0; i < MAX_DATA_CONN; i++) {
data_sock[i].sock = -1;
}
ctrl_sock.sock = android_get_control_socket("lmkd");
if (ctrl_sock.sock < 0) {
ALOGE("get lmkd control socket failed");
return -1;
}
ret = listen(ctrl_sock.sock, MAX_DATA_CONN);
if (ret < 0) {
ALOGE("lmkd control socket listen failed (errno=%d)", errno);
return -1;
}
epev.events = EPOLLIN;
ctrl_sock.handler_info.handler = ctrl_connect_handler;
epev.data.ptr = (void *)&(ctrl_sock.handler_info);
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_sock.sock, &epev) == -1) {
ALOGE("epoll_ctl for lmkd control socket failed (errno=%d)", errno);
return -1;
}
maxevents++;
has_inkernel_module = !access(INKERNEL_MINFREE_PATH, W_OK);
use_inkernel_interface = has_inkernel_module && !enable_userspace_lmk;
if (use_inkernel_interface) {
ALOGI("Using in-kernel low memory killer interface");
if (init_poll_kernel()) {
epev.events = EPOLLIN;
epev.data.ptr = (void*)&kernel_poll_hinfo;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, kpoll_fd, &epev) != 0) {
ALOGE("epoll_ctl for lmk events failed (errno=%d)", errno);
close(kpoll_fd);
kpoll_fd = -1;
} else {
maxevents++;
/* let the others know it does support reporting kills */
property_set("sys.lmk.reportkills", "1");
}
}
} else {
if (!init_monitors()) {
return -1;
}
/* let the others know it does support reporting kills */
property_set("sys.lmk.reportkills", "1");
}
for (i = 0; i <= ADJTOSLOT(OOM_SCORE_ADJ_MAX); i++) {
procadjslot_list[i].next = &procadjslot_list[i];
procadjslot_list[i].prev = &procadjslot_list[i];
}
memset(killcnt_idx, KILLCNT_INVALID_IDX, sizeof(killcnt_idx));
/*
* Read zoneinfo as the biggest file we read to create and size the initial
* read buffer and avoid memory re-allocations during memory pressure
*/
if (reread_file(&file_data) == NULL) {
ALOGE("Failed to read %s: %s", file_data.filename, strerror(errno));
}
/* check if kernel supports pidfd_open syscall */
pidfd = TEMP_FAILURE_RETRY(pidfd_open(getpid(), 0));
if (pidfd < 0) {
pidfd_supported = (errno != ENOSYS);
} else {
pidfd_supported = true;
close(pidfd);
}
ALOGI("Process polling is %s", pidfd_supported ? "supported" : "not supported" );
return 0;
}
/* 1个socket 监听 lmkd fd dev/socket/lmkd
3个client下发的socket ctrl_connect_handle添加到epoll中
3个pressure init_mp_psi/init_mo_common添加到epoll中
1个监听lmkd事件 但是现在弃用
1个wait for process death,start_wait_for_proc_kill添加到epoll
*/
epollfd = epoll_create(MAX_EPOLL_EVENTS);
if (epollfd == -1) {
ALOGE("epoll_create failed (errno=%d)", errno);
return -1;
}
// mark data connections as not connected
for (int i = 0; i < MAX_DATA_CONN; i++) {
data_sock[i].sock = -1;
}
//socket lmkd
ctrl_sock.sock = android_get_control_socket("lmkd");
if (ctrl_sock.sock < 0) {
ALOGE("get lmkd control socket failed");
return -1;
}
ret = listen(ctrl_sock.sock, MAX_DATA_CONN);
if (ret < 0) {
ALOGE("lmkd control socket listen failed (errno=%d)", errno);
return -1;
}
epev.events = EPOLLIN;
//未完待续....