Android源码剖析之WatchDog前世今生

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

从整个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 threadui线程
i/o threadi/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_HALF30s到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);
    原文作者:木子一秋
    原文地址: https://www.jianshu.com/p/9febfd0e9201
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