Ausführliche Erklärung des Prinzips des App Freezers in Android (2): S-Version

Basierend auf der Version: Android S

0. Vorwort

In den beiden vorherigen Blogbeiträgen „App Memory Recycling Optimization in Android (1)“ und „App Memory Recycling Optimization in Android (2)“ wurde der Prozess der App Memory Optimierung in Android ausführlich analysiert. Die Verwaltung dieses Mechanismus erfolgt über die Klasse CachedAppOptimizer. Warum heißt sie so und nicht AppCompact? Es wurde auch in den beiden vorherigen Blogbeiträgen erwähnt, da diese Klasse auch eine wichtige Funktion verwaltet: Freezer , eine Optimierung für Anwendungsprozesse, die sich längere Zeit im Cached-Zustand befinden.

Im vorherigen Blog-Beitrag „App Freezer-Prinzip R-Version“ wurde der Prozess des Einfrierens / Auftauens von Apps kurz analysiert. Aus Sicht der Codelogik ist der Autor jedoch der Ansicht, dass es in der R-Version einige logische Probleme gibt. Glücklicherweise Sie wurden in der S-Version repariert.

In diesem Artikel wird eine vergleichende Analyse der S-Version basierend auf den Prinzipien der R-Version durchgeführt.

1. Gefrierauslöser

Wie die R-Version wird auch die S-Version durch den Aufruf von updateAppFreezeStateLSP() in  der Funktion applyOomAdjLSP() ausgelöst , um zu bestätigen, ob der Prozess eingefroren werden soll:

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

   private void updateAppFreezeStateLSP(ProcessRecord app) {

        // 确认该功能是否使能,如果没有使能则返回
        if (!mCachedAppOptimizer.useFreezer()) {
            return;
        }

        // S 版本新加的,确认应用是否可以豁免
        if (app.mOptRecord.isFreezeExempt()) {
            return;
        }

        // S 版本中cached进程优化相关的属性,都放到了mOptRecord中管理
        final ProcessCachedOptimizerRecord opt = app.mOptRecord;

        // 如果进程处于frozen状态,但shouldNotFreeze变成true,需要解冻
        if (opt.isFrozen() && opt.shouldNotFreeze()) {
            mCachedAppOptimizer.unfreezeAppLSP(app);
            return;
        }

        // 确定adj,是进入freeze 还是 unfreeze 流程
        final ProcessStateRecord state = app.mState;
        if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ && !opt.isFrozen()
                && !opt.shouldNotFreeze()) {
            mCachedAppOptimizer.freezeAppAsyncLSP(app);
        } else if (state.getSetAdj() < ProcessList.CACHED_APP_MIN_ADJ) {
            mCachedAppOptimizer.unfreezeAppLSP(app);
        }
    }

Die Grundlogik dieser Funktion ähnelt der R-Version, mit einigen geringfügigen Unterschieden:

  • Der Prozess hat die Funktion der Freeze-Ausnahme. Wenn die Anwendung die INSTALL_PACKAGES- Berechtigung festlegt, befindet sich die Anwendung im Ausnahmezustand. Weitere Informationen finden Sie in der Funktion ProcessList.startProcessLocked ( ).
  • Einige Zustände des zwischengespeicherten Prozesses in der S-Version werden von mOptRecord in ProcessRecord einheitlich verwaltet;
  • Wenn schließlich Unfreeze aufgerufen wird, wird nicht mehr beurteilt, ob die App eingefroren ist, da dieser Teil der logischen Beurteilung in der Unfreeze-Funktion beurteilt wird, was den Code prägnanter macht und in der R-Version etwas redundant ist.

2. CachedAppOptimizer.init()

Informationen zum Konstruktionsaufruf von CachedAppOptimizer und zum Auslöseprozess der Funktion init() finden Sie in den Abschnitten  1  und  2   des Artikels „App Memory Recycling Optimization in Android (2)“ .

frameworks/base/services/core/java/com/android/server/am/CachedAppOptimizer.java
 
    public void init() {
        ...
        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) {
            ...
            updateUseFreezer();
            ...
        }
    }

