Android6.0 watchdog

背景
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判断系统是否出问题的依据。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值