1. 程式人生 > 其它 >Android O lowmemorykiller原理解析

Android O lowmemorykiller原理解析

技術標籤:Stability

文章目錄

1. LowmemoryKiller介紹

1.1 為什麼引入LowmemoryKiller?

程序的啟動分冷啟動和熱啟動,當用戶退出某一個程序的時候,並不會真正的將程序退出,而是將這個程序放到後臺,以便下次啟動的時候可以馬上啟動起來,這個過程名為熱啟動,這也是Android的設計理念之一。這個機制會帶來一個問題,每個程序都有自己獨立的記憶體地址空間,隨著應用開啟數量的增多,系統已使用的記憶體越來越大,就很有可能導致系統記憶體不足。為了解決這個問題,系統引入LowmemoryKiller(簡稱lmk)管理所有程序,根據一定策略來kill某個程序並釋放佔用的記憶體,保證系統的正常執行。

1.2 LMK基本原理?

所有應用程序都是從zygote孵化出來的,記錄在AMS中mLruProcesses列表中,由AMS進行統一管理,AMS中會根據程序的狀態更新程序對應的oom_adj值,這個值會通過檔案傳遞到kernel中去,kernel有個低記憶體回收機制,在記憶體達到一定閥值時會觸發清理oom_adj值高的程序騰出更多的記憶體空間,這就是Lowmemorykiller工作原理。

