Android lmkd--02:AMS调整adj

本文转载自:Android源码阅读:AMS调整adj

本文基于android-13.0.0_r1

1.OomAdjuster简介和adj调整前的检查

1.1 OomAdjuster的实例化

  AMS调整adj最核心的逻辑都在OomAdjuster中实现,OomAdjuster仅在AMS的构造函数中实例化。

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

    public ActivityManagerService(Injector injector, ServiceThread handlerThread) {
        // ...
        mOomAdjuster = hasHandlerThread
                ? new OomAdjuster(this, mProcessList, activeUids, handlerThread) : null;
        // ...
    }
 // frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java
    OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) {
        this(service, processList, activeUids, createAdjusterThread());
    }

    private static ServiceThread createAdjusterThread() {
        // The process group is usually critical to the response time of foreground app, so the
        // setter should apply it as soon as possible.
        final ServiceThread adjusterThread =
                new ServiceThread(TAG, THREAD_PRIORITY_TOP_APP_BOOST, false /* allowIo */);
        adjusterThread.start();
        return adjusterThread;
    }

1.2 adj调整的入口:updateOomAdjLocked

  adj调整是从调用updateOomAdjLocked函数开始的。

// frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java
    @GuardedBy("mService")
    boolean updateOomAdjLocked(ProcessRecord app, String oomAdjReason) {
        synchronized (mProcLock) {
            return updateOomAdjLSP(app, oomAdjReason);
        }
    }

    @GuardedBy("mService")
    void updateOomAdjLocked(String oomAdjReason) {
        synchronized (mProcLock) {
            updateOomAdjLSP(oomAdjReason);
        }
    }
  • 两个updateOomAdjLocked函数,分别针对某一特定进程,和针对LRU列表中所有进程。

  • 两个函数都是在已持有AMS锁的情况下,加mProcLock锁调用updateOomAdjLSP。

  • 这里LSP是“locked by mService& mProcLock”的含义。

  • 那么哪里会调用到updateOomAdjLocked呢?

  ActivityManagerService.java和ProcessList.java中都有针对LRU队列全部更新的调用,ActiveServices.java、ContentProviderHelper.java、ProcessStateRecord.java、ProcessRecord.java等都有针对单个进程的更新。

1.3 updateOomAdjLSP

  以全部更新为例,updateOomAdjLSP函数主要的内容是:检查是否有更新正在进行,没有的话再设置mOomAdjUpdateOngoing为true,标志adj更新正式开始,然后调用performUpdateOomAdjLSP。

    @GuardedBy({"mService", "mProcLock"})
    private void updateOomAdjLSP(String oomAdjReason) {
        if (checkAndEnqueueOomAdjTargetLocked(null)) {
            // Simply return as there is an oomAdjUpdate ongoing
            // 有更新正在进行,直接返回
            return;
        }
        try {
            mOomAdjUpdateOngoing = true;
            performUpdateOomAdjLSP(oomAdjReason);
        } finally {
            // Kick off the handling of any pending targets enqueued during the above update
            mOomAdjUpdateOngoing = false;
            updateOomAdjPendingTargetsLocked(oomAdjReason);
        }
    }
  • 检查是否有更新正在进行是通过checkAndEnqueueOomAdjTargetLocked函数获取,其内容比较简单,主要是判断mOomAdjUpdateOngoing是否为true。

  • 在有更新正在进行的情况下,会将待更新的进程塞入mPendingProcessSet,或者在全部更新的请求时设置mPendingFullOomAdjUpdate为true。

    @GuardedBy("mService")
    private boolean checkAndEnqueueOomAdjTargetLocked(@Nullable ProcessRecord app) {
        if (!mOomAdjUpdateOngoing) {
            return false;
        }
        if (app != null) {
            mPendingProcessSet.add(app);
        } else {
            mPendingFullOomAdjUpdate = true;
        }
        return true;
    }

2.adj调整和计算

2.1 performUpdateOomAdjLSP

  经过前面的检查,到performUpdateOomAdjLSP可以认为正式开始对adj的更新。

  该函数中清除待更新的进程,获取当前Top应用所在的进程topApp,并调用updateOomAdjInnerLSP继续更新。

    @GuardedBy({"mService", "mProcLock"})
    private void performUpdateOomAdjLSP(String oomAdjReason) {
        final ProcessRecord topApp = mService.getTopApp();
        // Clear any pending ones because we are doing a full update now.
        // 清除所有待处理的问题,因为我们现在正在进行全面更新。
        mPendingProcessSet.clear();
        mService.mAppProfiler.mHasPreviousProcess = mService.mAppProfiler.mHasHomeProcess = false;
        updateOomAdjInnerLSP(oomAdjReason, topApp , null, null, true, true);
    }

2.2 updateOomAdjInnerLSP

  该函数中内容较多,比较核心的内容是:

