转自:https://juejin.cn/post/6844904085695496199

概览

(本文系统源码基于Andoroid 10.0.0-r16)Watchdog的中文叫“看门狗”,最早引入Watchdog是在单片机系统中,由于单片机的工作环境容易受到外界磁场的干扰,导致程序“跑飞”,造成整个系统无法正常工作,因此,引入了一个“看门狗”,对单片机的运行状态进行实时监测,针对运行故障做一些保护处理,譬如让系统重启。这种Watchdog属于硬件层面,必须有硬件电路的支持。

Linux也引入了Watchdog,在Linux内核下,当Watchdog启动后,便设定了一个定时器,如果在超时时间内没有对/dev/Watchdog进行写操作,则会导致系统重启,通过定时器实现的Watchdog属于软件层面。

Android设计了一个软件层面Watchdog,用于保护一些重要的系统服务,当出现故障时,通常会让Android系统重启。由于这种机制的存在,就经常会出现一些system_server进程被Watchdog杀掉而发生系统重启的问题。 原理就是每到达一定的时间,获取正在监控的锁(Monitor.monitor()),能否获得锁从而判断是否产生了死锁。

Watchdog流程

Watchdog初始化

首先我们可以看到WatchDog是继承于Thread类,而实际上WatchDog也是单线程运行的。

public class Watchdog extends Thread {}

复制代码

接下来我们可以看下Watchdog的构造函数: frameworks/base/services/core/java/com/android/server/Watchdog.java

private Watchdog() {

// 设置线程名字为watchdog

super("watchdog");

// Initialize handler checkers for each common thread we want to check.

// 请注意,我们目前没有检查后台线程,因为它可能会持有较长时间运行的操作,

// 而不能保证那里的操作的及时性。

// FgThread monitor

mMonitorChecker = new HandlerChecker(FgThread.getHandler(),

"foreground thread", DEFAULT_TIMEOUT);

mHandlerCheckers.add(mMonitorChecker);

// main thread monitor

mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),

"main thread", DEFAULT_TIMEOUT));

// UiThread monitor

mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),

"ui thread", DEFAULT_TIMEOUT));

// IoThread monitor

mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),

"i/o thread", DEFAULT_TIMEOUT));

// DisplayThread monitor

mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),

"display thread", DEFAULT_TIMEOUT));

// AnimationThread monitor

mHandlerCheckers.add(new HandlerChecker(AnimationThread.getHandler(),

"animation thread", DEFAULT_TIMEOUT));

// SurfaceAnimationThread monitor

mHandlerCheckers.add(new HandlerChecker(SurfaceAnimationThread.getHandler(),

"surface animation thread", DEFAULT_TIMEOUT));

// BinderThreadMonitor 主要用于检测binder线程是否达到连接上限16个

// (正常的是16,这里是SystemServer的进程,所以是31) >= 31个则阻塞线程等待mThreadCountDecrement唤醒

addMonitor(new BinderThreadMonitor());

// 用于对fd数量进行检测 只会在userdebug或者eng版本开启

// 值是厂商设置的 达到这个值 - 12 就会在/data/anr/ dump fd的信息

mOpenFdMonitor = OpenFdMonitor.create();

assert DB ||

DEFAULT_TIMEOUT > ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;

}

复制代码

可以看出我们在创建出Watchdog的时候先把一些重要的线程加到Watchdog的mHandlerCheckers里面,当Watchdog执行run()方法的时候就会依次对mHandlerCheckers里面的HandlerChecker对象进行轮询,查看是否有超时的线程。有的话一般都是重启来解决。

那我们在哪里调用这个设置为private的构造函数呢?就是在本类的getInstance()中

public static Watchdog getInstance() {

if (sWatchdog == null) {

sWatchdog = new Watchdog();

}

return sWatchdog;

}

复制代码

我们现在再看深一层,那究竟谁调用了getInstance()方法呢?我们动用ctrl + 鼠标点一下就弹出一系列的调用者。查看一番之后,可以观察到是SystemServer调用了我们Watchdog的getInstance()方法进行初始化。 frameworks/base/services/java/com/android/server/SystemServer.java