Im Vergleich zur R-Version gibt es zwei weitere Funktionen:

  • Fügen Sie einen neuen  Listener hinzu, dessen freeze_debounce_timeout- Attribut sich ändert. Wenn sich das Attribut ändert, wird updateFreezerDebounceTimeout() zum Aktualisieren aufgerufen.
  • Ein neuer Beobachter, dessen Attributwert „cached_apps_freezer“ sich ändert , wurde hinzugefügt  .

In der R-Version beträgt das Freeze-Timeout 10 Minuten und es wird eine Konstante verwendet. In der S-Version ist dieser Wert variabel und kann vom Benutzer über DeviceConfig geändert werden. Wenn Änderungen auftreten,  wird updateFreezerDebounceTimeout() zur Aktualisierung aufgerufen.

Darüber hinaus wird in der S-Version ein Beobachter für den Wert voncached_apps_freezer erstellt, um die Aktivierung des Freezers zeitnah zu steuern.

2.1 updateUseFreezer()

    private void updateUseFreezer() {
        // 获取 settings中属性 cached_apps_freezer的值,同R 版本
        final String configOverride = Settings.Global.getString(mAm.mContext.getContentResolver(),
                Settings.Global.CACHED_APPS_FREEZER_ENABLED);

        if ("disabled".equals(configOverride)) {
            mUseFreezer = false;
        } else if ("enabled".equals(configOverride)
                || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
                    KEY_USE_FREEZER, DEFAULT_USE_FREEZER)) {
            mUseFreezer = isFreezerSupported();
            updateFreezerDebounceTimeout();
        } else {
            mUseFreezer = false;
        }

        final boolean useFreezer = mUseFreezer;
        // enableFreezer() would need the global ActivityManagerService lock, post it.
        mAm.mHandler.post(() -> {
            if (useFreezer) {
                Slog.d(TAG_AM, "Freezer enabled");
                enableFreezer(true);

                if (!mCachedAppOptimizerThread.isAlive()) {
                    mCachedAppOptimizerThread.start();
                }

                if (mFreezeHandler == null) {
                    mFreezeHandler = new FreezeHandler();
                }

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

            } else {
                Slog.d(TAG_AM, "Freezer disabled");
                enableFreezer(false);
            }
        });
    }

Auch für die R-Version wurde eine Optimierung vorgenommen und  die Verarbeitung von enableFreezer() erfolgt asynchron, da in der S-Version eine große Anpassung vorgenommen wurde. Weitere Informationen finden Sie in Abschnitt 4 unten .

Ob die Freezer-Funktion aktiviert ist oder nicht, lässt sich anhand eines Flussdiagramms besser erklären:

Es ist zu beachten, dass sich der Freezer-Knoten beim Abrufen von der R-Version unterscheidet :

  • Die R-Version besteht darin, den Knoten /sys/fs/cgroup/freezer/cgroup.freeze direkt anzugeben.
  • Die S-Version verwendet die Funktion getFreezerCheckPath(), um den Knoten zu finden, der der PID in libprocessgroup entspricht;

3. Einführung in cgroups

Ich werde hier nichts hinzufügen. Weitere Informationen finden Sie in der R-Version oder in der „Detaillierte Erläuterung der cgroup-Abstraktionsschicht in Android“.

Hierbei ist zu beachten, dass in der R - Version des Freezers /sys/fs/cgroup/freezer/cgroup.freeze verwendet wird , um zu bestimmen, ob der Freezer aktiviert werden soll, und /sys/fs/cgroup/freezer/cgroup.procs Wird verwendet, um den eingefrorenen/entfrorenen Vorgang durchzuführen.

In der S-Version passieren eingefrorene/nicht eingefrorene Vorgänge  den Knoten /sys/fs/cgroup/uid_xxx/pid_xxx/cgroup.freeze  .

