背景
watchdog即Linux看门狗,用于监视系统的运行。
在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,使程序的运行产生异常,从而引发不可预料的后果。
针对上面的现象,有必要对单片机的运行状态进行实时监测,于是便产生了watchdog。
Android中也定义了watchdog,用于检测关键服务的运行情况。
版本
android 6.0
正文
在分析SystemServer进程时,我们知道当SystemServer启动后,将启动一些系统服务,其中就包括watchdog。
在SystemServer.java中的startOtherServices中:
private void startOtherServices() {
........
Slog.i(TAG, "Init Watchdog");
//创建watchdog对象
final Watchdog watchdog = Watchdog.getInstance();
//初始化watchdog
watchdog.init(context, mActivityManagerService);
//其它服务创建,其中有些服务需要检测
........
//所有需要检测的服务创建完毕,启动watchdog
Watchdog.getInstance().start();
}
以上就是SystemServer.java中,watchdog涉及的主要代码,相对比较简单。
1. 创建watchdog对象
public static Watchdog getInstance() {
if (sWatchdog == null) {
sWatchdog = new Watchdog();
}
return sWatchdog;
}
从Watchdog.java的getInstance函数,我们知道watchdog是一个单例对象,保存在类的静态成员变量中。
private Watchdog() {
...........
// The shared foreground thread is the main checker. It is where we will also dispatch monitor checks and do other work.
//DEFAULT_TIMEOUT值为60s
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.
//这里用的是main looper,考虑到watchdog对象在SystemSever中创建,那么这里用的应该是SystemServer的looper
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());
}
从watchDog的构造函数,可以看出整个watchdog中比较重要的成员类型其实只有两个,一个是HandlerChecker,一个是Monitor。在构造函数中,watchdog利用不同线程的handler创建了几个HandlerChecker,同时创建了Binder对应的Monitor。
接下来我们看看,Monitor和HandlerChecker具体的含义。
1.1 Monitor
public interface Monitor {
void monitor();
}
从上面代码可以看出,在watchdog中Monitor实际上只是个接口,将由watchdog的观察对象来实现。
为了弄清楚Monitor的用法,我们可以看看watchdog的监控对象是如何实现的。
我们以比较常见的PowerManagerService为例。
public final class PowerManagerService extends SystemService
implements Watchdog.Monitor {
............
}
@Override // Watchdog.Monitor implementation
public void monitor() {
// Grab and release lock for watchdog monitor to detect deadlocks.
//此处仅检测是否死锁
synchronized (mLock) {
}
从上面的代码可以看出,PowerManagerService自己实现了monitor函数。我们进一步看看PowerManagerService.java的onStart函数:
@Override
public void onStart() {
..........
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
}
可以看到PowerManagerService 将自己作为接口注册到了watchdog,同时还传入了自己的handler。
至此,我们知道了Monitor其实是watchdog对被检测者的抽象,由被检测者自己来实现该接口,以定义需要被检测的内容,从而达到设计模式所说的依赖倒置。
接下来,我们顺着PowerManagerService使用watchdog的流程,看看Watchdog.java中的addThread和addMonitor函数:
public void addThread(Handler thread) {
addThread(thread, DEFAULT_TIMEOUT);
}
public void addThread(Handler thread, long timeoutMillis) {
synchronized (this) {
...........
final String name = thread.getLooper().getThread().getName();
mHandlerCheckers.add(new HandlerChecker(thread, name, timeoutMillis));
}
}
public void addMonitor(Monitor monitor) {
synchronized (this) {
.........
//watchdog的构造函数中已经见过mMonitorChecker,也是一个HandlerChecker
mMonitorChecker.addMonitor(monitor);
}
}
我们可以看到,watchdog的addThread函数,其实就是利用传入handler对应的线程名,构造出一个HandlerChecker,并进行存储;addMonitor函数就是将被检测对象加入到HandlerChecker中。
结合watchdog的构造函数,现在我们知道了watchdog中主要成员的对象关系了。整个watchdog维护了多个HandlerChecker,但其中只有mMonitorChecker中存储了多个Monitor对象。
接下来,我们就可以看看HandlerChecker的内容了。
1.2 HandlerChecker
public final class HandlerChecker implements Runnable {
private final Handler mHandler;
.....
private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
.....
public void addMonitor(Monitor monitor) {
mMonitors.add(monitor);
}
......
}
HandlerChecker是定义于watchdog中的内部类,从上面的代码可以看到Handler是一个runnable对象,同时具有变量存储加入的monitor对象。对于watchdog而言,HandlerChecker是进行工作的主要成员,其中还定义了其它的方法。
接下来我们以watchdog运行的过程为主线,同时介绍HandlerChecker中的主要函数。
2. watchdog运行
public class Watchdog extends Thread {
.........
}
由于watchdog继承自Thread类,因此当在SystemServer.java的startOtherService中,调用Watchdog的start后,最终将调用到watchdog的run方法。
public void run() {
//记录检测结果的一个变量
boolean waitedHalf = false;
//无限循环,检测运行状态
while (true) {
.........
//这里在并行块中执行等待的耗时操作,但会调用wait释放掉锁,降低了对并发性的影响
synchronized (this) {
.........
//每两次检测的间隔时间30s
long timeout = CHECK_INTERVAL;
for (int i=0; i<mHandlerCheckers.size(); i++) {
HandlerChecker hc = mHandlerCheckers.get(i);
//调用HandlerChecker的函数,进行检测
hc.scheduleCheckLocked();
}
long start = SystemClock.uptimeMillis();
while (timeout > 0) {
.........
try {
//检测与评估间的等待;也可以认为是两次检测间的等待
wait(timeout);
} catch (InterruptedException e) {
...........
}
}
//评估检测结果
final int waitState = evaluateCheckerCompletionLocked();
if (waitState == COMPLETED) {
waitedHalf = false;
continue;
} else if (waitState == WAITING) {
continue;
} else if (waitState == WAITED_HALF) {
if (!waitedHalf) {
//打印trace日志
............
waitedHalf = true;
}
continue;
}
..........
}
//记录问题信息
..............
//未连接debug设备时,才会kill进程;注意,watchdog运行在SystemServer进程中
Process.killProcess(Process.myPid());
System.exit(10);
}
从上面的代码可以看出:
watchdog每次依次调用HandlerChecker的scheduleCheckLocked函数,启动检测;
然后,等待一段时间,利用evaluateCheckerCompletionLocked函数评估检测结果;
最后根据检测结果,做出相应的处理,或是打印log信息,执行下一次检测,或是结束掉SystemServer进程。
2.1 启动检测
从上面的分析,我们知道watchdog每次逐一调用自己HandlerChecker的scheduleCheckLocked函数,以启动检测。
public void scheduleCheckLocked() {
if (mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling()) {
mCompleted = true;
return;
}
........
mCompleted = false;
mCurrentMonitor = null;
//记录了post之前的时间
mStartTime = SystemClock.uptimeMillis();
mHandler.postAtFrontOfQueue(this);
}
我们之前也提到过,被监测服务通过addMonitor函数将自己注册到watchdog中,monitor对象都会被添加到的HandlerChecker均是mMonitorChecker。
因此,结合watchdog的构造函数,知道UiThread、IoThread和DisplayThread等对应的HandlerChecker中均不含有monitor对象,即mMonitors.size() == 0 的条件衡成立。
结合上面的代码,我们知道当UiThread等线程的Looper queue,处于polling态时,即这些线程没有阻塞时,watchdog就认为它们是正常的,不再进行进一步的检测。
对于mMonitorChecker和其它Thread对应的正在处理事件的HandlerChecker,scheduleCheckLocked将再调用postAtFrontOfQueue函数做进一步处理。由于HandlerChecker继承自runnable,因此会调用HandlerChecker的run方法。
public void run() {
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;
}
}
可以看到对于mMonitorChecker来说,将调用其记录的每一个Monitor对象的monitor方法,判断服务是否正常;对于不含有Monitor的HandlerChecker而言,只要该run方法执行了,就说明对应Thread能正常处理消息,没有出现死锁等异常。
注意到,mCompleted的值在检测前是false状态,执行成功后才变为true。
2.2 评估结果
接下来,我们看看evaluateCheckerCompletionLocked函数如何根据结果来评估被检测对象的运行情况。
static final int COMPLETED = 0;
static final int WAITING = 1;
static final int WAITED_HALF = 2;
static final int OVERDUE = 3;
以上是watchdog中定义的每个被检测对象的运行状态,COMPLETED表示此次检测正常;OVERDUE表示此次检测超时;其余是中间状态。
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;
}
从上面代码可以看出,watchdog依次调用每个HandlerChecker的getCompletionStateLocked函数,得出对应被检测对象的运行状态,然后整体比较得出系统的运行状态。
根据watchdog预定义的状态值,我们知道watchdog对系统整体运行情况的判断,取决于当前状态最差的被检测对象,可以简单理解为短板理论。
public int getCompletionStateLocked() {
if (mCompleted) {
return COMPLETED;
} else {
long latency = SystemClock.uptimeMillis() - mStartTime;
//mWaitMax是创建HandlerChecker时,定义的timeout时间
if (latency < mWaitMax/2) {
return WAITING;
} else if (latency < mWaitMax) {
return WAITED_HALF;
}
}
return OVERDUE;
}
上面为getCompletionStateLocked函数的实现,结合上述代码,现在再重新看一下watchdog run方法中,对执行结果判断的代码:
..........
if (waitState == COMPLETED) {
waitedHalf = false;
continue;
} else if (waitState == WAITING) {
continue;
} else if (waitState == WAITED_HALF) {
if (!waitedHalf) {
//dump
..........
continue;
}
}
//kill process
.....
我们可以看出watchdog的检测还是很严格的。只要有一个被检测对象出现OVERDUE,或连续两次检测,均有被检测对象处于WAITED_HALF状态,那么watchdog将不执行continue。如果不执行continue,watchdog后续的代码将kill掉system server。
结束语
以上是对android中watchdog服务的分析,我们举例介绍了其它服务如何使用watchdog,以及watchdog判断系统是否出问题的依据。