1.3 lmkd整體流程

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-9qVFyhNE-1610437293271)(https://s3.ax1x.com/2021/01/08/suAePA.png)]

  1. AMS設定 /sys/module/lowmemorykiller/parameters/minfree和/sys/module/lowmemorykiller/parameters/adj節點標註當前系統的程序對應的adj值,已經對應的記憶體值minfree.在應用變化中設定/proc/pid/adj應用的當前adj值

  2. lmkd讀取minfree,adj_score值知道記憶體的閾值,在讀取當前的剩餘記憶體,就知道殺死那些adj值

  3. lmkd遍歷/proc/pid/adj值,知道各個程序的adj值,滿足條件的就殺死

參考link:

https://www.jianshu.com/p/221f4a246b45

1.4. 檢視程序adj值已經程序對應的型別

檢視程序1015的adj值為 -800。也可以通過adb shell dumpsys activity o/p

//第一種
msm8996_gvmq:/ # cat /proc/1015/oom_score_adj         
-800
msm8996_gvmq:/ # cat /proc/1015/oom_adj               
-14

//第二種
msm8996_gvmq:/ # dumpsys activity o | grep dms -A 3 
    PERS #29: pers  F/ /PER  trm: 5 1015:com.chinatsp.dmsservice.TDmsBootService/1000 (fixed)
        oom: max=-800 curRaw=-800 setRaw=-800 cur=-800 set=-800
        state: cur=PER  set=PER  lastPss=147MB lastSwapPss=0.00 lastCachedPss=0.00
        cached=false empty=false hasAboveClient=false

minfree中數值的單位是記憶體中的頁面數量,一般情況下一個頁面是4KB。因此80640代表 80640*4k = 322560k,大概320M,對應殺死的adj值為906,也就是少於320M就殺死大於等於906的程序

//對應上面的一組數,每個陣列代表一個程序優先順序級別
msm8996_gvmq:/ # cat /sys/module/lowmemorykiller/parameters/adj
0,100,200,300,900,906
//裡面是以","分割的一組數,每個數字代表一個記憶體級別。
msm8996_gvmq:/ # cat /sys/module/lowmemorykiller/parameters/minfree
18432,23040,27648,32256,55296,80640

Ams設定的等級

@frameworks/base/services/core/java/com/android/server/am/ProcessList.java
//adj值 0,100,200,300,900,906
    private final int[] mOomAdj = new int[] {
            FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
            BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_MAX_ADJ
    };

    // These are the low-end OOM level limits.  This is appropriate for an
    // HVGA or smaller phone with less than 512MB.  Values are in KB.
    private final int[] mOomMinFreeLow = new int[] {
            12288, 18432, 24576,
            36864, 43008, 49152
    };
    
    //解析度記憶體高的專案: 對應minfree:18432,23040,27648,32256,55296,80640
    private final int[] mOomMinFreeHigh = new int[] {
            73728, 92160, 110592,
            129024, 147456, 184320
    };

各型別對應的adj值,
CACHED_APP_MAX_ADJ

@frameworks/base/services/core/java/com/android/server/am/ProcessList.java
    static final int INVALID_ADJ = -10000;

    // Adjustment used in certain places where we don't know it yet.
    // (Generally this is something that is going to be cached, but we
    // don't know the exact value in the cached range to assign yet.)
    static final int UNKNOWN_ADJ = 1001;

    // This is a process only hosting activities that are not visible,
    // so it can be killed without any disruption.
    static final int CACHED_APP_MAX_ADJ = 906;
    static final int CACHED_APP_MIN_ADJ = 900;

    // The B list of SERVICE_ADJ -- these are the old and decrepit
    // services that aren't as shiny and interesting as the ones in the A list.
    static final int SERVICE_B_ADJ = 800;

    // This is the process of the previous application that the user was in.
    // This process is kept above other things, because it is very common to
    // switch back to the previous app.  This is important both for recent
    // task switch (toggling between the two top recent apps) as well as normal
    // UI flow such as clicking on a URI in the e-mail app to view in the browser,
    // and then pressing back to return to e-mail.
    static final int PREVIOUS_APP_ADJ = 700;

    // This is a process holding the home application -- we want to try
    // avoiding killing it, even if it would normally be in the background,
    // because the user interacts with it so much.
    static final int HOME_APP_ADJ = 600;

    // This is a process holding an application service -- killing it will not
    // have much of an impact as far as the user is concerned.
    static final int SERVICE_ADJ = 500;

    // This is a process with a heavy-weight application.  It is in the
    // background, but we want to try to avoid killing it.  Value set in
    // system/rootdir/init.rc on startup.
    static final int HEAVY_WEIGHT_APP_ADJ = 400;

    // This is a process currently hosting a backup operation.  Killing it
    // is not entirely fatal but is generally a bad idea.
    static final int BACKUP_APP_ADJ = 300;

    // This is a process only hosting components that are perceptible to the
    // user, and we really want to avoid killing them, but they are not
    // immediately visible. An example is background music playback.
    static final int PERCEPTIBLE_APP_ADJ = 200;

    // This is a process only hosting activities that are visible to the
    // user, so we'd prefer they don't disappear.
    static final int VISIBLE_APP_ADJ = 100;
    static final int VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1;

    // This is the process running the current foreground app.  We'd really
    // rather not kill it!
    static final int FOREGROUND_APP_ADJ = 0;

    // This is a process that the system or a persistent process has bound to,
    // and indicated it is important.
    static final int PERSISTENT_SERVICE_ADJ = -700;

    // This is a system persistent process, such as telephony.  Definitely
    // don't want to kill it, but doing so is not completely fatal.
    static final int PERSISTENT_PROC_ADJ = -800;

    // The system process runs at the default adjustment.
    static final int SYSTEM_ADJ = -900;

    // Special code for native processes that are not being managed by the system (so
    // don't have an oom adj assigned by the system).
    static final int NATIVE_ADJ = -1000;

2. AMS怎麼調整adj和記憶體值minFree

在高通的shell中又發現,他有通過讀取機身總記憶體來設定minfree等級

@device/qcom/common/rootdir/etc/init.qcom.post_boot.sh
    if [ "$arch_type" == "aarch64" ] && [ $MemTotal -gt 2097152 ]; then
        echo 10 > /sys/module/process_reclaim/parameters/pressure_min
        echo 1024 > /sys/module/process_reclaim/parameters/per_swap_size
        echo "18432,23040,27648,32256,55296,80640" > /sys/module/lowmemorykiller/parameters/minfree
        echo 81250 > /sys/module/lowmemorykiller/parameters/vmpressure_file_min
    elif [ "$arch_type" == "aarch64" ] && [ $MemTotal -gt 1048576 ]; then
        echo 10 > /sys/module/process_reclaim/parameters/pressure_min
        echo 1024 > /sys/module/process_reclaim/parameters/per_swap_size
        echo "14746,18432,22118,25805,40000,55000" > /sys/module/lowmemorykiller/parameters/minfree
        echo 81250 > /sys/module/lowmemorykiller/parameters/vmpressure_file_min
    elif [ "$arch_type" == "aarch64" ]; then
        echo 50 > /sys/module/process_reclaim/parameters/pressure_min
        echo 512 > /sys/module/process_reclaim/parameters/per_swap_size
        echo "14746,18432,22118,25805,40000,55000" > /sys/module/lowmemorykiller/parameters/minfree
        echo 81250 > /sys/module/lowmemorykiller/parameters/vmpressure_file_min
    else
        echo 50 > /sys/module/process_reclaim/parameters/pressure_min
        echo 512 > /sys/module/process_reclaim/parameters/per_swap_size
        echo "15360,19200,23040,26880,34415,43737" > /sys/module/lowmemorykiller/parameters/minfree
        echo 53059 > /sys/module/lowmemorykiller/parameters/vmpressure_file_min
    fi

2.1 AMS設定minfree

Ams設定的等級

@frameworks/base/services/core/java/com/android/server/am/ProcessList.java
//adj值 0,100,200,300,900,906
    private final int[] mOomAdj = new int[] {
            FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
            BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_MAX_ADJ
    };

    // These are the low-end OOM level limits.  This is appropriate for an
    // HVGA or smaller phone with less than 512MB.  Values are in KB.
    private final int[] mOomMinFreeLow = new int[] {
            12288, 18432, 24576,
            36864, 43008, 49152
    };
    
    //解析度記憶體高的專案: 對應minfree:18432,23040,27648,32256,55296,80640
    private final int[] mOomMinFreeHigh = new int[] {
            73728, 92160, 110592,
            129024, 147456, 184320
    };

if (i == 4) high = (high3)/2;
else if (i == 5) high = (high
7)/4;
因此計算公式:
184320 * 7 /4 = 322,560(記憶體大小) /4k = 80640
147456 *3 /2 = 221,184(記憶體大小)/4k = 55296
129024 / 4k = 32256

18432,23040,27648,32256,55296,80640 剛剛對應lmkd裡面的值

完成向lmkd寫minfree和adj節點資料

   private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
        // Scale buckets from avail memory: at 300MB we use the lowest values to
        // 700MB or more for the top values.
        float scaleMem = ((float)(mTotalMemMb-350))/(700-350);

        // Scale buckets from screen size.
        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);
        }

        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;

        //第4和5會擴大3/2 和7/4倍值
        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
                if (i == 4) high = (high*3)/2;
                else if (i == 5) high = (high*7)/4;
            }
            mOomMinFree[i] = (int)(low + ((high-low)*scale));
        }

        if (minfree_abs >= 0) {
            for (int i=0; i<mOomAdj.length; i++) {
                mOomMinFree[i] = (int)((float)minfree_abs * mOomMinFree[i]
                        / mOomMinFree[mOomAdj.length - 1]);
            }
        }

        if (minfree_adj != 0) {
            for (int i=0; i<mOomAdj.length; i++) {
                mOomMinFree[i] += (int)((float)minfree_adj * mOomMinFree[i]
                        / mOomMinFree[mOomAdj.length - 1]);
                if (mOomMinFree[i] < 0) {
                    mOomMinFree[i] = 0;
                }
            }
        }

        // The maximum size we will restore a process from cached to background, when under
        // memory duress, is 1/3 the size we have reserved for kernel caches and other overhead
        // before killing background processes.
        mCachedRestoreLevel = (getMemLevel(ProcessList.CACHED_APP_MAX_ADJ)/1024) / 3;

        // Ask the kernel to try to keep enough memory free to allocate 3 full
        // screen 32bpp buffers without entering direct reclaim.
        int reserve = displayWidth * displayHeight * 4 * 3 / 1024;
        int reserve_adj = Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAdjust);
        int reserve_abs = Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAbsolute);

        if (reserve_abs >= 0) {
            reserve = reserve_abs;
        }

        if (reserve_adj != 0) {
            reserve += reserve_adj;
            if (reserve < 0) {
                reserve = 0;
            }
        }

        //put LMK_TARGET,lmkd會解析這個
        //put mOomMinFree:minfree
        //put mOomAdj對應的adj值
        if (write) {
            ByteBuffer buf = ByteBuffer.allocate(4 * (2*mOomAdj.length + 1));
            buf.putInt(LMK_TARGET);
            for (int i=0; i<mOomAdj.length; i++) {
                buf.putInt((mOomMinFree[i]*1024)/PAGE_SIZE);
                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
    }

2.2 什麼時候ams會設定lmkd的閾值

applyDisplaySize會呼叫updateOomLevels修改lmkd閾值

    void applyDisplaySize(WindowManagerService wm) {
        if (!mHaveDisplaySize) {
            Point p = new Point();
            // TODO(multi-display): Compute based on sum of all connected displays' resolutions.
            wm.getBaseDisplaySize(Display.DEFAULT_DISPLAY, p);
            if (p.x != 0 && p.y != 0) {
                updateOomLevels(p.x, p.y, true);
                mHaveDisplaySize = true;
            }
        }
    }

3. AMS設定當前程序adj值(/proc/pid/adj)

設定pid的adj值

    /**
     * Set the out-of-memory badness adjustment for a process.
     *
     * @param pid The process identifier to set.
     * @param uid The uid of the app
     * @param amt Adjustment value -- lmkd allows -16 to +15.
     *
     * {@hide}
     */
    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);  //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);
        }
    }

