WatchDog 的原理

# 核心源码( Android 9.0 )

Source Path(frameworks/base/)
AndroidXRef http://androidxref.com/9.0.0_r3/xref/
SystemServer.java services/java/com/android/server/SystemServer.java
Watchdog.java services/core/java/com/android/server/Watchdog.java
ActivityManagerService.java services/core/java/com/android/server/am/ActivityManagerService.java

一、什么是 WatchDog?

1.1 Linux

在 Linux 嵌入式开发中,为了防止嵌入式系统 MCU 里的程序因为干扰而跑飞,专门在 MCU 里设计了一个定时器电路,叫做 看门狗。当 MCU 正常工作的,每隔一段时间会输出一个信号给看门狗,也就是所谓的 喂狗。如果程序跑飞,MCU 在规定的时间内没法喂狗,这时看门狗就会直接触发一个 reset 信号,让 CPU 重新启动

1.2 Android

那么在 Android 中,framework 内部也设计了一个系统服务 Watchdog,它可以看作是一个软件看门狗,用来保护重要的系统服务

为什么要设计这种“看门狗”呢?

做系统开发的工程师都知道,SystemServer 进程是相当核心的,也是个非常复杂的进程,启动并管理着系统中几十个核心服务,这些服务支撑着我们的系统运行,让你体验到绚丽的功能。但是我们很难保证这些服务不出问题,服务出了问题就会导致功能障碍,这会严重影响用户体验。

例如在你的项目中可能会出现以下问题:如果因为 SystemServer 的某些核心服务 hang 住而产生了冻屏现象,那么 Process.killProcess 就会杀死 SystemServer 进程,它一旦挂了,Zygote 就会跟着殉情,那必然会导致手机需要重启(平常出现的软重启其实就是这样的情况),但是我们也不需要紧张,因为 SystemServer 出现了问题,导致系统重启,与 Kernel 没有关系,大部分问题还是很好解决的,并且软重启的启动时间很快,对用户的体验影响并不是很致命,但是我们还是要尽量避免这样的情况产生。

那么话说回来,Android 系统层到底是怎么去监控这些核心服务的状态的呢?这就涉及到我们今天要讲解的 WatchDog


二、WatchDog 启动

WatchDog 是在 SystemServer 中创建并在 AMS 的 SystemReady 接口的 CallBack 函数中实现 WatchDog 的启动,接下来我们跟源码:

2.1 SystemServer.run

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// frameworks/base/services/java/com/android/server/SystemServer.java

private void run() {
... ...
// Start services.
try {
startBootstrapServices();
startCoreServices();
startOtherServices(); // 在 startOtherServices 方法中去创建 WatchDog
SystemServerInitThreadPool.shutdown();
} catch (Throwable ex) {
... ...
}
... ...
}

2.2 startOtherServices

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// frameworks/base/services/java/com/android/server/SystemServer.java

private void startOtherServices() {
... ...
try {
... ...
// 获取 Watchdog 对象,2.3 节分析
final Watchdog watchdog = Watchdog.getInstance();
// 注册 receiver 以接收系统重启广播,2.6 节分析
watchdog.init(context, mActivityManagerService);
... ...
}
... ...
// 在 ActivityManagerService 的 SystemReady 接口的 CallBack 函数中实现 WatchDog 的启动
mActivityManagerService.systemReady(() -> {
... ...
Watchdog.getInstance().start();
... ...
}
}

2.3 Watchdog.getInstance

1
2
3
4
5
6
7
8
9
10
// frameworks/base/services/core/java/com/android/server/Watchdog.java

public static Watchdog getInstance() {
if (sWatchdog == null) {
// 典型的单例模式创建实例的方法,构造函数 2.4 节分析
sWatchdog = new Watchdog();
}

return sWatchdog;
}

2.4 WatchDog 构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// frameworks/base/services/core/java/com/android/server/Watchdog.java

private Watchdog() {
super("watchdog");

mMonitorChecker = new HandlerChecker(FgThread.getHandler(),
"foreground thread", DEFAULT_TIMEOUT);
mHandlerCheckers.add(mMonitorChecker);
mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),
"main thread", DEFAULT_TIMEOUT));
mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),
"ui thread", DEFAULT_TIMEOUT));
mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),
"i/o thread", DEFAULT_TIMEOUT));
mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),
"display thread", DEFAULT_TIMEOUT));
// addMonitor 方法,2.5 节分析
addMonitor(new BinderThreadMonitor());

// 从 Android O 开始新增对 FD 泄露的监控
mOpenFdMonitor = OpenFdMonitor.create();
assert DB ||
DEFAULT_TIMEOUT > ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;
}

我们发现 Watchdog 的构造函数中构建了很多 HandlerChecker 对象,是用来干嘛的?这个我们下面再作讲解。

2.5 WatchDog.addMonitor

我们看下最后的 addMonitor 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
// frameworks/base/services/core/java/com/android/server/Watchdog.java

final HandlerChecker mMonitorChecker;