cgroups.json ist in der S-Version anders:

  "Cgroups2": {
    "Path": "/sys/fs/cgroup",
    "Mode": "0755",
    "UID": "system",
    "GID": "system",
    "Controllers": [
      {
        "Controller": "freezer",
        "Path": ".",
        "Mode": "0755",
        "UID": "system",
        "GID": "system"
      }
    ]
  }

Das Freezer-Verzeichnis wird nicht mehr angezeigt, aber das Verzeichnis uid_xxx/pid_xxx wird direkt unter dem Verzeichnis /sys/fs/cgroup erstellt .

Weitere Informationen finden Sie im Abschnitt 5.2.1 weiter unten .

4. enableFreezer()

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

    public synchronized boolean enableFreezer(boolean enable) {
        if (!mUseFreezer) {
            return false;
        }

        if (enable) {
            mFreezerDisableCount--;

            if (mFreezerDisableCount > 0) {
                return true;
            } else if (mFreezerDisableCount < 0) {
                Slog.e(TAG_AM, "unbalanced call to enableFreezer, ignoring");
                mFreezerDisableCount = 0;
                return false;
            }
        } else {
            mFreezerDisableCount++;

            if (mFreezerDisableCount > 1) {
                return true;
            }
        }

        // Override is applied immediately, restore is delayed
        synchronized (mAm) {
            synchronized (mProcLock) {
                mFreezerOverride = !enable;
                Slog.d(TAG_AM, "freezer override set to " + mFreezerOverride);

                mAm.mProcessList.forEachLruProcessesLOSP(true, process -> {
                    if (process == null) {
                        return;
                    }

                    final ProcessCachedOptimizerRecord opt = process.mOptRecord;
                    if (enable && opt.hasFreezerOverride()) {
                        freezeAppAsyncLSP(process);
                        opt.setFreezerOverride(false);
                    }

                    if (!enable && opt.isFrozen()) {
                        unfreezeAppLSP(process);

                        // Set freezerOverride *after* calling unfreezeAppLSP (it resets the flag)
                        opt.setFreezerOverride(true);
                    }
                });
            }
        }

        return true;
    }

Der Code unterscheidet sich von der R-Version  . Der Prozess in jeder LRU wird hier durch  mAm.mProcessList.forEachLruProcessesLOSP() bestätigt.

Das Attribut mOptRecord.mFreezerOverride wird in den Prozess eingeführt , um zu markieren, ob der Freezer-Vorgang für einen Prozess deaktiviert werden soll. Wenn dieser Wert wahr ist, bedeutet dies, dass der Freezer-Prozess deaktiviert wurde. Wenn Sie es zu diesem Zeitpunkt erneut aktivieren, müssen Sie eine Einfrieranfrage an den Prozess stellen.

Natürlich verfügt auch die Klasse CachedAppOptimizer über eine solche Mitgliedsvariable mFreezerOverride , mit der der von außen aufgerufene freezeAppAsyncLSP() gesteuert wird. Wenn der Wert wahr ist, bedeutet dies, dass der Freezer deaktiviert ist. Wenn freezeAppAsyncLSP() extern aufgerufen wird, ist keine Verarbeitung erforderlich.

5. freezeAppAsyncLSP()

    void freezeAppAsyncLSP(ProcessRecord app) {
        final ProcessCachedOptimizerRecord opt = app.mOptRecord;
        if (opt.isPendingFreeze()) {
            // Skip redundant DO_FREEZE message
            return;
        }

        mFreezeHandler.sendMessageDelayed(
                mFreezeHandler.obtainMessage(
                    SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),
                mFreezerDebounceTimeout);
        opt.setPendingFreeze(true);
    }

Gegenüber der R-Version ist hier ein Schutz implementiert, um wiederholte Freeze-Anfragen zu verhindern.

Darüber hinaus wurde das Timeout von der Konstante FREEZE_TIMEOUT_MS in der R-Version in ein variables Debounce-Timeout geändert.

Hinweis: Der asynchrone Vorgang während des Einfrierens wird mithilfe des in der CachedAppOptimizer-Klasse definierten ServiceThread ausgeführt, während das Auftauen ein östlicher Vorgang ist und nicht über ServiceThread erfolgt.