private void startBootstrapServices() {

final Watchdog watchdog = Watchdog.getInstance();

watchdog.start();

// ...

}

复制代码

在调用了初始化的getInstance()之后就马上调用了watchdog的start()方法,其实也就是父类Thread的start()方法,这时候我们的“看门狗”就跑起来了(执行了run()方法),而且可以看到watchdog是在startBootstrapServices()中的第一句就进行初始化了,SystemServer中初始化服务的顺序是这样的:

startBootstrapServices();

startCoreServices();

startOtherServices();

这说明了watchdog在众多的服务中,优先级还是很高的,那有没有同学会想到是为什么呢?答案就是很多服务的超时结束机制都是依赖watchdog提供的,所以watchdog必须要比其他服务早初始化。

我们再来看一下WatchDog的init()方法,因为依赖于ActivityManagerService,所以我们将在ActivityManagerService初始化之后再执行这个方法。

public void init(Context context, ActivityManagerService activity) {

mActivity = activity;

context.registerReceiver(new RebootRequestReceiver(),

new IntentFilter(Intent.ACTION_REBOOT),

android.Manifest.permission.REBOOT, null);

}

复制代码

可以看到这里通过context注册了一个广播接收器,而这个RebootRequestReceiver用来接收重启的广播来进行手机重启的,是Watchdog中定义的一个内部类,RebootRequestReceiver的代码如下:

final class RebootRequestReceiver extends BroadcastReceiver {

@Override

public void onReceive(Context c, Intent intent) {

if (intent.getIntExtra("nowait", 0) != 0) {

rebootSystem("Received ACTION_REBOOT broadcast");

return;

}

Slog.w(TAG, "Unsupported ACTION_REBOOT broadcast: " + intent);

}

}

void rebootSystem(String reason) {

Slog.i(TAG, "Rebooting system because: " + reason);

IPowerManager pms = (IPowerManager)ServiceManager.getService(Context.POWER_SERVICE);

try {

// 这里是手机重启而不是系统重启

pms.reboot(false, reason, false);

} catch (RemoteException ex) {

}

}

复制代码

HandlerChecker

我们再来看看在Watchdog中相对比较重要的HandlerChecker内部类是长什么样子的。

/**

* Used for checking status of handle threads and scheduling monitor callbacks.

*/

