从源码角度看 LowMemoryKiller

简介

Android系统中,进程的运行状态常常是各个应用开发者关注点的重中之重,进程是否能够存活基本也就意味着应用能否从用户手中获益。之前我写过一篇文章专门讲过进程保活与进程优先级的计算方式,有兴趣的话大家可以再看看。

进程一直存活固然很重要,一来应用可以随时随地的在用户手机上做业务操作,二来用户再次启动时速度也会更快些。然而,作为移动设备,应用进程如果只启动而不能被Android系统被动的杀除,那么越来越多的内存被尚在运行的无用进程霸占,系统只会越用越卡。对此,Android基于OOM机制引入了lowmemmorykiller,可以在系统内存紧缺时杀死不必要的低优先级进程,进程的优先级越低就越容易被杀死。

对于lowmemorykiller, 我总结为三层:AMS, lmkd 与 lowmemorykiller。其中AMS负责更新各应用的进程优先级与阈值数组,lmkd负责接收AMS传输过来的数据然后写入到sys与proc节点,lowmemorykiller则在内存低于阈值时才会被触发并负责杀死低优先级的进程。

这篇文章中,我会先列出一张总结流程图,便于大家掌握lowmemorykiller总体涉及也方便以后查看。随后我再会按照三层从上往下的分析lowmemmorykiller的代码。

查看大图

上层:ActivityManagerService更新adj

AMS.updateConfiguration

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public void updateConfiguration(Configuration values) {
    synchronized (this) {
...
        if (mWindowManager != null) {
            mProcessList.applyDisplaySize(mWindowManager);
        }
...
    }
}

updateConfiguration是AMS对外提供的binder接口,调用后可以更新窗口配置

frameworks/base/services/core/java/com/android/server/am/ProcessList.java

void applyDisplaySize(WindowManagerService wm) {
    if (!mHaveDisplaySize) {
        Point p = new Point();
        // 获取窗口显示的宽高
        wm.getBaseDisplaySize(Display.DEFAULT_DISPLAY, p);
        if (p.x != 0 && p.y != 0) {
            updateOomLevels(p.x, p.y, true);
            mHaveDisplaySize = true;
        }
    }
}
private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
    // mTotalMemMb是指当前设备内存大小,以MB为单位
    // 计算内存比例
    float scaleMem = ((float)(mTotalMemMb-350))/(700-350);

    // 计算屏幕大小
    int minSize = 480*800;  //  384000
    int maxSize = 1280*800; // 1024000  230400 870400  .264
    float scaleDisp = ((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize);
    if (false) {
        Slog.i("XXXXXX", "scaleMem=" + scaleMem);
        Slog.i("XXXXXX", "scaleDisp=" + scaleDisp + " dw=" + displayWidth
                + " dh=" + displayHeight);
    }

    // 选择较大的比例,最小为0最大为1
    float scale = scaleMem > scaleDisp ? scaleMem : scaleDisp;
    if (scale < 0) scale = 0;
    else if (scale > 1) scale = 1;
    int minfree_adj = Resources.getSystem().getInteger(
            com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAdjust);
    int minfree_abs = Resources.getSystem().getInteger(
            com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAbsolute);
    if (false) {
        Slog.i("XXXXXX", "minfree_adj=" + minfree_adj + " minfree_abs=" + minfree_abs);
    }

    final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0;

    for (int i=0; i<mOomAdj.length; i++) {
        int low = mOomMinFreeLow[i];
        int high = mOomMinFreeHigh[i];
        if (is64bit) {
            // Increase the high min-free levels for cached processes for 64-bit
            // 64位的机器high值会适当的提高,最终空进程、缓存进程被杀的内存阈值也会被提高
            if (i == 4) high = (high*3)/2;
            else if (i == 5) high = (high*7)/4;
        }
        mOomMinFree[i] = (int)(low + ((high-low)*scale));
    }

...

    if (write) {
        // 通过socket将计算完毕的信息传输给lwkd守护进程
        ByteBuffer buf = ByteBuffer.allocate(4 * (2*mOomAdj.length + 1));
        buf.putInt(LMK_TARGET);
        for (int i=0; i<mOomAdj.length; i++) {
            // 最终写入到minfree文件的数组单位为page的个数
            buf.putInt((mOomMinFree[i]*1024)/PAGE_SIZE);
          // 写入到adj文件的数组为固定的adj数组
            buf.putInt(mOomAdj[i]);
        }

        writeLmkd(buf);
        SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve));
    }
    // GB: 2048,3072,4096,6144,7168,8192
    // HC: 8192,10240,12288,14336,16384,20480
}
名称
FOREGROUND_APP\_ADJ 0
VISIBLE_APP\_ADJ 1
PERCEPTIBLE_APP\_ADJ 2
BACKUP_APP\_ADJ 3
CACHED_APP\_MIN\_ADJ 9
CACHED_APP\_MAX\_ADJ 15

这张表为mOomAdj数组,最终会被写入到sys节点的adj文件