5.1 Nachrichtenverarbeitung einfrieren

        public void handleMessage(Message msg) {
            switch (msg.what) {
                case SET_FROZEN_PROCESS_MSG:
                    synchronized (mAm) {
                        freezeProcess((ProcessRecord) msg.obj);
                    }
                    break;
                case REPORT_UNFREEZE_MSG:
                    int pid = msg.arg1;
                    int frozenDuration = msg.arg2;
                    String processName = (String) msg.obj;

                    reportUnfreeze(pid, frozenDuration, processName);
                    break;
                default:
                    return;
            }
        }

Es gibt zwei Nachrichten für den Freeze. Die Nachricht REPORT_UNFREEZE_MSG wird nach dem Unfreeze aufgezeichnet.

 Dieser Artikel konzentriert sich auf die Nachrichtenverarbeitung von Freeze. Hier sehen wir, dass die  Funktion freezeProcess() schließlich aufgerufen wird . Weitere Informationen finden Sie im nächsten Abschnitt.

5.2 freezeProcess()

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

       private void freezeProcess(final ProcessRecord proc) {
            int pid = proc.getPid(); // Unlocked intentionally
            final String name = proc.processName;
            final long unfrozenDuration;
            final boolean frozen;
            final ProcessCachedOptimizerRecord opt = proc.mOptRecord;

            opt.setPendingFreeze(false);

            try {
                // 确认进程是否存在任意的文件锁,避免不必要的 free/unfreeze操作
                //   这是为了防止冻结进程持有文件锁,从而引起死锁
                //   冻结成功之后,还会再次确认文件锁,如果有锁,则立即解冻
                if (mProcLocksReader.hasFileLocks(pid)) {
                    if (DEBUG_FREEZER) {
                        Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, not freezing");
                    }
                    return;
                }
            } catch (Exception e) {
                Slog.e(TAG_AM, "Not freezing. Unable to check file locks for " + name + "(" + pid
                        + "): " + e);
                return;
            }

            synchronized (mProcLock) {
                pid = proc.getPid();

                // 如果进程没有变成Cached或者不能freeze,则退出此次freeze操作
                if (proc.mState.getCurAdj() < ProcessList.CACHED_APP_MIN_ADJ
                        || opt.shouldNotFreeze()) {
                    return;
                }

                // 如果freezer 处于disable状态,返回,并告知该进程
                if (mFreezerOverride) {
                    opt.setFreezerOverride(true);
                    return;
                }


                // 已经处于frozen,或者不是一个应用进程,则退出此次 freeze操作
                //   pid为0,有可能进程还没有launch完成,或者进程被kill了
                if (pid == 0 || opt.isFrozen()) {
                    // Already frozen or not a real process, either one being
                    // launched or one being killed
                    return;
                }

                Slog.d(TAG_AM, "freezing " + pid + " " + name);

                // 在S版本中,将这部分功能提前,也是正确的行为
                // 冻结 binder
                //    1.如果freezer是使能,将同步发送所有的pending 交互给指定的pid;
                //    2.该函数调用后,所有的binder 请求,都会被block,并返回error给发送请求的进程;
                try {
                    if (freezeBinder(pid, true) != 0) {
                        rescheduleFreeze(proc, "outstanding txns");
                        return;
                    }
                } catch (RuntimeException e) { //如果调用失败,则直接kill该进程
                    Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
                    mFreezeHandler.post(() -> {
                        synchronized (mAm) {
                            proc.killLocked("Unable to freeze binder interface",
                                    ApplicationExitInfo.REASON_OTHER,
                                    ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true);
                        }
                    });
                }

                long unfreezeTime = opt.getFreezeUnfreezeTime();

                //核心函数 setProcessFrozen(),同步冻结进程
                try {
                    Process.setProcessFrozen(pid, proc.uid, true);

                    opt.setFreezeUnfreezeTime(SystemClock.uptimeMillis());
                    opt.setFrozen(true);
                } catch (Exception e) {
                    Slog.w(TAG_AM, "Unable to freeze " + pid + " " + name);
                }

                unfrozenDuration = opt.getFreezeUnfreezeTime() - unfreezeTime;
                frozen = opt.isFrozen();
            }

            if (!frozen) {
                return;
            }

            Slog.d(TAG_AM, "froze " + pid + " " + name);

            // 将此次 freeze 记录到 event log 中
            EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name);

            // See above for why we're not taking mPhenotypeFlagLock here
            if (mRandom.nextFloat() < mFreezerStatsdSampleRate) {
                FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED,
                        FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__FREEZE_APP,
                        pid,
                        name,
                        unfrozenDuration);
            }

            // 确认在冻结的时候,是否收到了 TXNS_PENDING_WHILE_FROZEN的binder请求,
            //    如果有有收到请求,则重新freeze 该进程(unfreeze + freeze)
            try {
                // post-check to prevent races
                int freezeInfo = getBinderFreezeInfo(pid);

                if ((freezeInfo & TXNS_PENDING_WHILE_FROZEN) != 0) {
                    synchronized (mProcLock) {
                        rescheduleFreeze(proc, "new pending txns");
                    }
                    return;
                }
            } catch (RuntimeException e) {
                Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
                mFreezeHandler.post(() -> {
                    synchronized (mAm) {
                        proc.killLocked("Unable to freeze binder interface",
                                ApplicationExitInfo.REASON_OTHER,
                                ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true);
                    }
                });
            }

            try {
                // 再次check文件锁,如果该冻结进程持有文件锁,立即unfreeze
                if (mProcLocksReader.hasFileLocks(pid)) {
                    if (DEBUG_FREEZER) {
                        Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, reverting freeze");
                    }
                    unfreezeAppLSP(proc);
                }
            } catch (Exception e) {
                Slog.e(TAG_AM, "Unable to check file locks for " + name + "(" + pid + "): " + e);
                unfreezeAppLSP(proc);
            }
        }