public final class HandlerChecker implements Runnable {

// 需要检测的线程对应的handler

private final Handler mHandler;

// 进行日志打印的时候知道是哪个handler出现问题

private final String mName;

// 超时时间

private final long mWaitMax;

// 检测的Monitor数组

private final ArrayList mMonitors = new ArrayList();

// 还未加入mMonitors中的Monitor

// 等待mCompleted为true的时候才一次性加入mMonitors中

private final ArrayList mMonitorQueue = new ArrayList();

// 是否检查完成的标志位 空闲的话默认为true

private boolean mCompleted;

// 目前正在检查的Monitor

private Monitor mCurrentMonitor;

// 检查开始的时间

private long mStartTime;

// mPauseCount > 0则说明这个HandlerChecker处于暂停状态

private int mPauseCount;

HandlerChecker(Handler handler, String name, long waitMaxMillis) {

mHandler = handler;

mName = name;

mWaitMax = waitMaxMillis;

mCompleted = true;

}

// 注释1

void addMonitorLocked(Monitor monitor) {

// We don't want to update mMonitors when the Handler is in the middle of checking

// all monitors. We will update mMonitors on the next schedule if it is safe

mMonitorQueue.add(monitor);

}

// 注释2

public void scheduleCheckLocked() {

if (mCompleted) {

// Safe to update monitors in queue, Handler is not in the middle of work

mMonitors.addAll(mMonitorQueue);

mMonitorQueue.clear();

}

// 待检测的mMonitors为零且looper正在从队列中轮询任务

// 或者正在暂停的情况下 不要进行一些数据的重置

if ((mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling())

|| (mPauseCount > 0)) {

mCompleted = true;

return;

}

if (!mCompleted) {

// we already have a check in flight, so no need

return;

}

mCompleted = false;

mCurrentMonitor = null;

mStartTime = SystemClock.uptimeMillis();

// 往handler前面插入任务(插队)

mHandler.postAtFrontOfQueue(this);

}

// 是否超时

boolean isOverdueLocked() {

return (!mCompleted) && (SystemClock.uptimeMillis() > mStartTime + mWaitMax);

}

// 通过mStartTime、mWaitMax和mCompleted 进行状态的判断

// 前 mWaitMax/2 秒为WAITING状态,后mWaitMax/2为WAITED_HALF状态

// 大于mWaitMax为超时状态也就是OVERDUE状态

public int getCompletionStateLocked() {

if (mCompleted) {

return COMPLETED;

} else {

long latency = SystemClock.uptimeMillis() - mStartTime;

if (latency < mWaitMax/2) {

return WAITING;

} else if (latency < mWaitMax) {

return WAITED_HALF;

}

}

return OVERDUE;

}

public Thread getThread() {

return mHandler.getLooper().getThread();

}

public String getName() {

return mName;

}

// 获取Blocked状态的描述

// 在哪个名字的HandlerChecker中 或者 当前handler执行哪个monitor

String describeBlockedStateLocked() {

if (mCurrentMonitor == null) {

return "Blocked in handler on " + mName + " (" + getThread().getName() + ")";

} else {

return "Blocked in monitor " + mCurrentMonitor.getClass().getName()

+ " on " + mName + " (" + getThread().getName() + ")";

}

}

@Override

public void run() {

// 一旦我们到达这里,我们确保即使我们调用#addMonitorLocked也不会改变mMonitors,

// 因为我们首先将新的监视器添加到mMonitorQueue中,

// 并在下一次mCompleted为真时将它们移到mMonitors中,此时我们已经完成了这个方法的执行。

// 所以这里用了两个数组分别存储Monitor

final int size = mMonitors.size();

for (int i = 0 ; i < size ; i++) {

synchronized (Watchdog.this) {

mCurrentMonitor = mMonitors.get(i);

}

mCurrentMonitor.monitor();

}

synchronized (Watchdog.this) {

mCompleted = true;

mCurrentMonitor = null;

}

}

// 暂停这个HandlerChecker

public void pauseLocked(String reason) {

mPauseCount++;

// Mark as completed, because there's a chance we called this after the watchog

// thread loop called Object#wait after 'WAITED_HALF'. In that case we want to ensure

// the next call to #getCompletionStateLocked for this checker returns 'COMPLETED'

mCompleted = true;

Slog.i(TAG, "Pausing HandlerChecker: " + mName + " for reason: "

+ reason + ". Pause count: " + mPauseCount);

}

// 继续这个HandlerChecker

public void resumeLocked(String reason) {

if (mPauseCount > 0) {

mPauseCount--;

Slog.i(TAG, "Resuming HandlerChecker: " + mName + " for reason: "

+ reason + ". Pause count: " + mPauseCount);

} else {

Slog.wtf(TAG, "Already resumed HandlerChecker: " + mName);

}

}

}

复制代码

从注释1开始,可以看到大部分方法后缀都加上了Locked,这是因为在Watchdog调用的时候都会加上synchronize锁,保证不会发生多线程并发导致的问题。而注释2的scheduleCheckLocked()方法在Watchdog中以间隔30s执行一次。

以下是实现了Monitor接口的类:

实现了Monitor的接口的类

WatchDog中比较重要的几个函数

// 设置activityController 用处在后面Watchdog的run()方法会介绍到

public void setActivityController(IActivityController controller) {

synchronized (this) {

mController = controller;

}

}

// 设置watchdog超时后是否能重启手机

public void setAllowRestart(boolean allowRestart) {

synchronized (this) {

mAllowRestart = allowRestart;

}

}

// 在FgThread中添加对monitor的监控

public void addMonitor(Monitor monitor) {

synchronized (this) {

mMonitorChecker.addMonitorLocked(monitor);

}

}

// 添加需要监控的handler 超时时间默认为60s

public void addThread(Handler thread) {

addThread(thread, DEFAULT_TIMEOUT);

}

