Android(2)의 앱 메모리 재활용 최적화: S 버전

버전 기반: Android S

0. 서문

Android Q에는 프레임워크 측 앱 메모리 재활용 최적화 솔루션이 추가되었습니다. 앱의 oom adj가 구체적으로 변경되면 프레임워크 측에서 애플리케이션의 메모리를 처리합니다. 버전이 발전하면서 이 부분의 최적화 작업이 개선되었는데, 저자는 이 부분의 최적화 과정을 안드로이드 R안드로이드 S 각각에 대해 자세히 분석해 보겠습니다.

이전 글 에서는 안드로이드 R 버전 에 대한  상세한 분석을 진행하였고 , 이번 글에서는 계속해서 안드로이드 S 버전에 대한 분석을 진행한다.

알아채다:

본 글에서 언급된 "압축"이라는 단어는 실제로 메모리 재활용 최적화를 의미하는 것으로, 정확한 로직이 밝혀져야만 익명 페이지 재활용인지 파일 페이지 재활용인지 명확해지기 때문입니다. 그 전에는 컴팩트를 사용하기로 잠정 결정했습니다. 처리.

또한, 본 글의 분석 내용을 안드로이드 R 버전과 비교하고, 마지막으로 두 버전 간의 차이점과 장점, 단점을 정리한다.

1. CachedAppOptimizer 클래스

앱 측의 메모리 압축 관리는 CachedAppOptimizer 클래스에서 완료됩니다.

이전 블로그 게시물 "oom_adj 업데이트 원리 (1)""oom_adj 업데이트 원리 (2)" 에서 AMS가 OomAdjuster 클래스를 통해 oom_adj의 업데이트, 계산 및 적용을 관리한다는 것을 배웠습니다. CachedAppOptimizer 개체는 OomAdjuster에서도 인스턴스화됩니다.

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

    OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids,
            ServiceThread adjusterThread) {
        mService = service;
        ...
        mCachedAppOptimizer = new CachedAppOptimizer(mService);
        ...
    }

매개변수는 AMS 개체입니다.

CachedAppOptimizer 구조를 살펴보겠습니다  .

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

    public CachedAppOptimizer(ActivityManagerService am) {
        this(am, null, new DefaultProcessDependencies());
    }

    @VisibleForTesting
    CachedAppOptimizer(ActivityManagerService am, PropertyChangedCallbackForTest callback,
            ProcessDependencies processDependencies) {
        mAm = am;
        mProcLock = am.mProcLock;
        mCachedAppOptimizerThread = new ServiceThread("CachedAppOptimizerThread",
            mCompactionPriority, true);
        mProcStateThrottle = new HashSet<>();
        mProcessDependencies = processDependencies;
        mTestCallback = callback;
        mSettingsObserver = new SettingsContentObserver();
        mProcLocksReader = new ProcLocksReader();
    }

ServiceThread는 이름이 CachedAppOptimizerThread이고 우선순위가 THREAD_GROUP_SYSTEM 인 구성에서 생성됩니다 .

mProcessDependency는 최종 압축 처리에 사용되는 DefaultProcessDependency 유형의 개체입니다.

안드로이드 R과의 차이점 :

  • AMS의 프로세스 관리를 동기화하기 위해 새로운 mProcLock 변수가 추가되었습니다 .
  • 캐시된_apps_freezer의 변경 사항을 모니터링하기 위해 새로운 mSettingsObserver 변수가 추가되었습니다  .
  • 프로세스가 정지되었을 때 /proc/locks 정보를 볼 수 있도록 새로운 mProcLocksReader 변수가 추가되었습니다  .

2. 초기화()

Android R 버전과 달리 Android R의 init() 함수는 SystemServer .startOtherServices () 및 mOomAdjuster .initSettings () 가 installSystemProviders()에서 호출될 때 AMS에서installSystemProviders() 함수를 호출하여 트리거됩니다.

Android S에서는 installSystemProviders()가 ContentProviderHelper .java 에 정의되어 있습니다 .

