Android SystemServer 中 WatchDog
一、WatchDog 简介
早期手机平台上通常是在设备中增加一个硬件看门狗,软件系统必须定时的向看门狗硬件中写值来表示自己没出故障(俗称“喂狗”),否则超过了规定的时间看门狗就会重新启动设备。
Android 的 SystemServer 是一个非常复杂的进程,里面运行的服务超过五十种,是最可能出问题的进程,因此有必要对 SystemServer 中运行的各种线程实施监控。但是如果使用硬件看门狗的工作方式,每个线程隔一段时间去喂狗,不但非常浪费CPU,而且会导致程序设计更加复杂。因此 Android 开发了 WatchDog 类作为软件看门狗来监控 SystemServer 中的线程。一旦发现问题,WatchDog 会杀死 SystemServer 进程。
Watchdog 工作原理
Watchdog 主要服务对象为 Framework 层用系统 service 和主要线程,它的工作原理是循环向被监控线程消息队列中投递 Runnable,来检查在指定时间内是否返回,如果超时不返回,则视为死锁,记录该 watchdog 记录,并做后续 dump 处理,然后 kill 掉当前 SystemServer 进程,SystemServer 的父进程 Zygote 接收到 SystemServer 的死亡信号后,会杀死自己,Zygote 进程死亡的信号传递到 Init 进程后,Init 进程会杀死 Zygote 进程所有的子进程并重启 Zygot e并上报 Framework crash,这样整个手机相当于重启一遍。通常 SystemServer 出现问题和 kernel 并没有关系,所以这种“软重启”大部分时候都能够解决问题,而且这种“软重启”的速度更快,对用户的影响也更小。
二、WatchDog 的启动
WatchDog 是在 SystemServer 进程中被初始化和启动的,在 SystemServer 的 run 方法中,各种Android 服务被注册和启动,其中也包括了WatchDog 的初始化和启动,代码如下:
final Watchdog watchdog = Watchdog.getInstance();
watchdog.init(context, mActivityManagerService);
在 SystemServer 中 startOtherServices()
的后半段,在 AmS 的 SystemReady 接口的 CallBack 函数中实现 WatchDog 的启动:
Watchdog.getInstance().start();
首先来看 Watchdog 的构造方法:
private Watchdog() {
super("watchdog");
//众多 HandlerChecker 中 mMonitorChecker 是最特殊的一个,
//它的作用是用来检测监听的服务是否死锁
mMonitorChecker = new HandlerChecker(FgThread.getHandler(),
"foreground thread", DEFAULT_TIMEOUT);
mHandlerCheckers.add(mMonitorChecker);
// Add checker for main thread. We only do a quick check since there
// can be UI running on the thread.
mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),
"main thread", DEFAULT_TIMEOUT));
// Add checker for shared UI thread.
mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),
"ui thread", DEFAULT_TIMEOUT));
// And also check IO thread.
mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),
"i/o thread", DEFAULT_TIMEOUT));
// And the display thread.
mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),
"display thread", DEFAULT_TIMEOUT));
// Initialize monitor for Binder threads.
addMonitor(new BinderThreadMonitor());
mOpenFdMonitor = OpenFdMonitor.create();
// See the notes on DEFAULT_TIMEOUT.
assert DB ||
DEFAULT_TIMEOUT > ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;
}
三、WatchDog两个主要作用
1. 检测关键服务是否存在死锁或阻塞
关键服务通过addMonitor()方法向WatchDog注册,WatchDog通过 foreground thread的HandlerChecker回调服务实现的monitor()方法,来判断是否死锁或阻塞。
public void addMonitor(Monitor monitor) {
mMonitors.add(monitor);
}
一般关键服务(如WmS等),都已经实现Watchdog.Monitor接口,并会把自己添加到watchdog的monitor列表中
@Override
public void monitor() {
synchronized (mWindowMap) { }
}
// 把自己添加到 Watchdog monitors 中
Watchdog.getInstance().addMonitor(this);
我们来看Watchdog中addMonitor()方法实现:
public void addMonitor(Monitor monitor) {
synchronized (this) {
if (isAlive()) {
throw new RuntimeException("Monitors can't be added once the Watchdog is running");
}
//最终会把自己添加到mMonitorChecker的monitor列表中
mMonitorChecker.addMonitor(monitor);
}
}
2. 检测服务主线程是否阻塞
通过向服务主线程发送 Message 来确认,外部服务也可以通过 Watchdog.java addThread() 方法加入到检测列表中。
public void addThread(Handler thread, long timeoutMillis) {
synchronized (this) {
if (isAlive()) {
throw new RuntimeException("Threads can't be added once the Watchdog is running");
}
final String name = thread.getLooper().getThread().getName();
//每个新加入的thread,WatchDog都会为其创建一个HandlerChecker对象
mHandlerCheckers.add(new HandlerChecker(thread, name, timeoutMillis));
}
}
四、Watchdog的工作机制
watchdog流程图.jpgWatchdog 本身也是一个线程,我们来看它的 run() 方法:
public void run() {
boolean waitedHalf = false;
while (true) {
final List<HandlerChecker> blockedCheckers;
synchronized (this) {
long timeout = CHECK_INTERVAL;
// Make sure we (re)spin the checkers that have become idle within
// this wait-and-check interval
for (int i=0; i<mHandlerCheckers.size(); i++) {
HandlerChecker hc = mHandlerCheckers.get(i);
//调用每个HandlerChecker的scheduleCheckLocked() 方法
① hc.scheduleCheckLocked();
}
long start = SystemClock.uptimeMillis();
while (timeout > 0) {
try {
wait(timeout);
}
timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);
}
boolean fdLimitTriggered = false;
if (mOpenFdMonitor != null) {
fdLimitTriggered = mOpenFdMonitor.monitor();
}
if (!fdLimitTriggered) {
② final int waitState = evaluateCheckerCompletionLocked();
if (waitState == COMPLETED) { //线程状态正常,重新轮询
// The monitors have returned; reset
waitedHalf = false;
continue;
} else if (waitState == WAITING) {//处于阻塞状态,但监测时间小于30s,继续监测
// still waiting but within their configured intervals; back off and recheck
continue;
//处于阻塞状态,监测时间已经超过30s,开始dump一些系统信息,然后继续监测30s
} else if (waitState == WAITED_HALF) {
if (!waitedHalf) {
// We've waited half the deadlock-detection interval. Pull a stack
// trace and wait another half.
ArrayList<Integer> pids = new ArrayList<Integer>();
pids.add(Process.myPid());
waitedHalf = true;
}
continue;
}
// something is overdue!
//上面三种状态都不满足,表明是OVERDUE状态:检测超时
blockedCheckers = getBlockedCheckersLocked();
subject = describeCheckersLocked(blockedCheckers);
} else {
blockedCheckers = Collections.emptyList();
subject = "Open FD high water mark reached";
}
allowRestart = mAllowRestart;
}
Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + subject);
//杀死系统进程
Process.killProcess(Process.myPid());
System.exit(10);
waitedHalf = false;
}
}
首先分析 ① scheduleCheckLocked()
,它的主要作用是执行 HandlerChecker 的 run() 方法:
public void scheduleCheckLocked() {
if (mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling()) {
mCompleted = true;
return;
}
if (!mCompleted) {
// we already have a check in flight, so no need
return;
}
//每次重新检测handlerchecker对象时,都会把mCompleted置为false,等检测发现状态正常时会再把它置为true
mCompleted = false;
mCurrentMonitor = null;
//这个变量很关键,设置每个HandlerChecker对象检测的开始时间,后面计算时间时会用到
mStartTime = SystemClock.uptimeMillis();
//在相应的handler中执行HandlerChecker的run()方法
mHandler.postAtFrontOfQueue(this);
}
HandlerChecker 的 run() 方法,在每一个检测周期中 watchdog 会使用
foreground thread 的 HandlerChecker 回调服务注册的 monitor() 方法给服务的关键区上锁并马上释放,以检测关键区是否存在死锁或阻塞;
@Override
public void run() {
final int size = mMonitors.size();
// for循环用来检测监听列表中是否有阻塞,而且只有mMonitorChecker会走进此循环,
//其余的handlerChecker因为mMonitors为空,都不会执行此循环
for (int i = 0 ; i < size ; i++) {
synchronized (Watchdog.this) {
mCurrentMonitor = mMonitors.get(i);
}
mCurrentMonitor.monitor();
}
synchronized (Watchdog.this) {
mCompleted = true;
mCurrentMonitor = null;
}
}
再来看 ② evaluateCheckerCompletionLocked()
方法,主要作用是等待 30s 后,开始检测 HandlerChecker 的状态。
WatchDog 检测是以 HandlerChecker 对象为单位,它的状态分为四种:
State | Function |
---|---|
COMPLETED | 检测对象运行正常 |
WAITING | 检测不到 30s,不算超时,继续监听 |
WAITED_HALF | 检测已超时 30s,开始 dump 关键信息,但还会继续监听 30s |
OVERDUE | 检测超时,准备 kill 当前进程 |
private int evaluateCheckerCompletionLocked() {
int state = COMPLETED;
for (int i=0; i<mHandlerCheckers.size(); i++) {
HandlerChecker hc = mHandlerCheckers.get(i);
//调用 getCompletionStateLocked() 获取当前 state
state = Math.max(state, hc.getCompletionStateLocked());
}
return state;
}
//获得当前HandlerChecker的状态,注意每个HandlerChecker的mStartTime都不相同
public int getCompletionStateLocked() {
if (mCompleted) {
return COMPLETED;
} else {
long latency = SystemClock.uptimeMillis() - mStartTime;
if (latency < mWaitMax/2) {
return WAITING;
} else if (latency < mWaitMax) { //注意:这里的 mWaitMax = 60s
return WAITED_HALF;
}
}
return OVERDUE;
}
最后分析 ③ getBlockedCheckersLocked()
,获得阻塞或死锁的线程信息
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;
}
打印阻塞或死锁线程的信息:
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();
}
public String describeBlockedStateLocked() {
// mCurrentMonitor == null 表示出现阻塞
if (mCurrentMonitor == null) {
return "Blocked in handler on " + mName + " (" + getThread().getName() + ")";
} else {
// mCurrentMonitor == null 表示出现死锁
return "Blocked in monitor " + mCurrentMonitor.getClass().getName()
+ " on " + mName + " (" + getThread().getName() + ")";
}
}
注意通过 monitor() 方法检查死锁针对不同线程之间的,而服务主线程是否阻塞是针对主线程,所以通过 sendMessage() 方式是只能检测主线程是否阻塞,而不能检测是否死锁,因为如果服务主线程和另外一个线程发生死锁(如另外一个线程synchronized 关键字长时间持有某个锁,不释放),此时向主线程发送 Message,主线程的Handler是可以继续处理的。