// 添加需要监控的handler 可以自定义超时时间

public void addThread(Handler thread, long timeoutMillis) {

synchronized (this) {

final String name = thread.getLooper().getThread().getName();

mHandlerCheckers.add(new HandlerChecker(thread, name, timeoutMillis));

}

}

// 暂停当前正在运行的线程的监视操作。在执行可能错误触发watchdog的长时间运行的操作之前非常有用。

// 每个调用都需要一个匹配的{@link #resumeWatchingCurrentThread}调用。

public void pauseWatchingCurrentThread(String reason) {

synchronized (this) {

for (HandlerChecker hc : mHandlerCheckers) {

if (Thread.currentThread().equals(hc.getThread())) {

hc.pauseLocked(reason);

}

}

}

}

// 恢复watchdog的检测状态

public void resumeWatchingCurrentThread(String reason) {

synchronized (this) {

for (HandlerChecker hc : mHandlerCheckers) {

if (Thread.currentThread().equals(hc.getThread())) {

hc.resumeLocked(reason);

}

}

}

}

// 计算HandlerChecker的状态 只要一个handler中有其中一个HandlerChecker超时了

// 那Watchdog就超时了

private int evaluateCheckerCompletionLocked() {

int state = COMPLETED;

for (int i=0; i

HandlerChecker hc = mHandlerCheckers.get(i);

state = Math.max(state, hc.getCompletionStateLocked());

}

return state;

}

// 获取哪个HandlerChecker超时,并加入数组返回

private ArrayList getBlockedCheckersLocked() {

ArrayList checkers = new ArrayList();

for (int i=0; i

HandlerChecker hc = mHandlerCheckers.get(i);

if (hc.isOverdueLocked()) {

checkers.add(hc);

}

}

return checkers;

}

// 获取每个超时HandlerChecker的超时原因,并组成String返回

private String describeCheckersLocked(List checkers) {

StringBuilder builder = new StringBuilder(128);

for (int i=0; i

if (builder.length() > 0) {

builder.append(", ");

}

builder.append(checkers.get(i).describeBlockedStateLocked());

}

return builder.toString();

}

复制代码

WatchDog.run()

接下来我们就看一下Watchdog的核心代码,run()方法

@Override