CachedAppOptimizer .init ()는 OomAdjuster .initSettings () 에서 호출됩니다 .

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

    public void init() {
        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                ActivityThread.currentApplication().getMainExecutor(), mOnFlagsChangedListener);
        DeviceConfig.addOnPropertiesChangedListener(
                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
                ActivityThread.currentApplication().getMainExecutor(),
                mOnNativeBootFlagsChangedListener);
        mAm.mContext.getContentResolver().registerContentObserver(
                CACHED_APP_FREEZER_ENABLED_URI, false, mSettingsObserver);
        synchronized (mPhenotypeFlagLock) {
            updateUseCompaction();
            updateCompactionActions();
            updateCompactionThrottles();
            updateCompactStatsdSampleRate();
            updateFreezerStatsdSampleRate();
            updateFullRssThrottle();
            updateFullDeltaRssThrottle();
            updateProcStateThrottle();
            updateUseFreezer();
            updateMinOomAdjThrottle();
            updateMaxOomAdjThrottle();
        }
    }

Android R과 비교하면 고정된 속성에 대한 추가 리스너인 mOnNativeBootFlagsChangedListener가 있습니다.

게다가 마지막에 두 가지 업데이트 기능이 더 있습니다.

2.1 updateUseCompaction()

    private void updateUseCompaction() {
        mUseCompaction = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                    KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION);

        mCompactionPriority = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                    KEY_COMPACTION_PRIORITY, Process.THREAD_GROUP_BACKGROUND);

        if (mUseCompaction && mCompactionHandler == null) {
            if (!mCachedAppOptimizerThread.isAlive()) {
                mCachedAppOptimizerThread.start();
            }

            mCompactionHandler = new MemCompactionHandler();
        }

        Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(),
                mCompactionPriority);
    }

Android R과 비교하여 여기에서는 스레드 우선순위가 동적으로 구성되며 DeviceConfig의 Compaction_priority 속성을 통해 우선순위를 설정할 수 있습니다. 또한 우선순위 설정은 mUsecompaction 활성화 여부에 따라 달라지지 않습니다.

다른 모든 것은 R과 동일합니다. 먼저 use_compaction 속성을 가져옵니다. 기본값은 DEFAULT_USE_COMPACTION( false )입니다.

    @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;

user_compation이 활성화되면 비동기 메시지 처리를 위해 mCompactionHandler가 생성되고 생성자에서 생성된 ServiceThread가 시작됩니다.

MemCompactionHandler 클래스를 살펴보겠습니다.

    private final class MemCompactionHandler extends Handler {
        private MemCompactionHandler() {
            super(mCachedAppOptimizerThread.getLooper());
        }
 
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case COMPACT_PROCESS_MSG: {
                    ...
                    break;
                }
                case COMPACT_SYSTEM_MSG: {
                    ...
                    break;
                }
            }
        }

Looper는 주로 압축된 프로세스 메모리나 시스템 프로세스 메모리를 처리하는데 사용되는 ServiceThread의 Looper를 사용합니다.

2.2 updateCompactionActions()

    private void updateCompactionActions() {
        int compactAction1 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                KEY_COMPACT_ACTION_1, DEFAULT_COMPACT_ACTION_1);

        int compactAction2 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                KEY_COMPACT_ACTION_2, DEFAULT_COMPACT_ACTION_2);

        mCompactActionSome = compactActionIntToString(compactAction1);
        mCompactActionFull = compactActionIntToString(compactAction2);
    }

안드로이드 R과 동일합니다