(1)获取AMS中维护的进程列表,遍历并逐个计算adj

        final boolean fullUpdate = processes == null;
        ArrayList<ProcessRecord> activeProcesses = fullUpdate ? mProcessList.getLruProcessesLOSP()
                : processes;
        final int numProc = activeProcesses.size();

        // 遍历,逐个计算adj
        for (int i = numProc - 1; i >= 0; i--) {
            ProcessRecord app = activeProcesses.get(i);
            final ProcessStateRecord state = app.mState;
            if (!app.isKilledByAm() && app.getThread() != null) {
                state.setProcStateChanged(false);
                computeOomAdjLSP(app, ProcessList.UNKNOWN_ADJ, topApp, fullUpdate, now, false,
                        computeClients); // It won't enter cycle if not computing clients.
                // if any app encountered a cycle, we need to perform an additional loop later
                retryCycles |= state.containsCycle();
                // Keep the completedAdjSeq to up to date.
                state.setCompletedAdjSeq(mAdjSeq);
            }
        }

(2)遍历并调用applyOomAdjLSP将adj修改

        boolean allChanged = updateAndTrimProcessLSP(now, nowElapsed, oldTime, activeUids);

2.3 computeOomAdjLSP:adj的计算

  computeOomAdjLSP基本上是最重要的函数了,adj的具体调整策略都是在其中进行的,通常想对某些进程的adj进行干预都会在该函数中进行修改。

    @GuardedBy({"mService", "mProcLock"})
    private boolean computeOomAdjLSP(ProcessRecord app, int cachedAdj,
            ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,
            boolean computeClients) {
        final ProcessStateRecord state = app.mState;

        // ......

        state.setCurRawAdj(adj);

        state.setCurAdj(psr.modifyRawOomAdj(adj));
        state.setCurProcState(procState);
        state.setCurRawProcState(procState);

        // if curAdj or curProcState improved, then this process was promoted
        return state.getCurAdj() < prevAppAdj || state.getCurProcState() < prevProcState
                || state.getCurCapability() != prevCapability;
    }

函数内容有非常多的判断逻辑,adj和procState计算出后会放在app.mState的“CurRaw”中,其中adj会通过modifyRawOomAdj修正一下,再赋值给“Cur”。也就是说计算出的adj、procState,都暂时存放在app.mState的CurAdj和CurProcState中。

3.adj的应用

3.1 applyOomAdjLSP

  计算adj之后通过applyOomAdjLSP函数进行应用,主要是将计算出的各种状态赋值给以Set开头的变量。

    /** Applies the computed oomadj, procstate and sched group values and freezes them in set* */
    /* 应用计算出的 oomadj、procstate 和 sched 组值,并将它们冻结在 set* 中 */
    @GuardedBy({"mService", "mProcLock"})
    private boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now,
            long nowElapsed) {

        final ProcessStateRecord state = app.mState;

        // 将 CurRawAdj 赋值给 SetRawAdj
        if (state.getCurRawAdj() != state.getSetRawAdj()) {
            state.setSetRawAdj(state.getCurRawAdj());
        }

        // 将 CurAdj 赋值给 SetAdj 
        if (state.getCurAdj() != state.getSetAdj()) {
            ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj());
            if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) {
                String msg = "Set " + app.getPid() + " " + app.processName + " adj "
                        + state.getCurAdj() + ": " + state.getAdjType();
                reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
            }
            state.setSetAdj(state.getCurAdj());
            if (uidRec != null) {
                uidRec.noteProcAdjChanged();
            }
            state.setVerifiedAdj(ProcessList.INVALID_ADJ);
        }

        // .......
    }

该函数中内容也比较多,比较重要的是将 CurAdj 赋值给 SetAdj,并调用ProcessList.setOomAdj

3.2 ProcessList.setOomAdj:与lmkd通信写入adj

// frameworks/base/services/core/java/com/android/server/am/ProcessList.java
    public static void setOomAdj(int pid, int uid, int amt) {
        // This indicates that the process is not started yet and so no need to proceed further.
        if (pid <= 0) {
            return;
        }
        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, null);
        long now = SystemClock.elapsedRealtime();
        if ((now-start) > 250) {
            Slog.w("ActivityManager", "SLOW OOM ADJ: " + (now-start) + "ms for pid " + pid
                    + " = " + amt);
        }
    }

四个字节的buffer里,先写入LMK_PROCPRIO的控制指令,代表要做优先级的修改。 然后是pid、uid、adj

  之后调用writeLmkd与lmkd进程通信。

    private static boolean writeLmkd(ByteBuffer buf, ByteBuffer repl) {
        if (!sLmkdConnection.isConnected()) {
            // try to connect immediately and then keep retrying
            sKillHandler.sendMessage(
                    sKillHandler.obtainMessage(KillHandler.LMKD_RECONNECT_MSG));

            // wait for connection retrying 3 times (up to 3 seconds)
            if (!sLmkdConnection.waitForConnection(3 * LMKD_RECONNECT_DELAY_MS)) {
                return false;
            }
        }

        return sLmkdConnection.exchange(buf, repl);
    }

后续通信可以看LmkdConnection.java以及lmkd.cpp,lmkd接收到adj修改的命令后,会去修改/proc/[pid]/oom_score_adj节点的值。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容