美文网首页
Android源码剖析之WatchDog前世今生

Android源码剖析之WatchDog前世今生

作者: 码上就说 | 来源:发表于2018-08-28 18:05 被阅读259次

    前言:知其所以,知其所以然

    从整个Android系统来讲,发生的问题主要有3种:

    本文介绍的主要内容:

    • 什么是WatchDog
    • WatchDog启动流程
    • WatchDog状态介绍
    • WatchDog工作流程

    三、WatchDog问题

    看门狗,熟悉Linux的人应该知道Linux也是有看门狗的,而且还分硬件看门狗和软件看门狗,Android底层基于Linux kernel,设计的思想脱胎于Linux,因而Android也是一套自己的看门狗系统。
    本文只谈软件看门狗,Android中的watchdog功能,用于监视系统的运行,watchdog 定时器,会监测特定进程的运行情况,如果监测的进程在一定时间内没有响应,那么watchdog会输出一定的信息表明当前系统所处的状态,严重的情况下会导致watchdog重启。这种机制保证Android系统正常稳定运行。

    3.1 WatchDog启动

    既然是看门狗,那么肯定是系统启动的时候就启动了,Android系统中的第一个进程是zygote进程,zygote进程启动system_server进程,在system_server进程中,启动了Android系统中用到的一系列服务。
    WatchDog的启动是在SystemServer.java中的startOtherServices()函数中。
    具体的启动代码如下:

    final Watchdog watchdog = Watchdog.getInstance();
    watchdog.init(context, mActivityManagerService);
    Watchdog.getInstance().start();
    

    3行代码,执行的意思是:

    • 实例化WatchDog
    • 初始化WatchDog
    • 启动WatchDog

    3.2 WatchDog类

    3.2.1 介绍WatchDog类

    我们首先看一下WatchDog类是一个什么样的类:

    public class Watchdog extends Thread {
    }
    

    这是一个线程,说明WatchDog的核心执行过程必定在run()方法中,其实为什么要放在线程中,我觉得有下面的原因:

    • 系统启动,必定不能在system_server的当前线程中监测特定进程,一定要新开一个常驻的线程,只要system_server在,那么这个线程就会一直监测需要监测的进程。

    WatchDog监测了那些进程和那些硬件抽象层:
    当然我们可以修改本地代码,加入自己想监测的一些进程和硬件抽象层

    public static final String[] NATIVE_STACKS_OF_INTEREST = new String[] {
            "/system/bin/audioserver",
            "/system/bin/cameraserver",
            "/system/bin/drmserver",
            "/system/bin/mediadrmserver",
            "/system/bin/mediaserver",
            "/system/bin/sdcard",
            "/system/bin/surfaceflinger",
            "media.extractor", // system/bin/mediaextractor
            "media.metrics", // system/bin/mediametrics
            "media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service
            "com.android.bluetooth",  // Bluetooth service
            "statsd",  // Stats daemon
        };
    
        public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList(
            "android.hardware.audio@2.0::IDevicesFactory",
            "android.hardware.audio@4.0::IDevicesFactory",
            "android.hardware.bluetooth@1.0::IBluetoothHci",
            "android.hardware.camera.provider@2.4::ICameraProvider",
            "android.hardware.graphics.composer@2.1::IComposer",
            "android.hardware.media.omx@1.0::IOmx",
            "android.hardware.media.omx@1.0::IOmxStore",
            "android.hardware.sensors@1.0::ISensors",
            "android.hardware.vr@1.0::IVr"
        );
    

    从WatchDog的启动来看,WatchDog开始经历过三步,我们分析这个类,就要从这3步着手。

    3.2.2 实例化WatchDog

    WatchDog的构造函数中做了什么事情,我们从代码的角度分析一下:

    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());
    
            mOpenFdMonitor = OpenFdMonitor.create();
            assert DB ||
                    DEFAULT_TIMEOUT > ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;
        }
    
    • HandlerChecker
      用于检查句柄线程的状态和调度监视器回调。
      这里监测的线程主要有:
    监测的线程 备注
    foreground thread 前台进程
    main thread 主线程
    ui thread ui线程
    i/o thread i/o线程
    display thread 线程线程

    这些线程一旦出现了问题,都会极大的影响用户的使用体验,所以必须对这些必要的线程进程监测,出现问题的时候有必要的措施和反馈来处理。

    public final class HandlerChecker implements Runnable {
        HandlerChecker(Handler handler, String name, long waitMaxMillis) {
                mHandler = handler;
                mName = name;
                mWaitMax = waitMaxMillis;
                mCompleted = true;
            }
    }
    

    可知HandlerChecker实现了Runnable,那么其核心操作也在run()方法中。其中HandlerChecker的构造函数中传入了3个参数:

    参数 含义
    handler 当前类中的handler,传入的handler Looper不同
    name 线程名
    waitMaxMillis 等待最大的时间
    mMonitorChecker = new HandlerChecker(FgThread.getHandler(),
                    "foreground thread", DEFAULT_TIMEOUT);
    

    这个执行语句表明当前传入的前台线程,默认的超时时间是60s

    static final long DEFAULT_TIMEOUT = DB ? 10*1000 : 60*1000;
    
    • mHandlerCheckers
      HandlerChecker的列表,当前定义了5个HandlerChecker放入列表中。
    final ArrayList<HandlerChecker> mHandlerCheckers = new ArrayList<>();
    
    • addMonitor(new BinderThreadMonitor());
      初始化Binder线程的监视器。
    private static final class BinderThreadMonitor implements Watchdog.Monitor {
            @Override
            public void monitor() {
                Binder.blockUntilThreadAvailable();
            }
        }
    

    监视以检查绑定程序线程的可用性。监视器将阻塞,直到有一个可用于处理即将到来的IPC通信的binder线程,以确保其他进程仍然可以与服务进行通信。

    • mOpenFdMonitor = OpenFdMonitor.create();
      获取一个OpenFdMonitor实例,以便在发生WatchDog时可以往本地文件中写入相关的信息。
    3.2.3 初始化WatchDog
    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);
        }
    
    • 获取当前context的contentResolver对象。
    • 注册一个广播
      广播接收中会直接重启系统。接下来需要关注在什么情况下发送这个广播。当这个Intent.ACTION_REBOOT = "android.intent.action.REBOOT"的时候,就会发生重启系统,这个也要根据各个定制的rom对watchdog的容忍程度。
    final class RebootRequestReceiver extends BroadcastReceiver {
            @Override
            public void onReceive(Context c, Intent intent) {
                if (intent.getIntExtra("nowait", 0) != 0) {
                    rebootSystem("Received ACTION_REBOOT broadcast");
                    return;
                }
                Slog.w(TAG, "Unsupported ACTION_REBOOT broadcast: " + intent);
            }
        }
    
    3.2.4 启动WatchDog

    启动watchdog,进入run()方法。run()方法中执行了一个无线循环,无线循环中用上面定义的HandlerChecker来监测一下当前的指定线程的运行状态。
    当前线程的运行状态在watchdog中被定义为4种:

    watchdog线程状态 状态的定义
    COMPLETED 完成的状态,圆满
    WAITING 当前watchdog定义的超时时间是60s,一般规定0到30s之间称为等待状态
    WAITED_HALF 30s到60s之间称为等待一半状态
    OVERDUE 超过60s就完成超时了,这时候触发watchdog

    下面描述一下watchdog线程中做的事情:

    • 启动HandlerChecker开始监测foreground thread、main thread、ui thread、i/o thread、display thread 5个线程的运行状态。
    • 在此状态下不断地轮训check当前的运行状态,check一下timeout,就是当前watchdog的timeout
    • 本地的fd trigger如果触发了重启了,下面不继续了,因为已经重启了。
    • 本地的fd trigger没有触发重启,这时候就要监测监控线程的状态的,check一下他们处于watchdog的哪一种状态。
    final int waitState = evaluateCheckerCompletionLocked();
                        if (waitState == COMPLETED) {
                            // The monitors have returned; reset
                            waitedHalf = false;
                            continue;
                        } else if (waitState == WAITING) {
                            // still waiting but within their configured intervals; back off and recheck
                            continue;
                        } 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());
                                ActivityManagerService.dumpStackTraces(true, pids, null, null,
                                    getInterestingNativePids());
                                waitedHalf = true;
                            }
                            continue;
                        }
                    // something is overdue!
                        blockedCheckers = getBlockedCheckersLocked();
                        subject = describeCheckersLocked(blockedCheckers);
    

    可以发现,当处于WAITED_HALF状态时,我们需要做:

    ActivityManagerService.dumpStackTraces(true, pids, null, null,
                                    getInterestingNativePids());
    

    将当前进程和本地定义的native进程的栈dump出来。

    当处于OVERDUE状态时,我们需要做:
    将超时的线程导出来,后面还需要将blockedCheckers中的信息导出来。

    blockedCheckers = 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;
        }
    

    接下来打印出blockedCheckers的堆栈,同时杀死当前的进程。

                    WatchdogDiagnostics.diagnoseCheckers(blockedCheckers);
                    Slog.w(TAG, "*** GOODBYE!");
                    Process.killProcess(Process.myPid());
                    System.exit(10);
    

    相关文章

      网友评论

          本文标题:Android源码剖析之WatchDog前世今生

          本文链接:https://www.haomeiwen.com/subject/meqaiftx.html