applyOomAdjLocked會判斷如果adj變化會重新設定setOomAdj

    private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
            long nowElapsed) {
        boolean success = true;

        if (app.curRawAdj != app.setRawAdj) {
            String seempStr = "app_uid=" + app.uid
                + ",app_pid=" + app.pid + ",oom_adj=" + app.curAdj
                + ",setAdj=" + app.setAdj + ",hasShownUi=" + (app.hasShownUi ? 1 : 0)
                + ",cached=" + (app.cached ? 1 : 0)
                + ",fA=" + (app.foregroundActivities ? 1 : 0)
                + ",fS=" + (app.foregroundServices ? 1 : 0)
                + ",systemNoUi=" + (app.systemNoUi ? 1 : 0)
                + ",curSchedGroup=" + app.curSchedGroup
                + ",curProcState=" + app.curProcState + ",setProcState=" + app.setProcState
                + ",killed=" + (app.killed ? 1 : 0) + ",killedByAm=" + (app.killedByAm ? 1 : 0)
                + ",debugging=" + (app.debugging ? 1 : 0);
            android.util.SeempLog.record_str(385, seempStr);
            app.setRawAdj = app.curRawAdj;
        }

        int changes = 0;

        if (app.curAdj != app.setAdj) {
            ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);
            if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mCurOomAdjUid == app.info.uid) {
                String msg = "Set " + app.pid + " " + app.processName + " adj "
                        + app.curAdj + ": " + app.adjType;

4. lmkd接收訊息原理

ams和lmkd程序通過socket連線併發送資料

    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;
    }

    private static void writeLmkd(ByteBuffer buf) {
        //3次建立連線機會
        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;
            }
        }
    }
}