2.3 updateCompactionThrottles()

    private void updateCompactionThrottles() {
        boolean useThrottleDefaults = false;
        // TODO: improve efficiency by calling DeviceConfig only once for all flags.
        String throttleSomeSomeFlag =
                DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                    KEY_COMPACT_THROTTLE_1);
        String throttleSomeFullFlag =
                DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                    KEY_COMPACT_THROTTLE_2);
        String throttleFullSomeFlag =
                DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                    KEY_COMPACT_THROTTLE_3);
        String throttleFullFullFlag =
                DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                    KEY_COMPACT_THROTTLE_4);
        String throttleBFGSFlag =
                DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                    KEY_COMPACT_THROTTLE_5);
        String throttlePersistentFlag =
                DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                    KEY_COMPACT_THROTTLE_6);
        String throttleMinOomAdjFlag =
                DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                    KEY_COMPACT_THROTTLE_MIN_OOM_ADJ);
        String throttleMaxOomAdjFlag =
                DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                    KEY_COMPACT_THROTTLE_MAX_OOM_ADJ);

        if (TextUtils.isEmpty(throttleSomeSomeFlag) || TextUtils.isEmpty(throttleSomeFullFlag)
                || TextUtils.isEmpty(throttleFullSomeFlag)
                || TextUtils.isEmpty(throttleFullFullFlag)
                || TextUtils.isEmpty(throttleBFGSFlag)
                || TextUtils.isEmpty(throttlePersistentFlag)
                || TextUtils.isEmpty(throttleMinOomAdjFlag)
                || TextUtils.isEmpty(throttleMaxOomAdjFlag)) {
            // Set defaults for all if any are not set.
            useThrottleDefaults = true;
        } else {
            try {
                mCompactThrottleSomeSome = Integer.parseInt(throttleSomeSomeFlag);
                mCompactThrottleSomeFull = Integer.parseInt(throttleSomeFullFlag);
                mCompactThrottleFullSome = Integer.parseInt(throttleFullSomeFlag);
                mCompactThrottleFullFull = Integer.parseInt(throttleFullFullFlag);
                mCompactThrottleBFGS = Integer.parseInt(throttleBFGSFlag);
                mCompactThrottlePersistent = Integer.parseInt(throttlePersistentFlag);
                mCompactThrottleMinOomAdj = Long.parseLong(throttleMinOomAdjFlag);
                mCompactThrottleMaxOomAdj = Long.parseLong(throttleMaxOomAdjFlag);
            } catch (NumberFormatException e) {
                useThrottleDefaults = true;
            }
        }

        if (useThrottleDefaults) {
            mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1;
            mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2;
            mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3;
            mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4;
            mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5;
            mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6;
            mCompactThrottleMinOomAdj = DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ;
            mCompactThrottleMaxOomAdj = DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ;
        }
    }

Android R과 비교하면 adj에 대한 제한 값이 두 개 더 있습니다. mCompactThrottleMinOomAdj와 mCompactThrottleMaxOomAdj입니다. FULL로 압축하는 경우 마지막 oom_adj가 캐시되지 않아야 합니다. R 버전에서는 이러한 조건부 판단이 OomAdjuster.applyOomAdjLocked() 함수에 배치되지만, S 버전에서는 여기에 배치됩니다. 저자는 이 디자인의 원래 의도가 사용자가 FULL 압축된 조정을 제어할 수 있도록 하는 것이라고 추측하지만, applyOomAdjLSP() 함수와 비교하면 Android R이 더 좋습니다.

2.4 업데이트FullRssThrottle()

    private void updateFullRssThrottle() {
        mFullAnonRssThrottleKb = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                KEY_COMPACT_FULL_RSS_THROTTLE_KB, DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);

        // Don't allow negative values. 0 means don't apply the throttle.
        if (mFullAnonRssThrottleKb < 0) {
            mFullAnonRssThrottleKb = DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB;
        }
    }

Android R 버전과 동일합니다 .

2.5 업데이트FullDeltaRssThrottle()

    private void updateFullDeltaRssThrottle() {
        mFullDeltaRssThrottleKb = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);

        if (mFullDeltaRssThrottleKb < 0) {
            mFullDeltaRssThrottleKb = DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB;
        }
    }

안드로이드 R 버전과 동일

2.6 업데이트ProcStateThrottle()

    private void updateProcStateThrottle() {
        String procStateThrottleString = DeviceConfig.getString(
                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_PROC_STATE_THROTTLE,
                DEFAULT_COMPACT_PROC_STATE_THROTTLE);
        if (!parseProcStateThrottle(procStateThrottleString)) {
            Slog.w(TAG_AM, "Unable to parse app compact proc state throttle \""
                    + procStateThrottleString + "\" falling back to default.");
            if (!parseProcStateThrottle(DEFAULT_COMPACT_PROC_STATE_THROTTLE)) {
                Slog.wtf(TAG_AM,
                        "Unable to parse default app compact proc state throttle "
                                + DEFAULT_COMPACT_PROC_STATE_THROTTLE);
            }
        }
    }

안드로이드 R 버전과 동일

2.7 updateUseFreezer()

프로세스 동결 상태가 초기화되며 동결 최적화는 나중에 별도로 분석됩니다.

2.8 업데이트MinOomAdjThrottle()

    private void updateMinOomAdjThrottle() {
        mCompactThrottleMinOomAdj = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
            KEY_COMPACT_THROTTLE_MIN_OOM_ADJ, DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ);

        // Should only compact cached processes.
        if (mCompactThrottleMinOomAdj < ProcessList.CACHED_APP_MIN_ADJ) {
            mCompactThrottleMinOomAdj = DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ;
        }
    }

    private void updateMaxOomAdjThrottle() {
        mCompactThrottleMaxOomAdj = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
            KEY_COMPACT_THROTTLE_MAX_OOM_ADJ, DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ);

        // Should only compact cached processes.
        if (mCompactThrottleMaxOomAdj > ProcessList.CACHED_APP_MAX_ADJ) {
            mCompactThrottleMaxOomAdj = DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ;
        }
    }