public void addMonitor(Monitor monitor) {
synchronized (this) {
if (isAlive()) {
throw new RuntimeException("Monitors can't be added once the Watchdog is running");
}
// 初始化 BinderThreadMonitor 放入 HandlerChecker 列表中
mMonitorChecker.addMonitor(monitor);
}
}

2.6 WatchDog.init

1
2
3
4
5
6
7
8
9
10
// frameworks/base/services/core/java/com/android/server/Watchdog.java

public void init(Context context, ActivityManagerService activity) {
mResolver = context.getContentResolver();
mActivity = activity;

// 注册了 reboot 的广播,软重启的操作在这里进行
context.registerReceiver(new RebootRequestReceiver(), new IntentFilter(Intent.ACTION_REBOOT),
android.Manifest.permission.REBOOT, null);
}

2.7 WatchDog.run

WatchDog 作为一个 线程,启动之后最重要的肯定是执行它的 run 方法,在具体跟踪源码之前,我们先看下它的基本原理流程图,然后在后面的分析过程中,对比流程图,我们可以更好的理清思路。


mjsZjA.png


我们来看看 run 方法做了哪些工作:(流程图左)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// frameworks/base/services/core/java/com/android/server/Watchdog.java

public class Watchdog extends Thread {
... ...
@Override
public void run() {
boolean waitedHalf = false;
while (true) {
final List<HandlerChecker> blockedCheckers;
final String subject;
final boolean allowRestart;
int debuggerWasConnected = 0;
synchronized (this) {
// CHECK_INTERVAL 时长是 DEFAULT_TIMEOUT 的一半,默认 30 s
long timeout = CHECK_INTERVAL;
// 30 秒轮询系统中所有的 monitor
for (int i=0; i<mHandlerCheckers.size(); i++) {
HandlerChecker hc = mHandlerCheckers.get(i);
// 执行 scheduleCheckLocked 方法,流程图右
// 关于 scheduleCheckLocked 方法,我们放在 HandlerChecker 中介绍
hc.scheduleCheckLocked();
}

if (debuggerWasConnected > 0) {
debuggerWasConnected--;
}

// 等待 30 秒,等待检查结果
long start = SystemClock.uptimeMillis();
while (timeout > 0) {
if (Debug.isDebuggerConnected()) {
debuggerWasConnected = 2;
}
try {
wait(timeout);
} catch (InterruptedException e) {
Log.wtf(TAG, e);
}
if (Debug.isDebuggerConnected()) {
debuggerWasConnected = 2;
}
timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);
}
boolean fdLimitTriggered = false;
if (mOpenFdMonitor != null) {
fdLimitTriggered = mOpenFdMonitor.monitor();
}

if (!fdLimitTriggered) {
// 调用 evaluateCheckerCompletionLocked 方法检查 HandlerChecker 的状态
final int waitState = evaluateCheckerCompletionLocked();
// monitor() 顺利返回
if (waitState == COMPLETED) {
waitedHalf = false;
continue;
} else if (waitState == WAITING) {
// 不到 30 秒的等下个 30 秒,还未超时
continue;
} else if (waitState == WAITED_HALF) {
// 过了 30 秒的,继续等,还未超时
if (!waitedHalf) {
ArrayList<Integer> pids = new ArrayList<Integer>();
pids.add(Process.myPid());
// 超过 30 s,则 dumpStackTraces
ActivityManagerService.dumpStackTraces(true, pids, null, null,
getInterestingNativePids());
waitedHalf = true;
}
continue;
}

// 如果状态是 overdue!,也就是超过60秒
// 通过 getBlockedCheckersLocked() 方法,获取阻塞的 HandlerChecker,生成一些描述信息
blockedCheckers = getBlockedCheckersLocked();
subject = describeCheckersLocked(blockedCheckers);
} else {
blockedCheckers = Collections.emptyList();
subject = "Open FD high water mark reached";
}
allowRestart = mAllowRestart;
}

// 保存日志,重启系统
EventLog.writeEvent(EventLogTags.WATCHDOG, subject);
... ...
// 再次 dumpStackTraces,包含 pids 中的进程和 getInterestingNativePids 中的进程
final File stack = ActivityManagerService.dumpStackTraces(
!waitedHalf, pids, null, null, getInterestingNativePids());
... ...
// Only kill the process if the debugger is not attached.
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!");
Process.killProcess(Process.myPid()); // 干掉 SystemServer 进程
System.exit(10);
}
waitedHalf = false;
}
}
... ...
}

三、WatchDog 监视

上面我们了解了 WatchDog 是如何启动的,但是 WatchDog 存在的意思是去 看管系统服务,需要看管哪些服务,这是需要明确告知 WatchDog 的,所以接下来我们就有分析的方向了。

在 Android 中,系统服务需要向 WatchDog 报道,也就是需要向 WatchDog 注册,这样才好被看护。

3.1 看护方式

WatchDog 提供了 两种 看护方式:

    (1)通过 monitor() 回调监视服务关键区是否出现死锁或阻塞;

    (2)通过发送消息监视服务主线程是否阻塞。

比如,挑个服务来说,ActivityManagerService 我们再熟悉不过了,看下源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