Lmkd編譯到/system/bin/lmkd

@system/core/lmkd/Android.bp
cc_binary {
    name: "lmkd",

    srcs: ["lmkd.c"],
    shared_libs: [
        "liblog",
        "libprocessgroup",
        "libcutils",
    ],
    cflags: ["-Werror"],

    init_rc: ["lmkd.rc"],
}

lmkd是init core的時候啟動,定義了socket lmkd

service lmkd /system/bin/lmkd
    class core
    group root readproc
    critical
    socket lmkd seqpacket 0660 system system
    writepid /dev/cpuset/system-background/tasks
@system/core/lmkd/lmkd.c
enum lmk_cmd {
    LMK_TARGET,
    LMK_PROCPRIO,
    LMK_PROCREMOVE,
};

//處理邏輯的地方
static void ctrl_command_handler(void) {
    int ibuf[CTRL_PACKET_MAX / sizeof(int)];
    int len;
    int cmd = -1;
    int nargs;
    int targets;

    len = ctrl_data_read((char *)ibuf, CTRL_PACKET_MAX);
    if (len <= 0)
        return;

    nargs = len / sizeof(int) - 1;
    if (nargs < 0)
        goto wronglen;

    cmd = ntohl(ibuf[0]);

    switch(cmd) {
    case LMK_TARGET:  //設定INKERNEL_MINFREE_PATH值
        targets = nargs / 2;
        if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj))
            goto wronglen;
        cmd_target(targets, &ibuf[1]);
        break;
    case LMK_PROCPRIO: //設定/proc/pid值
        if (nargs != 3)
            goto wronglen;
        cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]), ntohl(ibuf[3]));
        break;
    case LMK_PROCREMOVE: //刪除值
        if (nargs != 1)
            goto wronglen;
        cmd_procremove(ntohl(ibuf[1]));
        break;
    default:
        ALOGE("Received unknown command code %d", cmd);
        return;
    }

    return;

wronglen:
    ALOGE("Wrong control socket read length cmd=%d len=%d", cmd, len);
}

寫ams傳過來的adj和minfree值,可見ams只是發起寫命令,lmkd才是寫的人

#define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
#define INKERNEL_ADJ_PATH "/sys/module/lowmemorykiller/parameters/adj"

static void cmd_target(int ntargets, int *params) {
    int i;

    if (ntargets > (int)ARRAY_SIZE(lowmem_adj))
        return;

    for (i = 0; i < ntargets; i++) {
        lowmem_minfree[i] = ntohl(*params++);
        lowmem_adj[i] = ntohl(*params++);
    }

    lowmem_targets_size = ntargets;

    if (has_inkernel_module) {
        char minfreestr[128];
        char killpriostr[128];

        minfreestr[0] = '\0';
        killpriostr[0] = '\0';

        for (i = 0; i < lowmem_targets_size; i++) {
            char val[40];

            if (i) {
                strlcat(minfreestr, ",", sizeof(minfreestr));
                strlcat(killpriostr, ",", sizeof(killpriostr));
            }

            snprintf(val, sizeof(val), "%d", use_inkernel_interface ? lowmem_minfree[i] : 0);
            strlcat(minfreestr, val, sizeof(minfreestr));
            snprintf(val, sizeof(val), "%d", use_inkernel_interface ? lowmem_adj[i] : 0);
            strlcat(killpriostr, val, sizeof(killpriostr));
        }

        writefilestring(INKERNEL_MINFREE_PATH, minfreestr); //write minfree
        writefilestring(INKERNEL_ADJ_PATH, killpriostr); //write adj
    }
}