이 두 가지 기능은 Android S에 추가되었으며 주로 FULL 압축을 판단하는 데 사용됩니다.

3. 압축 최적화 개시자

압축 최적화는 다양한 상황에서 실행됩니다.CachedAppOptimizer에서 압축 최적화 개시자는 주로 다음을 포함합니다.

  • 컴팩트앱썸()
  • 컴팩트앱전체()
  • 컴팩트앱지속성()
  • 컴팩트앱Bfgs()
  • 컴팩트올시스템()

중요한 로직은 Android R 버전과 유사하며, 이 글에서는 처음 두 가지 기능을 주로 분석합니다.

3.1 컴팩트앱썸()

OomAdjuster .applyOomAdjLSP() 에서 트리거됨 :

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

        if (mCachedAppOptimizer.useCompaction() && mService.mBooted) {
            // Cached and prev/home compaction
            if (state.getCurAdj() != state.getSetAdj()) {
                // Perform a minor compaction when a perceptible app becomes the prev/home app
                // Perform a major compaction when any app enters cached
                // reminder: here, setAdj is previous state, curAdj is upcoming state
                if (state.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ
                        && (state.getCurAdj() == ProcessList.PREVIOUS_APP_ADJ
                            || state.getCurAdj() == ProcessList.HOME_APP_ADJ)) {
                    mCachedAppOptimizer.compactAppSome(app);
                } else if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ
                        && state.getCurAdj() <= ProcessList.CACHED_APP_MAX_ADJ) {
                    mCachedAppOptimizer.compactAppFull(app);
                }
            } else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
                    && state.getSetAdj() < ProcessList.FOREGROUND_APP_ADJ
                    // Because these can fire independent of oom_adj/procstate changes, we need
                    // to throttle the actual dispatch of these requests in addition to the
                    // processing of the requests. As a result, there is throttling both here
                    // and in CachedAppOptimizer.
                    && mCachedAppOptimizer.shouldCompactPersistent(app, now)) {
                mCachedAppOptimizer.compactAppPersistent(app);
            } else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
                    && state.getCurProcState()
                        == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
                    && mCachedAppOptimizer.shouldCompactBFGS(app, now)) {
                mCachedAppOptimizer.compactAppBfgs(app);
            }
        }

        ...
    }

Andoird R 버전과 비교하면 여기서는 FULL 압축의 로직이 조정되었으며, R에서 Non-cached 에서 Cached 로 판단하는 것은 현재 캐시된 상태에만 관심을 갖고 CompactAppFull()에서 확인하도록 변경되었습니다. 저자는 이것이 단순히 조건을 완전히 CompactAppFull()에 맡긴다고 생각하는데, 글쎄요, 디자인의 목적은 모르겠습니다.

여기서는 논리적 분석을 생략하였으며,  자세한 내용은 안드로이드 R 버전에서 확인하실 수 있습니다 .

    void compactAppSome(ProcessRecord app) {
        app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_SOME);
        if (!app.mOptRecord.hasPendingCompact()) {
            app.mOptRecord.setHasPendingCompact(true);
            mPendingCompactionProcesses.add(app);
            mCompactionHandler.sendMessage(
                    mCompactionHandler.obtainMessage(
                    COMPACT_PROCESS_MSG, app.mState.getSetAdj(), app.mState.getSetProcState()));
        }
    }

Android R과 비교하여 ProcessRecord 클래스에는 mOptRecord 유형의 추가 멤버 변수가 있습니다.

ProcessCachedOptimizerRecord는 CachedAppOptimizer 클래스와 협력하도록 설계되었습니다.

그 외 로직은 Android R과 동일합니다.

