Android framework--AMSについてupdateOomAdjLocked

21473 ワード

Androidシステムのメモリ回収メカニズムについては、皆さんもよく知られていると思います.Androidは、各アプリケーションプロセスが4つのコンポーネントを搭載している状態に基づいてアプリケーションプロセスの重要性を評価し、システムメモリの緊張時に重要性が低いから高いまでアプリケーションプロセスを殺し、メモリを解放する目的を達成します.重要度評価はAMSが行う、具体的にはAMSである.updateOomAdjLocked関数、逆にAMS.updateOomAdjLockedの役割は、アプリケーションプロセスの更新の重要性です.
適用プロセス(ProcessRecord)の重要性は、3つのステータス値で表されます.
  • adj:LMK殺プロセスのスコアは
  • に基づいている.
  • procState:プロセスステータスを示す
  • schedGroup:プロセススケジューリングポリシーを示す
  • 本稿では,この関数の具体的な実行を解析するのではなく,Android Nを参照する3つの状態値の違いについて議論する.
    基本的な考え方
    プロセスの重要性を評価し、それをLMK回収プロセスの根拠とする以上、理論的には単一の状態から重要性を示し、LMKが最良の案であるべきであることを知らせ、簡単で乱暴で、論理がはっきりしている.皆さんはいろいろなところから知っています.oom_adjはLMKにメモリ回収を行う根拠となるが,procStateとschedGroupについては一筆書きか,いっそ言及しない.しかし、このような二つのものがある以上、必ずその存在の意味がある.
    まずAMSについてお話ししますupdateOomAdjLockedの実行プロセス:
    updateOomAdjLocked(ProcessRecord app)
        -updateOomAdjLocked(ProcessRecord app, int cachedAdj,
                   ProcessRecord TOP_APP, boolean doingAll, long now) 
            -computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,
                boolean doingAll, long now)
            -applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
                long nowElapsed)
    updateOomAdjLocked()
        -computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,
                boolean doingAll, long now)
            -applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
                long nowElapsed)

    上記の2つの実行パスがあり、同じスペースインデントは関数呼び出しスタックの同じレベルを表し、updateOomAdjLockedには複数のリロードバージョンがあることに注意してください.以下の点を重点的に説明します.
  • updateOomAdjLockedは、アプリケーションプロセスのコンポーネントの実行状態が変更されたときに呼び出されます.例えば、サービス起動、ブロードキャスト受信者がブロードキャストを受信し、Activity起動などがあります.これは、プロセスの重要性の計算がコンポーネントの実行状態に依存しているため、コンポーネントの実行状態が変更された以上、リアルタイムで更新する必要があります.
  • computeOomAdjLockedは、Androidがプロセスを分割する5つの優先度に関係する、フロントプロセス、可視プロセス、サービスプロセス、バックグラウンドプロセス、空プロセスの3つのステータス値を一定のルールに基づいて計算します.ここでは詳しく説明しません.
  • applyOomAdjLockedはcomputeOomAdjLockedが計算した3つの状態値を適用し、この3つの状態値の役割を果たす.

  • この3つの状態値の役割を分析する以上、ソースコードがこれらの値をどのように使用するかを見るのが最善の方法であり、applyOomAdjLockedが私たちの切り込み点であることがわかります.
    applyOomAdjLocked
    次のコードに含まれるProcessRecordのcurの先頭のフィールドは、curAdj、curProcState、curSchedGroupなどのcomputeOomAdjLocked関数によって計算された現在の状態値であり、コードの観点からそれぞれの役割を説明します.
    adj
    if (app.curAdj != app.setAdj) {
        ProcessList.setOomAdj(app.pid, app.info.uid, app.curAdj);
        if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
                "Set " + app.pid + " " + app.processName + " adj " + app.curAdj + ": "
                + app.adjType);
        app.setAdj = app.curAdj;
        app.verifiedAdj = ProcessList.INVALID_ADJ;
    }

    CurAdjは、computeOomAdjLockedが算出するadj値であり、setAdjに付与され、ProcessListが呼び出される.setoomAdj、下を見続けます:
    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);
        }
    }
    private static void writeLmkd(ByteBuffer buf) {
    
        for (int i = 0; i < 3; i++) {
            if (sLmkdSocket == null) {
                    if (openLmkdSocket() == false) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException ie) {
                        }
                        continue;
                    }
            }
    
            try {
                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;
            }
        }
    }
    private static boolean openLmkdSocket() {
        try {
            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;
    }

    最終的には「lmkd」という名前のSocketにデータを送信し、データ内容はpid、uidおよび対応するadj値であり、「lmkd」はlmkdプロセスによって作成されるので、後のことはlmkdによって処理される.簡単に言えば、lmkdは各プロセスのadj値および予め設定されたメモリ閾値に基づいて、一定のポリシーに基づいてプロセスを殺してメモリを解放し、ここでは具体的な実現に関心を持たず、lmkdがadj値を利用してどのようなことをしたかに関心を持つ.すなわちlmkdの戦略はadj値にのみ依存し,procStateやschedGroupとは無関係である.
    procState
    if (app.repProcState != app.curProcState) {
        app.repProcState = app.curProcState;
        changes |= ProcessChangeItem.CHANGE_PROCESS_STATE;
        if (app.thread != null) {
            try {
                if (false) {
                    //RuntimeException h = new RuntimeException("here");
                    Slog.i(TAG, "Sending new process state " + app.repProcState
                            + " to " + app /*, h*/);
                }
                app.thread.setProcessState(app.repProcState);
            } catch (RemoteException e) {
            }
        }
    }

    適用プロセスApplicationThreadのsetProcessStateメソッドが呼び出されました.
    public void setProcessState(int state) {
        updateProcessState(state, true);
    }
    
    public void updateProcessState(int processState, boolean fromIpc) {
        synchronized (this) {
            if (mLastProcessState != processState) {
                mLastProcessState = processState;
                // Update Dalvik state based on ActivityManager.PROCESS_STATE_* constants.
                final int DALVIK_PROCESS_STATE_JANK_PERCEPTIBLE = 0;
                final int DALVIK_PROCESS_STATE_JANK_IMPERCEPTIBLE = 1;
                int dalvikProcessState = DALVIK_PROCESS_STATE_JANK_IMPERCEPTIBLE;
                // TODO: Tune this since things like gmail sync are important background but not jank perceptible.
                if (processState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
                    dalvikProcessState = DALVIK_PROCESS_STATE_JANK_PERCEPTIBLE;
                }
                VMRuntime.getRuntime().updateProcessState(dalvikProcessState);
                if (false) {
                    Slog.i(TAG, "******************* PROCESS STATE CHANGED TO: " + processState
                            + (fromIpc ? " (from ipc": ""));
                }
            }
        }
    }

    最後にVMRuntimeが呼び出された.getRuntime().updateProcessState(dalvikProcessState)、渡されたパラメータprocessStateはActivity Management.PROCESS_STATE_IMPORTANT_FOREGROUNDはDALVIK_に変換されましたPROCESS_STATE_JANK_PERCEPTIBLEおよびDALVIK_PROCESS_STATE_JANK_IMPERCEPTIBLE.実際、これは、ART実行時の現在のプロセス状態が、仮想マシンを切り替えるためのGCアルゴリズム、すなわちフロントGCまたはバックグラウンドGCであるかを示すものである.フロントGCは効率が高いがフラグメントが発生し、バックグラウンドGCは効率が低いがフラグメントの問題はなく、アプリケーションプロセスがフロントバックグラウンドにある2つの状況に適している.詳細については、以下を参照してください.https://blog.csdn.net/luoshengyang/article/details/45301715
    schedGroup
    if (app.setSchedGroup != app.curSchedGroup) {
        int oldSchedGroup = app.setSchedGroup;
        app.setSchedGroup = app.curSchedGroup;
        if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
                "Setting sched group of " + app.processName
                + " to " + app.curSchedGroup);
        if (app.waitingToKill != null && app.curReceiver == null
                && app.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND) {
            app.kill(app.waitingToKill, true);
            success = false;
        } else {
            int processGroup;
            switch (app.curSchedGroup) {
                case ProcessList.SCHED_GROUP_BACKGROUND:
                    processGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
                    break;
                case ProcessList.SCHED_GROUP_DEFAULT:
                    processGroup = Process.THREAD_GROUP_DEFAULT;
                    break;
                case ProcessList.SCHED_GROUP_SYSTEM:
                    processGroup = Process.THREAD_GROUP_SYSTEM;
                    break;
                case ProcessList.SCHED_GROUP_AUDIO_APP:
                    processGroup = Process.THREAD_GROUP_AUDIO_APP;
                    break;
                case ProcessList.SCHED_GROUP_AUDIO_SYS:
                    processGroup = Process.THREAD_GROUP_AUDIO_SYS;
                    break;
                case ProcessList.SCHED_GROUP_TOP_APP:
                    processGroup = Process.THREAD_GROUP_TOP_APP;
                    break;
                default:
                    processGroup = Process.THREAD_GROUP_DEFAULT;
                    break;
            }
            long oldId = Binder.clearCallingIdentity();
            try {
                Process.setProcessGroup(app.pid, processGroup);  //         
                if (app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
                    // do nothing if we already switched to RT
                    if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
                        // Switch VR thread for app to SCHED_FIFO
                        if (mInVrMode && app.vrThreadTid != 0) {
                            try {
                                Process.setThreadScheduler(app.vrThreadTid,
                                    Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
                            } catch (IllegalArgumentException e) {
                                // thread died, ignore
                            }
                        }
                        if (mUseFifoUiScheduling) {
                            // Switch UI pipeline for app to SCHED_FIFO
                            app.savedPriority = Process.getThreadPriority(app.pid);
                            try {
                                Process.setThreadScheduler(app.pid,
                                    Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
                            } catch (IllegalArgumentException e) {
                                // thread died, ignore
                            }
                            if (app.renderThreadTid != 0) {
                                try {
                                    Process.setThreadScheduler(app.renderThreadTid,
                                        Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
                                } catch (IllegalArgumentException e) {
                                    // thread died, ignore
                                }
                                if (DEBUG_OOM_ADJ) {
                                    Slog.d("UI_FIFO", "Set RenderThread (TID " +
                                        app.renderThreadTid + ") to FIFO");
                                }
                            } else {
                                if (DEBUG_OOM_ADJ) {
                                    Slog.d("UI_FIFO", "Not setting RenderThread TID");
                                }
                            }
                        } else {  //     UI        -10
                            // Boost priority for top app UI and render threads
                            Process.setThreadPriority(app.pid, -10);
                            if (app.renderThreadTid != 0) {
                                try {
                                    Process.setThreadPriority(app.renderThreadTid, -10);
                                } catch (IllegalArgumentException e) {
                                    // thread died, ignore
                                }
                            }
                        }
                    }
                } else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
                           app.curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
                    // Reset VR thread to SCHED_OTHER
                    // Safe to do even if we're not in VR mode
                    if (app.vrThreadTid != 0) {
                        Process.setThreadScheduler(app.vrThreadTid, Process.SCHED_OTHER, 0);
                    }
                    if (mUseFifoUiScheduling) {
                        // Reset UI pipeline to SCHED_OTHER
                        Process.setThreadScheduler(app.pid, Process.SCHED_OTHER, 0);
                        Process.setThreadPriority(app.pid, app.savedPriority);
                        if (app.renderThreadTid != 0) {
                            Process.setThreadScheduler(app.renderThreadTid,
                                Process.SCHED_OTHER, 0);
                            Process.setThreadPriority(app.renderThreadTid, -4);
                        }
                    } else {  //     UI        0
                        // Reset priority for top app UI and render threads
                        Process.setThreadPriority(app.pid, 0);
                        if (app.renderThreadTid != 0) {
                            Process.setThreadPriority(app.renderThreadTid, 0);
                        }
                    }
                }
            } catch (Exception e) {
                Slog.w(TAG, "Failed setting process group of " + app.pid
                        + " to " + app.curSchedGroup);
                e.printStackTrace();
            } finally {
                Binder.restoreCallingIdentity(oldId);
            }
        }
    }

    まとめて、主に2つのことをしました.コードの中の中国語の注釈を参考にします.
    1.プロセスを呼び出す.setProcessGroup(int pid,int group)はプロセススケジューリングポリシーを設定し、native層コードは詳細に分析されなくなった.原理はlinuxのcgroupメカニズムを利用して、プロセスを状態に基づいて予め設定されたcgroupパケットに入れる.これらのパケットにはcpu使用率、cpuset、cpu周波数変調などのサブリソースの構成が含まれており、特定の状態プロセスのシステムリソースに対する需要を満たしている.
    2、schedGroupがProcessListにある場合.SCHED_GROUP_TOP_APPと非ProcessList.SCHED_GROUP_TOP_APP間切替時にProcessを呼び出す.setThreadPriority(int tid,int priority)は、アプリケーションがフロントにアクセスし、フロントを終了するときにUI関連スレッドの優先度を変更することであり、ここでのUI関連スレッドには、ユーザインタフェースの応答速度を向上させるためのプライマリスレッドとRenderThreadが含まれる.
    以上のように、3つの状態はそれぞれその職を司り、異なる面から応用の流暢な運行を保証するために護衛し、1つが欠けてはいけない.