LMK_PROCPRIO: cmd_procprio向/proc%d/oom_score_adj寫值

static void cmd_procprio(int pid, int uid, int oomadj) {
    struct proc *procp;
    char path[80];
    char val[20];
    int soft_limit_mult;

    if (oomadj < OOM_SCORE_ADJ_MIN || oomadj > OOM_SCORE_ADJ_MAX) {
        ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj);
        return;
    }

    snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid);
    snprintf(val, sizeof(val), "%d", oomadj);
    writefilestring(path, val);
...
}

5. lmkd kernel監測系統記憶體

static int __initlowmem_init(void)
{
    register_shrinker(&lowmem_shrinker);
    return 0;
}

static struct shrinker lowmem_shrinker = {
    .shrink = lowmem_shrink,
    .seeks = DEFAULT_SEEKS * 16
};

初始化lowmemorykiller,註冊cache shrinker,當空閒記憶體頁面不足時,核心程序kswpd會呼叫註冊的shrink回撥函式來回收頁面,在Android上,lowmemorykiller,是通過minifree與adj陣列來確認需要殺死的低優先順序程序,以此來釋放記憶體

例如,一組minifree與adj的閾值資料如下:

minifree:

15360,19200,23040,26880,34415,43737

adj:

0,58,117,176,529,1000

當記憶體低於43737x4KB時,會殺死adj大於1000的程序,當記憶體低於34415x4KB時,會殺死adj大於529的程序

static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask)
{
    struct task_struct *p;
    struct task_struct *selected = NULL;
    int rem = 0;
    int tasksize;
    int i;
    int min_adj = OOM_ADJUST_MAX + 1;
    int selected_tasksize = 0;
    int selected_oom_adj;
    int array_size = ARRAY_SIZE(lowmem_adj);
    // 獲取當前可用記憶體頁數
    int other_free = global_page_state(NR_FREE_PAGES);
    int other_file = global_page_state(NR_FILE_PAGES);

    if (lowmem_adj_size < array_size)
        array_size = lowmem_adj_size;
    if (lowmem_minfree_size < array_size)
        array_size = lowmem_minfree_size;
    for (i = 0; i < array_size; i++) {
        if (other_free < lowmem_minfree[i] &&
            other_file < lowmem_minfree[i]) {
           // 獲取需要被清除的adj閾值
            min_adj = lowmem_adj[i];
            break;
        }
    }

    rem = global_page_state(NR_ACTIVE_ANON) +
        global_page_state(NR_ACTIVE_FILE) +
        global_page_state(NR_INACTIVE_ANON) +
        global_page_state(NR_INACTIVE_FILE);

    selected_oom_adj = min_adj;

    read_lock(&tasklist_lock);
    // 遍歷每個程序
    for_each_process(p) {
        struct mm_struct *mm;
        int oom_adj;

        task_lock(p);
        mm = p->mm;
        if (!mm) {
            task_unlock(p);
            continue;
        }
        oom_adj = mm->oom_adj;
        if (oom_adj < min_adj) {
            task_unlock(p);
            continue;
        }
        tasksize = get_mm_rss(mm);
        task_unlock(p);
        if (tasksize <= 0)
            continue;
        if (selected) {
            // 當adj小於閾值時,不殺程序
            if (oom_adj < selected_oom_adj)
                continue;
            if (oom_adj == selected_oom_adj &&
                tasksize <= selected_tasksize)
                continue;
        }
        selected = p;
        selected_tasksize = tasksize;
        selected_oom_adj = oom_adj;
        lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n",
                 p->pid, p->comm, oom_adj, tasksize);
    }
    // 殺死所選的低優先順序程序
    if (selected) {
        lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n",
                 selected->pid, selected->comm,
                 selected_oom_adj, selected_tasksize);
        force_sig(SIGKILL, selected);
        rem -= selected_tasksize;
    }
    lowmem_print(4, "lowmem_shrink %d, %x, return %d\n",
             nr_to_scan, gfp_mask, rem);
    read_unlock(&tasklist_lock);
    return rem;
}