3.2 컴팩트앱풀()

    void compactAppFull(ProcessRecord app) {
        // Apply OOM adj score throttle for Full App Compaction.
        if ((app.mState.getSetAdj() < mCompactThrottleMinOomAdj
                || app.mState.getSetAdj() > mCompactThrottleMaxOomAdj)
                && app.mState.getCurAdj() >= mCompactThrottleMinOomAdj
                && app.mState.getCurAdj() <= mCompactThrottleMaxOomAdj) {
            app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_FULL);
            if (!app.mOptRecord.hasPendingCompact()) {
                app.mOptRecord.setHasPendingCompact(true);
                mPendingCompactionProcesses.add(app);
                mCompactionHandler.sendMessage(
                        mCompactionHandler.obtainMessage(
                        COMPACT_PROCESS_MSG, app.mState.getSetAdj(), app.mState.getSetProcState()));
            }
        } else {
            if (DEBUG_COMPACTION) {
                Slog.d(TAG_AM, "Skipping full compaction for " + app.processName
                        + " oom adj score changed from " + app.mState.getSetAdj()
                        + " to " + app.mState.getCurAdj());
            }
        }
    }

3.1절에서 언급한 대로 여기에는 추가적인 조건부 판단이 추가됩니다.

4. 압축된 메시지 처리

메시지 처리 로직은 MemCompactionHandler .handleMessage() 함수에 위치하며 로직 은 기본적으로 Android R 버전과 동일하므로 여기서는 자세히 분석하지 않겠습니다.

주목해야 할 두 가지 사항이 있습니다.

  • 비시스템 작업을 처리할 때 마지막으로 PerformCompaction() 함수가 호출됩니다.
  • 시스템의 작업을 처리할 때 최종적으로 CompactSystem() 함수가 호출됩니다.

4.1 수행컴팩션()

        public void performCompaction(String action, int pid) throws IOException {
            if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])) {
                compactProcess(pid, COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG);
            } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FILE])) {
                compactProcess(pid, COMPACT_ACTION_FILE_FLAG);
            } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) {
                compactProcess(pid, COMPACT_ACTION_ANON_FLAG);
            }
        }

Android R 버전과 달리 기본 인터페이스는 여기에서 호출됩니다.

static private native void compactProcess(int pid, int compactionFlags);
frameworks/base/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp

static void com_android_server_am_CachedAppOptimizer_compactProcess(JNIEnv*, jobject, jint pid,
                                                                    jint compactionFlags) {
    compactProcessOrFallback(pid, compactionFlags);
}

---->

CompactProcessOrFallback()

frameworks/base/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp

static void compactProcessOrFallback(int pid, int compactionFlags) {
    // 入参compactionFlags 必须是ANON_FLAG 或 FILE_FLAG中的一种或两种
    if ((compactionFlags & (COMPACT_ACTION_ANON_FLAG | COMPACT_ACTION_FILE_FLAG)) == 0) return;

    // 确认是否有 anon 页回收,还是有file 页回收
    bool compactAnon = compactionFlags & COMPACT_ACTION_ANON_FLAG;
    bool compactFile = compactionFlags & COMPACT_ACTION_FILE_FLAG;

    // Set when the system does not support process_madvise syscall to avoid
    // gathering VMAs in subsequent calls prior to falling back to procfs
    static bool shouldForceProcFs = false;
    std::string compactionType;
    VmaToAdviseFunc vmaToAdviseFunc;

    // 根据压缩action,确认compactionType 和 回调函数
    //   回调函数后面会调用到,主要返回 process_madvise() 所需要的类型 MADV_COLD 或MADV_PAGEOUT
    if (compactAnon) {
        if (compactFile) {
            compactionType = "all";
            vmaToAdviseFunc = getAnyPageAdvice;
        } else {
            compactionType = "anon";
            vmaToAdviseFunc = getAnonPageAdvice;
        }
    } else {
        compactionType = "file";
        vmaToAdviseFunc = getFilePageAdvice;
    }

    // 如果compactProcess() 失败,即系统不支持 process_madvise()系统调用时,
    //    通过shouldForceProcFs 变量控制,强制走 proc/PID/reclaim 节点驱动
    if (shouldForceProcFs || compactProcess(pid, vmaToAdviseFunc) == -ENOSYS) {
        shouldForceProcFs = true;
        compactProcessProcfs(pid, compactionType);
    }
}

---->

컴팩트프로세스()

frameworks/base/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp

