前言:知其所以,知其所以然
从整个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);