Die Logik unterscheidet sich deutlich von der R-Version :

  • Rufen Sie vor dem Einfrieren des Prozesses freezeBinder() auf, um die Binder-Kommunikation einzufrieren.
  • Rufen Sie nach dem Einfrieren des Prozesses getBinderFreezeInfo() auf, um zu bestätigen, ob beim Einfrieren  eine Binderanforderung für TXNS_PENDING_WHILE_FROZEN vorliegt . Wenn eine solche Anforderung vorliegt, frieren Sie den Prozess erneut ein (unfreeze + freeze);

Erstellen Sie ein Flussdiagramm, um den Gefriervorgang zu verstehen:

 

 5.2.1 setProcessFrozen()

frameworks/base/core/java/android/os/Process.java
 
    public static final native void setProcessFrozen(int pid, int uid, boolean frozen);
frameworks/base/core/jni/android_util_Process.cpp

void android_os_Process_setProcessFrozen(
        JNIEnv *env, jobject clazz, jint pid, jint uid, jboolean freeze)
{
    bool success = true;

    if (freeze) {
        success = SetProcessProfiles(uid, pid, {"Frozen"});
    } else {
        success = SetProcessProfiles(uid, pid, {"Unfrozen"});
    }

    if (!success) {
        signalExceptionForGroupError(env, EINVAL, pid);
    }
}

Der Aufruf hier wurde im  Blogbeitrag „cgroup Abstraction Layer“ analysiert. Über die Schnittstelle SetProcessProfiles() handelt es sich speziell um ein Profil vom Typ SetAttributeAction  und ruft schließlich ExecuteForProcess() auf:

system/core/libprocesscgroup/task_profiles.cpp

bool SetAttributeAction::ExecuteForProcess(uid_t, pid_t pid) const {
    return ExecuteForTask(pid);
}

bool SetAttributeAction::ExecuteForTask(int tid) const {
    std::string path;

    if (!attribute_->GetPathForTask(tid, &path)) {
        LOG(ERROR) << "Failed to find cgroup for tid " << tid;
        return false;
    }

    if (!WriteStringToFile(value_, path)) {
        PLOG(ERROR) << "Failed to write '" << value_ << "' to " << path;
        return false;
    }

    return true;
}