static int64_t compactProcess(int pid, VmaToAdviseFunc vmaToAdviseFunc) {
    ProcMemInfo meminfo(pid);
    std::vector<Vma> pageoutVmas, coldVmas;
    auto vmaCollectorCb = [&coldVmas,&pageoutVmas,&vmaToAdviseFunc](const Vma& vma) {
        int advice = vmaToAdviseFunc(vma); //通过回调确定madvise的类型MADV_PAGEOUT或MADV_COLD
        switch (advice) {
            case MADV_COLD:
                coldVmas.push_back(vma);
                break;
            case MADV_PAGEOUT:
                pageoutVmas.push_back(vma);
                break;
        }
    };

    // 读取 /proc/PID/maps 确定vma并调用回调函数 vmaClooectorCb
    meminfo.ForEachVmaFromMaps(vmaCollectorCb);

    // 对进程中统计出来的 pageoutVma 进行 MADV_PAGEOUT 处理
    int64_t pageoutBytes = compactMemory(pageoutVmas, pid, MADV_PAGEOUT);
    if (pageoutBytes < 0) {
        // Error, just forward it.
        return pageoutBytes;
    }

    // 对进程中统计出来的 coldVma 进行MADV_COLD处理
    int64_t coldBytes = compactMemory(coldVmas, pid, MADV_COLD);
    if (coldBytes < 0) {
        // Error, just forward it.
        return coldBytes;
    }

    return pageoutBytes + coldBytes;
}

---->

컴팩트메모리()

frameworks/base/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp

static int64_t compactMemory(const std::vector<Vma>& vmas, int pid, int madviseType) {
    // UIO_MAXIOV is currently a small value and we might have more addresses
    // we do multiple syscalls if we exceed its maximum
    static struct iovec vmasToKernel[UIO_MAXIOV];

    if (vmas.empty()) {
        return 0;
    }

    unique_fd pidfd(pidfd_open(pid, 0));
    if (pidfd < 0) {
        // Skip compaction if failed to open pidfd with any error
        return -errno;
    }

    int64_t totalBytesCompacted = 0;
    for (int iBase = 0; iBase < vmas.size(); iBase += UIO_MAXIOV) {
        int totalVmasToKernel = std::min(UIO_MAXIOV, (int)(vmas.size() - iBase));
        for (int iVec = 0, iVma = iBase; iVec < totalVmasToKernel; ++iVec, ++iVma) {
            vmasToKernel[iVec].iov_base = (void*)vmas[iVma].start;
            vmasToKernel[iVec].iov_len = vmas[iVma].end - vmas[iVma].start;
        }

        auto bytesCompacted =
                process_madvise(pidfd, vmasToKernel, totalVmasToKernel, madviseType, 0);
        if (CC_UNLIKELY(bytesCompacted == -1)) {
            return -errno;
        }

        totalBytesCompacted += bytesCompacted;
    }

    return totalBytesCompacted;
}

 각 vma는 process_madvise()를 사용하여 최종적으로 처리됩니다.

4.2 컴팩트시스템()

frameworks/base/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp

static void com_android_server_am_CachedAppOptimizer_compactSystem(JNIEnv *, jobject) {
    std::unique_ptr<DIR, decltype(&closedir)> proc(opendir("/proc"), closedir);
    struct dirent* current;
    while ((current = readdir(proc.get()))) {
        if (current->d_type != DT_DIR) {
            continue;
        }

        // don't compact system_server, rely on persistent compaction during screen off
        // in order to avoid mmap_sem-related stalls
        if (atoi(current->d_name) == getpid()) {
            continue;
        }

        std::string status_name = StringPrintf("/proc/%s/status", current->d_name);
        struct stat status_info;

        if (stat(status_name.c_str(), &status_info) != 0) {
            // must be some other directory that isn't a pid
            continue;
        }

        // android.os.Process.FIRST_APPLICATION_UID
        if (status_info.st_uid >= 10000) {
            continue;
        }

        int pid = atoi(current->d_name);

        compactProcessOrFallback(pid, COMPACT_ACTION_ANON_FLAG | COMPACT_ACTION_FILE_FLAG);
    }
}

최종 호출은 또한 CompactProcessOrFallback() 함수입니다.

5. 프로세스_마드바이스()

bionic/libc/include/sys/mman.h

ssize_t process_madvise(int picfd, const struct iovec* iovec, size_t vlen, int advice, unsigned flags);

process_madvise() 시스템 호출은 커널에서 프로세스 주소 영역 처리에 관한 제안이나 지침을 제공하는 데 사용됩니다.

Linux 5.10 버전을 지원해야 합니다.

