作者:Chickny的造梦空间 | 来源:互联网 | 2023-05-17 14:20
SystemServer的WatchDog-引言SystemServer对整个系统来说很重要,因此内部使用WatchDog监听各种服务的状态,一旦出问题就会重启。WatchDo
引言
SystemServer 对整个系统来说很重要,因此内部使用 WatchDog 监听各种服务的状态,一旦出问题就会重启。
WatchDog 继承 Thread,因此看门狗在单独的线程中执行监听
。看门狗内部会监听两种类型:
一种是:Watchdog.Monitor
类,监听其 monitor() 方法耗时。使用该方法可以监听死锁
。如 ams 的实现:
public void monitor() {
// 就简简单单是地一个 sync,能拿到说明 this 上不存在死锁,否则就存在
synchronized (this) { }
}
另一种是:Handler 消息列表
,检查它的消息队列是否阻塞。
构造函数
逻辑很简单,创建一堆的 HandlerChecker 对象,然后添加到看门狗的 mHandlerCheckers 中,它是一个 ArrayList 对象。其中有一个最重要的 HandlerChecker mMonitorChecker,它用来处理所有的 Monitor
// 构造函数节选
mMOnitorChecker= new HandlerChecker(FgThread.getHandler(),
"foreground thread", DEFAULT_TIMEOUT);
// 将参数添加到 mMonitorChecker 中,最终会添加到 HandlerChecker#mMonitorQueue
addMonitor(new BinderThreadMonitor());
run
看门狗本身继承 Thread,因此它启动后就会执行它的 run() 方法
public void run() {
boolean waitedHalf = false;
// 死循环,不断监听
while (true) {
final List blockedCheckers;
final String subject;
final boolean allowRestart;
int debuggerWasCOnnected= 0;
synchronized (this) {
long timeout = CHECK_INTERVAL; // 30s
for (int i=0; i 0) {
try {
wait(timeout);
} catch (InterruptedException e) {
// 被唤醒后接着 wait,最终会强制 wait 30s
}
timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);
}
boolean fdLimitTriggered = false;
if (mOpenFdMonitor != null) {
fdLimitTriggered = mOpenFdMonitor.monitor();
}
// 【代码一】
if (!fdLimitTriggered) {
// 评估所有 checkers 的最终结果
// 依次调用每一个 HandlerChecker#getCompletionStateLocked
// 然后取最大值返回
final int waitState = evaluateCheckerCompletionLocked();
if (waitState == COMPLETED) {
// 所有 checker 都已完成,直接 continue 进行下一次循环
// 将 waitedHalf 设置为 false,重新记录有没有 checker 超过最大时间的一半
waitedHalf = false;
continue;
} else if (waitState == WAITING) {
// 所有 checker 都没有到最大等待时间的一半,也进行下一次循环
continue;
} else if (waitState == WAITED_HALF) {
// 到这里也就是说至少有一个 checker 已经超过最大等待时间的一半
if (!waitedHalf) {
// 如果是第一次,那就先 dump 一下系统信息
// 如果 if 判断不成立,说明不是第一次有 checker 超过一半时间。
// 这有可能的情况是:第一次 A 超过一半,然后 dump 信息再次循环
// 循环的时间等待 30s,然后 B 超过一半,但 A 已经结束
ArrayList pids = new ArrayList<>(mInterestingJavaPids);
ActivityManagerService.dumpStackTraces(pids, null, null,
getInterestingNativePids(), null);
waitedHalf = true;
}
continue;
}
// 剩余一下状态就是 OVERDEU,也就是有 checker 已经超过最大等待时间
// something is overdue!
blockedCheckers = getBlockedCheckersLocked();
subject = describeCheckersLocked(blockedCheckers);
} else {
blockedCheckers = Collections.emptyList();
subject = "Open FD high water mark reached";
}
allowRestart = mAllowRestart;
}
// 根据注释,如果执行到这里说明系统已经被阻塞了。在【代码一】中也已说明了各种情况
EventLog.writeEvent(EventLogTags.WATCHDOG, subject);
ArrayList pids = new ArrayList<>(mInterestingJavaPids);
long anrTime = SystemClock.uptimeMillis();
StringBuilder report = new StringBuilder();
report.append(MemoryPressureUtil.currentPsiState());
ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(false);
StringWriter tracesFileException = new StringWriter();
// 再 dump 一次
final File stack = ActivityManagerService.dumpStackTraces(
pids, processCpuTracker, new SparseArray<>(), getInterestingNativePids(),
tracesFileException);
// 等待 5s,保证 dump 完。已经阻塞 1min 中了,不在乎这多余的 5s
SystemClock.sleep(5000);
// 省略关于 dropbox 的处理
// 正常情况下,到这里应该 kill 掉 system server,但有可能是 debug 时,所以加个判断
if (Debug.isDebuggerConnected()) {
debuggerWasCOnnected= 2;
}
if (debuggerWasConnected >= 2) {
Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process");
} else if (debuggerWasConnected > 0) {
Slog.w(TAG, "Debugger was connected: Watchdog is *not* killing the system process");
} else if (!allowRestart) {
Slog.w(TAG, "Restart not allowed: Watchdog is *not* killing the system process");
} else {
// 杀掉进程,然后 systerm_server 退出
Process.killProcess(Process.myPid());
System.exit(10);
}
waitedHalf = false;
}
}
这里面最难理解的就是 waitedHalf,它的理解必须依赖于 HandlerChecker#getCompletionStateLocked()
private static final int COMPLETED = 0;
private static final int WAITING = 1;
private static final int WAITED_HALF = 2;
private static final int OVERDUE = 3;
public int getCompletionStateLocked() {
if (mCompleted) {
// 已完成
return COMPLETED;
} else {
long latency = SystemClock.uptimeMillis() - mStartTime;
if (latency
结合上面的代码以及【代码一】中的注释就很好理解了:看门狗主要想在有 checker 超过最大等待时间一半时 dump 一下系统信息
。如果最后出现超时,再 dump 一份,两份信息更容易判断出问题出现点。
检测思路
在上面代码中,有一个很重要的方法没有说明:Checker#scheduleCheckLocked(),它包含看门狗检测各种情况的主要逻辑
public void scheduleCheckLocked() {
if (mCompleted) {
// 将 monitor 移植到 mMonitors 中
mMonitors.addAll(mMonitorQueue);
mMonitorQueue.clear();
}
// mHandler 是构造函数中赋值,指的是要检测的 Handler
if ((mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling())
|| (mPauseCount > 0)) {
// 没必要进行检测。要么正处于暂停,要么没有 monitor 而且 handler 正在执行
mCompleted = true;
return;
}
if (!mCompleted) {
// 已经在检测了,所以不重复提交
return;
}
mCompleted = false;
mCurrentMOnitor= null;
mStartTime = SystemClock.uptimeMillis();
// 跟普通的卡顿思路一样,都是 handler#post 一个消息,只不过 post 到消息队列的头部。见下面的 run 方法
mHandler.postAtFrontOfQueue(this);
}
public void run() {
// 执行 monitor 的 monitor() 方法
final int size = mMonitors.size();
for (int i = 0 ; i
上面代码可以看出死锁的检测思路:un 中执行 monitor()。如果 monitor() 中等待某一个锁必须会导致 run() 方法被阻塞中调用 monitor() 处
。
如果阻塞的时候过长,看门狗又是一个独立的线程,在调用 checker#getCompletionStateLocked() 就会返回 overdue,就会被看门狗嗅探到。
这里只是以死锁检测举例,monitor 中可以执行各种检测,不一定非得是死锁。比如看门狗中还有一个检测 binder 线程的 BinderThreadMonitor,具体的思路也不分析了。
总结
- 死锁检测:
自己通过 sync 尝试获取某个对象锁
,如果长时间获取不到,就说明持有该锁的线程执行时间过长,有可能就是死锁