Attribute_->GetPathForTask() muss durch Code bestimmt werden:

system/core/libprocessgroup/task_profiles.cpp

bool ProfileAttribute::GetPathForTask(int tid, std::string* path) const {
    std::string subgroup;
    if (!controller()->GetTaskGroup(tid, &subgroup)) {
        return false;
    }

    if (path == nullptr) {
        return true;
    }

    if (subgroup.empty()) {
        *path = StringPrintf("%s/%s", controller()->path(), file_name_.c_str());
    } else {
        *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(),
                             file_name_.c_str());
    }
    return true;
}

Es gibt zwei Teile: Der eine besteht darin, die Untergruppe über den Controller zu erhalten, und der andere darin, den endgültigen Pfad zu verbinden.

Werfen wir einen Blick auf GetTaskGroup(), auf das congtroller zeigt :

system/core/libprocessgroup/cgroup_map.cpp

bool CgroupController::GetTaskGroup(int tid, std::string* group) const {
    std::string file_name = StringPrintf("/proc/%d/cgroup", tid);
    std::string content;
    if (!android::base::ReadFileToString(file_name, &content)) {
        PLOG(ERROR) << "Failed to read " << file_name;
        return false;
    }

    // if group is null and tid exists return early because
    // user is not interested in cgroup membership
    if (group == nullptr) {
        return true;
    }

    std::string cg_tag;

    if (version() == 2) {
        cg_tag = "0::";
    } else {
        cg_tag = StringPrintf(":%s:", name());
    }
    size_t start_pos = content.find(cg_tag);
    if (start_pos == std::string::npos) {
        return false;
    }

    start_pos += cg_tag.length() + 1;  // skip '/'
    size_t end_pos = content.find('\n', start_pos);
    if (end_pos == std::string::npos) {
        *group = content.substr(start_pos, std::string::npos);
    } else {
        *group = content.substr(start_pos, end_pos - start_pos);
    }

    return true;
}

Lesen Sie die Informationen in /proc/PID/cgroup . Für cgroup2 ist die gelesene Information 0::. In der S-Version wird die Verkettung von uid und pid angegeben, zum Beispiel:

130|shift:/sys/fs/cgroup # cat /proc/2119/cgroup
4:memory:/
3:cpuset:/restricted
2:cpu:/foreground
1:blkio:/foreground
0::/uid_10083/pid_2119

Die endgültige geschriebene Datei ist /sys/fs/cgroup/uid_xxx/pid_xxx /cgroup.freeze .

Verlaufsdiagramm eines eingefrorenen Prozesses:

6. unfreezeAppLSP()

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

   void unfreezeAppLSP(ProcessRecord app) {
        final int pid = app.getPid();
        final ProcessCachedOptimizerRecord opt = app.mOptRecord;

        // 进程已经处于peding freeze中,移除冻结消息
        if (opt.isPendingFreeze()) {
            // Remove pending DO_FREEZE message
            mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
            opt.setPendingFreeze(false);
        }

        opt.setFreezerOverride(false);

        // 如果进程还没有冻结,则无需做解冻处理
        if (!opt.isFrozen()) {
            return;
        }

        // 冻住的进程可以接收异步binder请求,但是不会处理,只是放入binder buffer, 过多的请求会导致buffer耗尽;
        // 这里需要确认下该进程在解冻之前,进程是否在冰冻期间收到同步的binder 请求,有则kill该进程
        boolean processKilled = false;

        try {
            int freezeInfo = getBinderFreezeInfo(pid);

            if ((freezeInfo & SYNC_RECEIVED_WHILE_FROZEN) != 0) {
                Slog.d(TAG_AM, "pid " + pid + " " + app.processName
                        + " received sync transactions while frozen, killing");
                app.killLocked("Sync transaction while in frozen state",
                        ApplicationExitInfo.REASON_OTHER,
                        ApplicationExitInfo.SUBREASON_FREEZER_BINDER_TRANSACTION, true);
                processKilled = true;
            }

            if ((freezeInfo & ASYNC_RECEIVED_WHILE_FROZEN) != 0 && DEBUG_FREEZER) {
                Slog.d(TAG_AM, "pid " + pid + " " + app.processName
                        + " received async transactions while frozen");
            }
        } catch (Exception e) {
            Slog.d(TAG_AM, "Unable to query binder frozen info for pid " + pid + " "
                    + app.processName + ". Killing it. Exception: " + e);
            app.killLocked("Unable to query binder frozen stats",
                    ApplicationExitInfo.REASON_OTHER,
                    ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true);
            processKilled = true;
        }

        //进程被kill 了,无需 unfreeze
        if (processKilled) {
            return;
        }

        // app.freezeUnfreezeTime记录的是上次free、unfreeze的时间
        long freezeTime = opt.getFreezeUnfreezeTime();

        try {
            freezeBinder(pid, false);
        } catch (RuntimeException e) {
            Slog.e(TAG_AM, "Unable to unfreeze binder for " + pid + " " + app.processName
                    + ". Killing it");
            app.killLocked("Unable to unfreeze",
                    ApplicationExitInfo.REASON_OTHER,
                    ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true);
            return;
        }

        try {
            Process.setProcessFrozen(pid, app.uid, false);

            opt.setFreezeUnfreezeTime(SystemClock.uptimeMillis());
            opt.setFrozen(false);
        } catch (Exception e) {
            Slog.e(TAG_AM, "Unable to unfreeze " + pid + " " + app.processName
                    + ". This might cause inconsistency or UI hangs.");
        }

        if (!opt.isFrozen()) {
            Slog.d(TAG_AM, "sync unfroze " + pid + " " + app.processName);

            mFreezeHandler.sendMessage(
                    mFreezeHandler.obtainMessage(REPORT_UNFREEZE_MSG,
                        pid,
                        (int) Math.min(opt.getFreezeUnfreezeTime() - freezeTime, Integer.MAX_VALUE),
                        app.processName));
        }
    }

Die Logik ist relativ klar. Die Kernverarbeitungsfunktion ist setProcessFrozen(). Der detaillierte Prozess wurde im obigen Abschnitt 5.2.1 analysiert.

7. Kernelverarbeitung

kernel/cgroup/cgroup.c

static struct cftype cgroup_base_files[] = {
    ...

	{
		.name = "cgroup.freeze",
		.flags = CFTYPE_NOT_ON_ROOT,
		.seq_show = cgroup_freeze_show,
		.write = cgroup_freeze_write,
	},

    ...
};

Das Schreiben in die Datei /sys/fs/cgroup/uid_xxx/pid_xxx/cgroup.freeze löst die Rückruffunktion cgroup_freeze_write() aus , die die entsprechende Kontrollgruppe über die aktuellen Kernfs findet und alle Prozesse und untergeordneten Prozesse unter dieser Kontrollgruppe einfriert.

Die detaillierte Funktion cgroup_freeze_write() wird später analysiert.

 
Verwandte Blogbeiträge:

https://justinwei.blog.csdn.net/article/details/131845360?

Detaillierte Erläuterung der cgroup-Abstraktionsschicht im Android_private kitchen blog-CSDN blog

Optimierung des App-Speicherrecyclings in Android (2): S-Version_privater Küchenblog-CSDN-Blog

Optimierung des App-Speicherrecyclings in Android (1): R version_cachedappoptimizer_private kitchen blog-CSDN blog

Android oom_adj detaillierte Interpretation_privater Küchenblog-CSDN-Blog

Android oom_adj Update-Prinzip (2)_privater Küchenblog-CSDN-Blog

Android oom_adj Update-Prinzip (1)_privater Küchenblog-CSDN-Blog

 

 

Ich denke du magst

Origin blog.csdn.net/jingerppp/article/details/131974011
Empfohlen
Rangfolge