(本文系统源码基于Andoroid 10.0.0-r16)
Watchdog
的中文叫“看门狗”,最早引入Watchdog
是在单片机系统中,由于单片机的工作环境容易受到外界磁场的干扰,导致程序“跑飞”,造成整个系统无法正常工作,因此,引入了一个“看门狗”,对单片机的运行状态进行实时监测,针对运行故障做一些保护处理,譬如让系统重启。这种Watchdog属于硬件层面,必须有硬件电路的支持。
Linux
也引入了Watchdog
,在Linux
内核下,当Watchdog启动后,便设定了一个定时器,如果在超时时间内没有对/dev/Watchdog
进行写操作,则会导致系统重启,通过定时器实现的Watchdog属于软件层面。
Android设计了一个软件层面Watchdog,用于保护一些重要的系统服务,当出现故障时,通常会让Android系统重启。由于这种机制的存在,就经常会出现一些system_server进程被Watchdog杀掉而发生系统重启的问题。
原理就是每到达一定的时间,获取正在监控的锁(Monitor.monitor()
),能否获得锁从而判断是否产生了死锁。
首先我们可以看到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个则阻塞线程等待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) { } } 复制代码
我们再来看看在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<Monitor> mMonitors = new ArrayList<Monitor>(); // 还未加入mMonitors中的Monitor // 等待mCompleted为true的时候才一次性加入mMonitors中 private final ArrayList<Monitor> mMonitorQueue = new ArrayList<Monitor>(); // 是否检查完成的标志位 空闲的话默认为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接口的类:
// 设置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<mHandlerCheckers.size(); i++) { HandlerChecker hc = mHandlerCheckers.get(i); state = Math.max(state, hc.getCompletionStateLocked()); } return state; } // 获取哪个HandlerChecker超时,并加入数组返回 private ArrayList<HandlerChecker> getBlockedCheckersLocked() { ArrayList<HandlerChecker> checkers = new ArrayList<HandlerChecker>(); for (int i=0; i<mHandlerCheckers.size(); i++) { HandlerChecker hc = mHandlerCheckers.get(i); if (hc.isOverdueLocked()) { checkers.add(hc); } } return checkers; } // 获取每个超时HandlerChecker的超时原因,并组成String返回 private String describeCheckersLocked(List<HandlerChecker> checkers) { StringBuilder builder = new StringBuilder(128); for (int i=0; i<checkers.size(); i++) { if (builder.length() > 0) { builder.append(", "); } builder.append(checkers.get(i).describeBlockedStateLocked()); } return builder.toString(); } 复制代码
接下来我们就看一下Watchdog的核心代码,run()
方法
@Override public void run() { // 是否在等待的前半段时间 true则为等待的后半段时间 boolean waitedHalf = false; // 死循环 while (true) { final List<HandlerChecker> 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<mHandlerCheckers.size(); 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<Integer> pids = new ArrayList<Integer>(); 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<Integer> 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机制以及问题分析