이러한 제안은 시스템 또는 응용 프로그램 성능을 향상시키기 위한 것입니다.

주소 영역은 iovec 및 vlen 구조를 통해 관리됩니다.

매개변수:

  • pidfd: 프로세스를 지정하는 데 사용되는 PID의 파일 설명자입니다.
  • iovec: 구조 배열에 대한 포인터입니다.
  • vlen: 구조 배열의 길이를 지정합니다. 이 값은 IOV_MAX 를 초과할 수 없습니다 .
  • 조언: 다음 유형 중 하나:
    • Linux 5.4부터 MADV_COLD는 지정된 페이지 범위를 비활성화하는 데 사용됩니다.
    • MADV_COLLAPSE;
    • Linux 5.4부터 MADV_PAGEOUT은 지정된 페이지 범위를 재생하는 데 사용됩니다.
    • MADV_WILLNEED;
  • 플래그: 예약되어 있으며 현재 기본적으로 0으로 설정되어 있습니다.

여기서 자세한 커널 호출은 크게 분석하지 않겠습니다. 모두 walk_page_range()를 호출하고, walk_ops도 cold_walk_ops입니다. 그러나 조언에 따라 논리가 약간 다릅니다(비활성화 및 회수 논리).

 

 

이제 Android S 버전의 앱 재활용 최적화에 대한 모든 분석이 완료되었으며 요약하면 다음과 같습니다.

  • CachedAppOptimizer를 사용하여 앱 압축을 관리하고 다음과 같은 주요 방법으로 압축 인터페이스를 트리거합니다.
    • 조정이 변경될 때 마지막 조정 <=  PERCEPTIBLE_APP_ADJ가 PREVIOUS 또는 HOME 이 되면 일부 압축 최적화를 사용하세요.
    • adj가 변경되면 지난번에 CACHED되지 않았으면 CACHED가 되고 FULL 압축 최적화가 사용됩니다.
    • 비활성 상태이고 마지막 조정(setAdj)이 전경 조정( FOEGROUND_APP_ADJ )보다 더 중요하며 마지막 압축 간격이 10분을 초과하거나 압축된 적이 없는 경우  CompactAppPercious()  압축을 사용합니다. 최적화;
    • 활성화되지 않은 상태이고 애플리케이션 프로세스 상태가  PROCESS_STATE_BOUND_FOREGROUND_SERVICE 이고 마지막 압축 이후 간격이 10분을 초과하거나 압축된 적이 없는 경우  CompactAppBfgs()  압축 최적화를 사용하십시오.
    • AMS에서 FinishBooting()이 호출되면, 즉 시스템 시작이 완료된 후 시스템의 모든 프로세스가 완전히 압축되고 CompactAllSystem()이 호출됩니다 .
    • MountServiceIdler는 startJob()이 시스템 압축을 수행할 때마다 AMS .performIdleMaintenance() 및 CompactAllSystem()을 호출합니다.
  • CachedAppOptimizer는 압축이 호출된 후 전송된 메시지를 처리하기 위해 ServiceThread를 유지 관리합니다.
  • 비시스템 압축은 공식적으로 압축 처리 기능에 들어가기 전에 많은 제한 사항이 필요합니다.
  • 압축 작업은 압축 전에 병합됩니다. 일부는 mCompactActionSome 압축 방법을 사용하고 FULL / PERSISTENT / BFGS는 모두 mCompactActionFull 압축 방법을 사용합니다.
  • 압축 처리 기능 포인트:
    • PerformCompaction() , 기본 인터페이스 CompactProcess() 호출 시스템이 process_madvise()를 지원하는 경우 최적화를 위해 process_madvise()를 사용 하고 시스템 호출이 지원되지 않는 경우 Android R 메커니즘을 사용하여 /proc/PID/reclaim을 작성합니다. 드라이버 지원이 필요한 노드;
    • CompactSystem() , 이는 기본 호출이며 궁극적으로 위와 동일하게 process_madvise()가 지원되는지 여부를 확인합니다.

Android R 버전의 압축 최적화에 대해서는 이전 블로그 게시물을 참조하세요 .

참고:

https://man7.org/linux/man-pages/man2/process_madvise.2.html

https://man7.org/linux/man-pages/man2/madvise.2.html

https://justinwei.blog.csdn.net/article/details/131591931

Supongo que te gusta

Origin blog.csdn.net/jingerppp/article/details/131769953
Recomendado
Clasificación