AMS.applyOomAdjLocked

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
                                                 long nowElapsed) {
...

    if (app.curAdj != app.setAdj) {
        ProcessList.setOomAdj(app.pid, app.info.uid, app.curAdj);
    }
...

当计算后的adj和之前的不一样,就会通过setOomAdj更新lmk的adj数值

frameworks/base/services/core/java/com/android/server/am/ProcessList.java

public static final void setOomAdj(int pid, int uid, int amt) {
    if (amt == UNKNOWN_ADJ)
        return;

    long start = SystemClock.elapsedRealtime();
    ByteBuffer buf = ByteBuffer.allocate(4 * 4);
    buf.putInt(LMK_PROCPRIO);
    buf.putInt(pid);
    buf.putInt(uid);
    buf.putInt(amt);
    writeLmkd(buf);
    long now = SystemClock.elapsedRealtime();
    if ((now-start) > 250) {
        Slog.w("ActivityManager", "SLOW OOM ADJ: " + (now-start) + "ms for pid " + pid
                + " = " + amt);
    }
}

setOomAdj会将AMS已经计算好的adj值通过socket发送到lmkd

AMS.cleanUpApplicationRecordLocked & AMS.handleAppDiedLocked

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

private final void handleAppDiedLocked(ProcessRecord app,
                                       boolean restarting, boolean allowRestart) {
    int pid = app.pid;
    boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1);
    if (!kept && !restarting) {
        removeLruProcessLocked(app);
        if (pid > 0) {
            ProcessList.remove(pid);
        }
    }
...

死亡回调后,如果进程不再重启,那么会先移除lruProcess的记录,随后会调用该进程的ProcessList.remove方法

private final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
                                                     boolean restarting, boolean allowRestart, int index) {
...
    if (restart && !app.isolated) {
        // We have components that still need to be running in the
        // process, so re-launch it.
        if (index < 0) {
            ProcessList.remove(app.pid);
        }
        addProcessNameLocked(app);
        startProcessLocked(app, "restart", app.processName);
        return true;
    } 
    return false;
}

通过trim会调用应用的cleanUp机制,如果应用会重启那么会调用该进程的ProcessList.remove方法

frameworks/base/services/core/java/com/android/server/am/ProcessList.java

public static final void remove(int pid) {
    ByteBuffer buf = ByteBuffer.allocate(4 * 2);
    buf.putInt(LMK_PROCREMOVE);
    buf.putInt(pid);
    writeLmkd(buf);
}

各协议所携带的参数

lmk协议 所需参数
LMK_TARGET mOomMinFree, mOomAdj两个数组
LMK_PROCPRIO pid, uid, amt (adj)
LMK_PROCREMOVE pid

ProcessList.writeLmkd

frameworks/base/services/core/java/com/android/server/am/ProcessList.java

private static void writeLmkd(ByteBuffer buf) {
    for (int i = 0; i < 3; i++) {
        if (sLmkdSocket == null) {
                    // 首先尝试打开socket,如果没有打开就睡眠1s,最多会尝试打开3次,如果没有打开就不会再做尝试
                if (openLmkdSocket() == false) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ie) {
                    }
                    continue;
                }
        }

        try {
            // 写入数据到对端的socket
            sLmkdOutputStream.write(buf.array(), 0, buf.position());
            return;
        } catch (IOException ex) {
            Slog.w(TAG, "Error writing to lowmemorykiller socket");

            try {
                sLmkdSocket.close();
            } catch (IOException ex2) {
            }

            sLmkdSocket = null;
        }
    }
}

writeLmkd的输入参数是ByteBuffer,用来保存需要传输给lmkd的数据

private static boolean openLmkdSocket() {
    try {
        // 尝试连接socket
        sLmkdSocket = new LocalSocket(LocalSocket.SOCKET_SEQPACKET);
        sLmkdSocket.connect(
            new LocalSocketAddress("lmkd",
                    LocalSocketAddress.Namespace.RESERVED));
       // 获取输出流,用于向对端写入数据
        sLmkdOutputStream = sLmkdSocket.getOutputStream();
    } catch (IOException ex) {
        Slog.w(TAG, "lowmemorykiller daemon socket open failed");
        sLmkdSocket = null;
        return false;
    }

    return true;
}

以上的代码便是AMS通过socket来将计算后的adj, minfree阈值写入到lwkd的实现方法

中层:lmkd守护线程,向节点写入数据

int main(int argc __unused, char **argv __unused) {
    struct sched_param param = {
            .sched_priority = 1,
    };

    mlockall(MCL_FUTURE);
    sched_setscheduler(0, SCHED_FIFO, &param);
    // 首先完成初始化操作
    if (!init())
        // 随后进入主循环,等待AMS发送socket请求
        mainloop();

    ALOGI("exiting");
    return 0;
}

```c
static int init(void) {
// 初始化epoll事件
struct epoll_event epev;
int i;
int ret;

...

// 创建一个epoll并获取fd
epollfd = epoll_create(MAX_EPOLL_EVENTS);

ctrl_lfd = android_get_control_socket("lmkd");

ret = listen(ctrl_lfd, 1);

epev.events = EPOLLIN;
// 当监听到socket连接事件后会调用ctrl_connect_handler方法
epev.data.ptr = (void *)ctrl_connect_handler;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_lfd, &epev) == -1) {
    ALOGE("epoll_ctl for lmkd control socket failed (errno=%d)", errno);
    return -1;
top Created with Sketch.