android进阶之Watchdog检查系统异常机制

Watchdog简介

Watchdog是Android系统提供的以后总检查系统异常的机制,正如其名,“看门狗”,它看的是Android框架层的几个核心服务。Watchdog一旦发现的AMS、WMS和PMS等核心服务的主线程阻塞,就清除systemserver进程,我们知道,一单SystemServer进程停止,Zygote进程就会自杀,系统就会重启。

Watchdog继承Thread,它是一个线程类,它的监控任务运行在独立的线程中,其中有两个非常重要的ArrayList变量,分别是mMonitors和mHandlerCheckers。变量mMonitors存放的是对象Monitor的子类,如AMS、PMS等。对于这类的监控主要是判断它们是否死锁。而对于变量mHandlerCheckers是ArrayList集合,里面存放的是HandlerChecker的对象,Watchdog主要是监控它里面重要的线程的handler是否阻塞,即监控重要线程的消息队列是否阻塞。mMonitors是一个HandlerChecker类型的对象。实际上,HandlerChecker类是Watchdog的核心,它负责对各个监控对象进行监控。具体的对应关系如下图所示:

Watchdog的启动

Watchdog在SystemServer系统服务进程中被初始化和启动,接下来看看SystemServer怎么把它初始化和启动起来:

public final class SystemServer {
    .....
    public static void main(String[] args) {
        new SystemServer().run();
    }
    .....
    private void run() {
        startBootstrapServices();
        startCoreServices();
        startOtherServices();
        .....
    }
    private void startOtherServices() {
        final Context context = mSystemContext;
        ........
        final Watchdog watchdog = Watchdog.getInstance();
        watchdog.init(context, mActivityManagerService);
        .....
        Watchdog.getInstance().start();
        .....
    }
    ......
}

当SystemServer进程启动之后,就会进入它的main()方法,而它的main()方法有调用了startOtherService()方法,这个方法就把Watchdog启动起来了。startOtherService()方法首先通过Watchdog的getInstance()方法获得Watchdog对象,使用的是单例模式。接着调用init()方法来做进一步操作,最后调用Watchdog的start()方法启动线程进行监控。

public class Watchdog extends Thread {
    static final boolean DB = false;
    //调试用,默认为false
    static final long DEFAULT_TIMEOUT = DB ? 10*1000 : 60*1000;
    static final long CHECK_INTERVAL = DEFAULT_TIMEOUT / 2;
    //这两个变量非常重要,由于DB为false,所以DEFAULT_TIMEOUT为60秒,
    //主要线程blocked了60秒,watchdog就会去kill进程systemServer。
    //CHECK_INTERVAL为30秒,意思是每隔30秒去check一次
}
    private Watchdog() {
        super("watchdog");
        //监听foreground thread,NetworkManagerService、NativeDaemonConnector、
        //MountService属于foreground thread
        mMonitorChecker = new HandlerChecker(FgThread.getHandler(),
                "foreground thread", DEFAULT_TIMEOUT);
        mHandlerCheckers.add(mMonitorChecker);
        //监听SystemServer主线程
        mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),
                "main thread", DEFAULT_TIMEOUT));
        //监听UI线程,包括ActivityManagerService、WindowManagerService、PointerEventDispatcher和DisplayManagerService
        mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),
                "ui thread", DEFAULT_TIMEOUT));
        //监听IO线程,包括BluetoothManagerService、MountService、PacManager等
        mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),
                "i/o thread", DEFAULT_TIMEOUT));
        //监听display thread线程,包括DIsplayManagerService、InputManagerService、WindowManagerService
        mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),
                "display thread", DEFAULT_TIMEOUT));

        //monitor可用的binder线程
        addMonitor(new BinderThreadMonitor());
    }

接下来看看Watchdog的init()方法:

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

        context.registerReceiver(new RebootRequestReceiver(),
                new IntentFilter(Intent.ACTION_REBOOT),
                android.Manifest.permission.REBOOT, null);
    }

init()方法的实现比较简单,主要是给mActivity对象赋值,mActivity是一个全局的AMS对象,init()方法中会注册重启广播接收器RebotRequestReceiver,用来负责接收系统内部发出的系统重启请求。

Watchdog的监听

Watchdog继承Thread,所以调用start()方法之后,就会进入Watchdog的run()方法,它来做监控工作。

Watchdog主要提供了addMonitor()方法来添加监控服务对象,而在添加这些服务对象到Watchdog监控之前,这些服务必须要实现Watchdog.Monitor接口。比如AMS就首先实现了Watchdog.Monitor接口,然后在它的构造方法里把自己添加到Watchdog中,让Watchdog检测自己是否死锁,代码如下:

public ActivityManagerService(Context systemContext) {
    .......
    Watchdog.getInstance().addMonitor(this);
    Watchdog.getInstance().addThread(mHandler);
}

通过addMonitor()方法和addThread()方法,分别把AMS和mHandler添加到Watchdog中。mHandler是一个AMS中的Handler对象,意思是Watchdog不仅要监控AMS是否死锁,还要监控mHandler分发消息的时候是否阻塞。接下来看看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.addMonitor(monitor);
        }
    }

AMS实现了Watchdog.Monitor接口,所以这个monitor就是AMS对象。mMontorChecker是一个HandlerChecker对象,HandlerChecker类实现了java的Runnable类:

    public final class HandlerChecker implements Runnable {
        .......
        private final ArrayList mMonitors = new ArrayList();
        ......
        public void addMonitor(Monitor monitor) {
            mMonitors.add(monitor);
        }
    }