public void run() {

// 是否在等待的前半段时间 true则为等待的后半段时间

boolean waitedHalf = false;

// 死循环

while (true) {

final List blockedCheckers;

// 超时原因 用于日志打印

final String subject;

// 是否允许system_server重启 默认为true

// 可以通过watchdog.setAllowRestart()重新设置值

final boolean allowRestart;

// 调试进程连接数 有的话会赋值为2

int debuggerWasConnected = 0;

synchronized (this) {

// CHECK_INTERVAL为30s

long timeout = CHECK_INTERVAL;

// 逐一执行每个HandlerChecker的scheduleCheckLocked()方法

for (int i=0; i

HandlerChecker hc = mHandlerCheckers.get(i);

hc.scheduleCheckLocked();

}

if (debuggerWasConnected > 0) {

debuggerWasConnected--;

}

// 这里用uptimeMillis() 是因为只会在设备唤醒的时候计算超时,

// 设备休眠计算时间会导致错误重启

long start = SystemClock.uptimeMillis();

while (timeout > 0) {

if (Debug.isDebuggerConnected()) {

debuggerWasConnected = 2;

}

try {

// 等待30s

wait(timeout);

// Note: mHandlerCheckers and mMonitorChecker may have changed after waiting

} catch (InterruptedException e) {

Log.wtf(TAG, e);

}

if (Debug.isDebuggerConnected()) {

debuggerWasConnected = 2;

}

// 有可能wait到一半的时候发生了InterruptedException 导致时间没有走完

// 只要没有消耗完timeout的值 就继续等待

timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);

}

// fd是否达到了限制的数量

boolean fdLimitTriggered = false;

if (mOpenFdMonitor != null) {

fdLimitTriggered = mOpenFdMonitor.monitor();

}

// fd数量限制没有达到

if (!fdLimitTriggered) {

final int waitState = evaluateCheckerCompletionLocked();

// 都完成了的话 继续下个循环 本次循环结束

if (waitState == COMPLETED) {

waitedHalf = false;

continue;

} else if (waitState == WAITING) {

// 在等待的前半段时间 继续下个循环 本次循环结束

continue;

} else if (waitState == WAITED_HALF) {

if (!waitedHalf) {

Slog.i(TAG, "WAITED_HALF");

// 我们等了一半的死锁探测时间。

// 获取堆栈跟踪并等待另一半。

ArrayList pids = new ArrayList();

pids.add(Process.myPid());

ActivityManagerService.dumpStackTraces(pids, null, null,

getInterestingNativePids());

waitedHalf = true;

}

continue;

}

// 到这里的时候已经有handler超时了

// 获取超时的HandlerChecker

blockedCheckers = getBlockedCheckersLocked();

// 获取超时的HandlerChecker的信息

subject = describeCheckersLocked(blockedCheckers);

} else {

// fd数量限制达到了

blockedCheckers = Collections.emptyList();

subject = "Open FD high water mark reached";

}

allowRestart = mAllowRestart;

}

// 如果我们到这里,这意味着系统很可能挂起。

// 首先从系统进程的所有线程收集堆栈跟踪。然后杀死这个进程,

// 这样系统才会重新启动。

EventLog.writeEvent(EventLogTags.WATCHDOG, subject);

ArrayList pids = new ArrayList<>();

pids.add(Process.myPid());

if (mPhonePid > 0) pids.add(mPhonePid);

// 打印java线程和native线程堆栈

final File stack = ActivityManagerService.dumpStackTraces(

pids, null, null, getInterestingNativePids());

// 挂起5s确保堆栈能写入到文件中

SystemClock.sleep(5000);

// 让kernel dump全部的blocked线程 和 cpu信息

doSysRq('w');

doSysRq('l');

// 尝试把error加到dropbox下,但假设ActivityManager自己会死锁

// 当这种情况发生时, 导致以下语句死锁,watchdog作为一个整体将会失效

Thread dropboxThread = new Thread("watchdogWriteToDropbox") {

public void run() {

// 如果其中一条被观察的线程在Watchdog init()方法执行前被挂起

// 我们则没有一个有效的AMS,所以不能把log打印存储到dropbox路径下

if (mActivity != null) {

mActivity.addErrorToDropBox(

"watchdog", null, "system_server", null, null, null,

subject, null, stack, null);

}

StatsLog.write(StatsLog.SYSTEM_SERVER_WATCHDOG_OCCURRED, subject);

}

};

dropboxThread.start();

try {

// 等待2s 让dropboxThread返回

dropboxThread.join(2000);

} catch (InterruptedException ignored) {}

IActivityController controller;

synchronized (this) {

controller = mController;

}

// 如果ActivityController不为null

if (controller != null) {

Slog.i(TAG, "Reporting stuck state to activity controller");

try {

// 由于挂起system process而禁用dump 防止controller在报告错误的时候被挂起

Binder.setDumpDisabled("Service dumps disabled due to hung system process.");

// 1 = keep waiting, -1 = kill system

int res = controller.systemNotResponding(subject);

if (res >= 0) {

Slog.i(TAG, "Activity controller requested to coninue to wait");

waitedHalf = false;

continue;

}

} catch (RemoteException e) {

}

}

// 只有在没有debugger连接的情况下才会杀死进程。

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 {

Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + subject);

WatchdogDiagnostics.diagnoseCheckers(blockedCheckers);

Slog.w(TAG, "*** GOODBYE!");

// 结束进程 watchdog存在于system_server进程之下

// 因为watchdog就是在system_server初始化的

Process.killProcess(Process.myPid());

System.exit(10);

}

waitedHalf = false;

}

}

复制代码

文章到这里就完了,基本上只是对参考文章进行代码的上更新(Android Q),结构大致一样。仅作为学习记录用途。

 

 

 

 

参考文章

Android 系统中的 WatchDog 详解Watchdog机制以及问题分析

作者:ContentPaneX链接:https://juejin.cn/post/6844904085695496199来源:稀土掘金

查看原文