Skip to content

Latest commit

 

History

History
131 lines (107 loc) · 4.79 KB

保活.md

File metadata and controls

131 lines (107 loc) · 4.79 KB

1 保活

保活目的其实就是让 App 永生不死,然而,说起来简单,实践起来困难,随着版本的升级,保活基本上起不到预期的效果,一般保活的手段从两个方面入手:

  • 保证应用进程不被杀死
  • 即使应用进程被杀死,也能够重新拉起来

2 保活原理

系统杀进程有两种方法:

  • killBackgroundProcesses(原生系统,常通过这一种方式)
  • forceStopPackage(比较强力)

分析第二种 forceStop 是如何工作的:

//ActivityManagerService
public void forceStopPackage(final String packageName, int userId) {
    ...
    try {
        IPackageManager pm = AppGlobals.getPackageManager();
        synchronized(this) {
            int[] users = userId == UserHandle.USER_ALL
                    ? mUserController.getUsers() : new int[] { userId };
            for (int user : users) {

                ...
                if (mUserController.isUserRunning(user, 0)) {
                	// 根据 UID 和包名杀死进程 代码1
                    forceStopPackageLocked(packageName, pkgUid, "from pid " + callingPid);
                    finishForceStopPackageLocked(packageName, pkgUid);
                }
            }
        }
    } finally {
        Binder.restoreCallingIdentity(callingId);
    }
}

//代码1
final boolean forceStopPackageLocked(...) {
    //killPackageProcessesLocked 杀死进程  代码2
    boolean didSomething = mProcessList.killPackageProcessesLocked(packageName, appId, userId,
            ProcessList.INVALID_ADJ, callerWillRestart, true /* allowRestart */, doit,
            evenPersistent, true /* setRemoved */,
            packageName == null ? ("stop user " + userId) : ("stop " + packageName));

    didSomething |=
            mAtmInternal.onForceStopPackage(packageName, doit, evenPersistent, userId);

    // 清理 service
    // 清理 broadcastreceiver
    // 清理 providers
    // 清理其他

    return didSomething;
}

//代码2 最终走到 ProcessRecord#kill
void kill(String reason, boolean noisy) {
    if (!killedByAm) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "kill");
        if (mService != null && (noisy || info.uid == mService.mCurOomAdjUid)) {
            mService.reportUidInfoMessageLocked(TAG,
                    "Killing " + toShortString() + " (adj " + setAdj + "): " + reason,
                    info.uid);
        }
        if (pid > 0) {
            EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
            //杀掉了目标进程
            Process.killProcessQuiet(pid);
            //uid 为单位 杀掉目标进程组 代码3
            ProcessList.killProcessGroup(uid, pid);
        } else {
            pendingStart = false;
        }
        if (!mPersistent) {
            killed = true;
            killedByAm = true;
        }
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    }
}

//代码3
int killProcessGroup(uid_t uid, int initialPid, int signal) {
    return KillProcessGroup(uid, initialPid, signal, 40 /*retries*/);
}

static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
    ...
    int retry = retries;
    int processes;
    while ((processes = DoKillProcessGroupOnce(cgroup, uid, initialPid, signal)) > 0) {
        LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid;
        //循环 40 遍不断杀进程,每次杀完之后等 5ms 
        if (retry > 0) {
            std::this_thread::sleep_for(5ms);
            --retry;
        } else {
            break;
        }
    }
    ...
}

从上面代码可以看出,假如 循环 40 遍不断杀进程,40 遍进程还在,那么就可以逃过,原理就是 App 进程在被杀掉之后,在 5ms 内启动一堆新的进程,循环 40 次,只要每次都能够拉起新的进程,那 App 就能逃过系统的追杀。这里只要 逃过 200ms,就可以保活。

核心原理:任意一个进程死亡之后,都能让把其他所有进程全部拉起

  • 2 个进程通过互相监听文件锁的方式,来感知彼此的死亡
  • 通过 fork 产生子进程,fork 的进程同属一个进程组,一个被杀之后会触发另外一个进程被杀,从而被文件锁感知

具体做法:

  • 创建 2 个进程 p1, p2,通过文件锁互相关联
  • p1 经过 2 次 fork -> 孤儿进程 c1,p2 经过 2 次 fork -> 孤儿进程 c2
  • c1,c2 通过文件锁互相关联
  • 假设 p1 被杀,那么 p2 会立马感知到
  • p1 和 c1 同属一个进程组,p1 被杀会触发 c1 被杀
  • c1 死后 c2 立马感受到从而拉起 p1

2 参考阅读