mMontors是一个Monitor类型的ArrayList,系统启动完成之后,系统服务都会添加到这个mMonitors中。AMS中还调用了Watchdog的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();
            mHandlerCheckers.add(new HandlerChecker(thread, name, timeoutMillis));
        }
    }

mHandlerCheckers是一个HandlerChecker的ArrayList:

    final ArrayList mHandlerCheckers = new ArrayList<>();

通过mHandlerCheckers的add()方法把Handler添加进去,从而监控那些重要的线程里的Handler是否阻塞。

当调用Watchdog.getInstance().start()时,则进入线程“watchdog”的run()方法, 该方法分成两部分:
- 前半段用于监测是否触发超时
- 后半段当触发超时时输出各种信息,最后杀死SystemServer进程

接下来进入Watchdog的run()方法:

public void run() {
    boolean waitedHalf = false;
    while (true) {
        final ArrayList blockedCheckers;
        final String subject;
        final boolean allowRestart;
        int debuggerWasConnected = 0;
        synchronized (this) {
            long timeout = CHECK_INTERVAL; //CHECK_INTERVAL=30s
            for (int i=0; i//执行所有的Checker的监控方法, 每个Checker记录当前的mStartTime
                hc.scheduleCheckLocked();
            }

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

            long start = SystemClock.uptimeMillis();
            //通过循环,保证执行30s才会继续往下执行
            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);
            }

            //评估Checker状态
            final int waitState = evaluateCheckerCompletionLocked();
            if (waitState == COMPLETED) {
                waitedHalf = false;
                continue;
            } else if (waitState == WAITING) {
                continue;
            } else if (waitState == WAITED_HALF) {
                if (!waitedHalf) {
                    //首次进入等待时间过半的状态
                    ArrayList pids = new ArrayList();
                    pids.add(Process.myPid());
                    //输出system_server和3个native进程的traces
                    ActivityManagerService.dumpStackTraces(true, pids, null, null,
                            NATIVE_STACKS_OF_INTEREST);
                    waitedHalf = true;
                }
                continue;
            }
            ... //进入这里,意味着Watchdog已超时
        }
        ...
    }
}

该方法主要功能:

  1. 执行所有的Checker的监控方法scheduleCheckLocked()
    • 当mMonitor个数为0(除了android.fg线程之外都为0)且处于poll状态,则设置mCompleted = true;
    • 当上次check还没有完成, 则直接返回.
  2. 等待30s后, 再调用evaluateCheckerCompletionLocked来评估Checker状态;
  3. 根据waitState状态来执行不同的操作:
    • 当COMPLETED或WAITING,则相安无事;
    • 当WAITED_HALF(超过30s)且为首次, 则输出system_server和3个Native进程的traces;
    • 当OVERDUE, 则输出更多信息.

接下来看看scheduleCheckLocked()方法:

public final class HandlerChecker implements Runnable {
    ...
    public void scheduleCheckLocked() {
        if (mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling()) {
            mCompleted = true; //当目标looper正在轮询状态则返回。
            return;
        }

        if (!mCompleted) {
            return; //有一个check正在处理中,则无需重复发送
        }
        mCompleted = false;

        mCurrentMonitor = null;
        // 记录当下的时间
        mStartTime = SystemClock.uptimeMillis();
        //发送消息,插入消息队列最开头, 见下方的run()方法
        mHandler.postAtFrontOfQueue(this);
    }

    public void run() {
        final int size = mMonitors.size();
        for (int i = 0 ; i < size ; i++) {
            synchronized (Watchdog.this) {
                mCurrentMonitor = mMonitors.get(i);
            }
            //回调具体服务的monitor方法
            mCurrentMonitor.monitor();
        }

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

该方法主要功能: 向Watchdog的监控线程的Looper池的最头部执行该HandlerChecker.run()方法, 在该方法中调用monitor(),执行完成后会设置mCompleted = true. 那么当handler消息池当前的消息, 导致迟迟没有机会执行monitor()方法, 则会触发watchdog

其中postAtFrontOfQueue(this),该方法输入参数为Runnable对象,根据消息机制, 最终会回调HandlerChecker中的run方法,该方法会循环遍历所有的Monitor接口,具体的服务实现该接口的monitor()方法。而monitor()方法只是简单获取锁,如AMS的monitor方法:

    public void monitor() {
        synchronized (this) { }
    }

因此,AMS判断是否死锁就是去看能不能拿到自己的锁

run()方法后面的实现则是当触发超时时输出各种信息,最后杀死SystemServer进程,此处就不继续贴出源码了。。。。

Watchdog总结

Watchdog是一个运行在system_server进程的名为”watchdog”的线程::

  • Watchdog运作过程,当阻塞时间超过1分钟则触发一次watchdog,会杀死system_server,触发上层重启;
  • mHandlerCheckers:记录所有的HandlerChecker对象的列表,包括foreground, main, ui, i/o, display线程的handler;
  • mHandlerChecker.mMonitors:记录所有Watchdog目前正在监控Monitor,所有的这些monitors都运行在foreground线程。
  • 有两种方式加入Watchdog的监控:
    • addThread():用于监测Handler对象,默认超时时长为60s.这种超时往往是所对应的handler线程消息处理得慢;
    • addMonitor(): 用于监控实现了Watchdog.Monitor接口的服务.这种超时可能是”android.fg”线程消息处理得慢,也可能是monitor迟迟拿不到锁;

你可能感兴趣的:(android进阶)