// 继承 watchdog.Monitor 接口
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
... ...
public ActivityManagerService(Context systemContext) {
... ...
// 在构造函数中把自身注册到 watchdog monitor 服务中
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
... ...
}
... ...
// 在类中实现 watchdog.Monitor 所需的 monitor 方法
// watchdog 运行时每 30 秒会回调这个方法来锁一次这个关键区,如果 60 秒都无法得到锁,
// 就说明服务已经发生了死锁,必须重启设备。
public void monitor() {
synchronized (this) { }
}
... ...
}

我们注意一下,这边有两个检测项

addMonitor:在每一个检测周期中 watchdog 会使用 foreground threadHandlerChecker 回调服务注册的 monitor() 方法给服务的关键区上锁并马上释放,以检测关键区 是否存在死锁或阻塞

addThread:watchdog 会定时通过 HandlerChecker 向系统服务发送消息,以检测服务 主线程是否被阻塞

3.2 HandlerChecker

通过以上的分析,我们知道了 WatchDog 是如何监控核心服务,以及服务如果向 WatchDog 注册了,那么具体的检测是谁负责的,检测流程是什么样的

回顾下 WatchDog 的构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// frameworks/base/services/core/java/com/android/server/Watchdog.java

private Watchdog() {
super("watchdog");

mMonitorChecker = new HandlerChecker(FgThread.getHandler(),
"foreground thread", DEFAULT_TIMEOUT);
mHandlerCheckers.add(mMonitorChecker);
mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),
"main thread", DEFAULT_TIMEOUT));
mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),
"ui thread", DEFAULT_TIMEOUT));
mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),
"i/o thread", DEFAULT_TIMEOUT));
mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),
"display thread", DEFAULT_TIMEOUT));
addMonitor(new BinderThreadMonitor());

// 从 Android O 开始新增对 FD 泄露的监控
mOpenFdMonitor = OpenFdMonitor.create();
assert DB ||
DEFAULT_TIMEOUT > ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;
}

我们发现 foreground threadmain thread 都传入了一个 HandlerChecker 类,这个类其实就是检测超时的执行者


mjsZjA.png


接下来,我们就要分析流程图右边的源码逻辑了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// frameworks/base/services/core/java/com/android/server/Watchdog.java

// HandlerChecker 继承了 Runnable,也就是说每个 HandlerChecker
// 在各自服务的主线程中运行并完成相应的检查,彼此之间不会互相干扰。
public final class HandlerChecker implements Runnable {
private final Handler mHandler;
private final String mName;
private final long mWaitMax;
private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
private boolean mCompleted;
private Monitor mCurrentMonitor;
private long mStartTime;

HandlerChecker(Handler handler, String name, long waitMaxMillis) {
mHandler = handler;
mName = name;
mWaitMax = waitMaxMillis;
mCompleted = true;
}

// 注册的 monitor 都在这里
public void addMonitor(Monitor monitor) {
mMonitors.add(monitor);
}

// scheduleCheckLocked() 是 HandlerChecker 的触发点
// 默认每隔 30 秒,WatchDog 会通过 scheduleCheckLocked 函数轮询一下 mMonitors 中的注册的 monitor
public void scheduleCheckLocked() {
if (mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling()) {
mCompleted = true;
return;
}

if (!mCompleted) {
// check 过了就不用再 check
// we already have a check in flight, so no need
return;
}

mCompleted = false;
mCurrentMonitor = null;
mStartTime = SystemClock.uptimeMillis();
// check 的时候往 MessageQueue 中最前面添加,最终会调用 run 方法
mHandler.postAtFrontOfQueue(this);
}

public boolean isOverdueLocked() {
return (!mCompleted) && (SystemClock.uptimeMillis() > mStartTime + mWaitMax);
}

// 获取 scheduleCheckLocked 最后的结果,如果没有从 monitor() 出来就会出现 WAIT
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;
}

// 每个 HandlerChecker 都是个单独的 thread,mHandler 比较特殊
public Thread getThread() {
return mHandler.getLooper().getThread();
}

public String getName() {
return mName;
}

public 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() + ")";
}
}

// scheduleCheckLocked 方法会执行这里
@Override
public void run() {
final int size = mMonitors.size();
for (int i = 0 ; i < size ; i++) {
synchronized (Watchdog.this) {
mCurrentMonitor = mMonitors.get(i);
}
// 调用注册进来的 monitor 的 monitor() 函数
mCurrentMonitor.monitor();
}

synchronized (Watchdog.this) {
mCompleted = true;
mCurrentMonitor = null;
}
}
}

每个通过 addThread 向 watchdog 注册自身的服务都对应一个 HandlerChecker 类实例。

我们现在知道了通过 addThread(mHandler) 方法注册的服务是由 HandlerChecker 来检测的,那么还剩下一个 addMonitor(this) 方法,通过这种方法注册的服务由谁监控?

答案就是前面出现的 mMonitorChecker。它除了需要检测 主线程是否堵塞外,还需要回调系统服务注册的 monitor() 方法,以检测这些服务的 关键区是否存在死锁或阻塞