1 watchdog分析

Watchdog就是“看门狗”。其最初存在的意义是因为以前嵌入式设备上的程序经常跑飞(电磁干扰之类的),所以专门设置了一个硬件看门狗,每个一段时间,看门狗就去检查一下某个参数是否被设置了,如果发现该参数没有被设置,则判断为系统出错,然后就会强制重启。

在软件层面上,Android对Systemserver的参数是否被设置也很谨慎,所以专门为它增加了一条看门狗,它主要看几个重要service的门。一旦发现service出现问题,就会kill system_server进程,进而导致zygote进程自杀,最后导致java世界重启。

我们先把systemServer使用watchdog的调用流程总结一下,然后以此为切入点来分析watchdog。SS和watchdog的交互流程可以总结如下:

①watchdog.getInstance.Init()

②watchdog.getInstance.start()

③watchdog.getInstance.addMonitor()

1.1 创建和初始化watchdog

GetInstance用于创建watchdog,代码如下:

public static Watchdog getInstance() {

if (sWatchdog == null) {

sWatchdog = new Watchdog(); //使用了单实例模式。

}

return sWatchdog;

}

//构造函数

private Watchdog() {

super("watchdog");

/*为每一个我们想检查的普通线程初始化一个handler检测器。需要注意的是:我们并不马上检查后台线程,因为这样可能会执行更长的操作且无法保证这些操作的耗时是合适的。

我们大家共享的前台线程是主要的检查者,同时我们也会派遣监视器进行检查并做一些其他的工作。

Handler是系统消息处理的地方,会在后面进行详细的分析*/

mMonitorChecker = new HandlerChecker(FgThread.getHandler(),

"foreground thread", DEFAULT_TIMEOUT);

mHandlerCheckers.add(mMonitorChecker);

//为主线程添加一个checker,鉴于可能会是一个UI正运行在主线程中,所以我们只在这里做一个快速的检查

mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),

"main thread", DEFAULT_TIMEOUT));

// 为共享的UI线程添加一个checker

mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),

"ui thread", DEFAULT_TIMEOUT));

// 同时为io线程添加一个checker

mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),

"i/o thread", DEFAULT_TIMEOUT));

}

在看门狗诞生后,再来看看init函数:

public void init(Context context, BatteryService battery,

PowerManagerService power, AlarmManagerService alarm,

ActivityManagerService activity) {

mResolver = context.getContentResolver();

mBattery = battery;

mPower = power;

mAlarm = alarm;

mActivity = activity;

/*注册一个广播接受者,这个接受者只接收ACTION标记为REBOOT的广播,并且这个接受者拥有重启系统的权限*/

context.registerReceiver(new RebootRequestReceiver(),

new IntentFilter(Intent.ACTION_REBOOT),

android.Manifest.permission.REBOOT, null);

}

1.2 watchdog跑起来

Systemserver调用watchdog的start函数,这将导致watchdog的run函数在另一个线程中被执行:

@Override

public void run() {

boolean waitedHalf = false;

while (true) {//外层while循环

final ArrayList<HandlerChecker> blockedCheckers;

final String subject;

final boolean allowRestart;

synchronized (this) {

long timeout = CHECK_INTERVAL; // 检查的间隔时间,=2500

//对每一个checker进行轮询,scheduleCheckLocked会调用handler的postAtFrontOfQueue(Runnable r)函数,这个函数发送的消息会在另一个线程中得到处理,该线程触发 monitor去检查server的状态(是否死锁).

for (int i=0; i<mHandlerCheckers.size(); i++) {

HandlerChecker hc = mHandlerCheckers.get(i);

hc.scheduleCheckLocked();

}

long start = SystemClock.uptimeMillis(); //检查的开始时间

//在CHECK_INTERVAL时间内循环等待检查结果

while (timeout > 0) {

try {

wait(timeout); //等待检查的结果

} catch (InterruptedException e) {

Log.wtf(TAG, e);

}

timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);

}

/* waitState共有四种状态:COMPLETED = 0;WAITING = 1;WAITED_HALF = 2;

OVERDUE = 3; evaluateCheckerCompletionLocked函数的作用是:遍历所有的checkers,返回这些checkers中最大的State值——值越大就说明该checker检查到的状态越严重*/

final int waitState = evaluateCheckerCompletionLocked();

if (waitState == COMPLETED) {

// 表明经过所有检查后,发现一切service正常,本轮检查后,系统不处于waitedHalf状态

waitedHalf = false;

continue;

} else if (waitState == WAITING) {

// 有某些checker还在它们的间隔时间内等待检查结果,那么就直接返回,重新检查,waitedHalf状态值不改变

continue;

} else if (waitState == WAITED_HALF) {//已经有某些个checker处于WAITED_HALF状态了

if (!waitedHalf) { //如果当前总的检查状态并不处于waitedHalf状态,那么就pull一个栈跟踪到ActivityManagerService中,并将waitedHalf状态置为真,然后继续检查;否则直接继续检查

ArrayList<Integer> pids = new ArrayList<Integer>();

pids.add(Process.myPid());

ActivityManagerService.dumpStackTraces(true, pids, null, null,

NATIVE_STACKS_OF_INTEREST);

waitedHalf = true;

}

continue;

}

//说明watchdog已经处于OVERDUE状态了,就可能需要重启了

blockedCheckers = getBlockedCheckersLocked();

subject = describeCheckersLocked(blockedCheckers);

allowRestart = mAllowRestart;

}

/*代码运行到这里就说明watchdog已经处于OVERDUE状态了(即系统极可能已经挂起了~),那么我们首先收集SS进程中所有线程的栈跟踪信息,然后杀掉SS进程,这样就会导致系统重启了~*/

EventLog.writeEvent(EventLogTags.WATCHDOG, subject);

……..

if (controller != null) { //如果第一次检查的时候发现controller可以处理这种情况,那么就不需要重启,重新检查即可

Slog.i(TAG, "Reporting stuck state to activity controller");

try {

Binder.setDumpDisabled("Service dumps disabled due to hung system process.");

// 1 = keep waiting, -1 = kill system

int res = controller.systemNotResponding(subject);

if (res >= 0) {

Slog.i(TAG, "Activity controller requested to coninue to wait");

waitedHalf = false;

continue;

}

} catch (RemoteException e) {

}

}

// 已经检查过两次了,还是有问题,那么说明这次是真的有问题了,所以SS就自杀

if (Debug.isDebuggerConnected()) {

Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process");

}

……..

Process.killProcess(Process.myPid());

System.exit(10);

}

//检查发现没有问题了,就将waitedHalf值置为假

waitedHalf = false;

}

}

总结:watchdog隔一段时间就会向另一个线程的消息队列头发送一条消息,那个线程将检查各个service的状况。而watchdog会等待检查的结果,如果第二次还是没有正确的返回结果,那么就杀掉SS。

1.3 列队检查

这么多service,哪些是watchdog比较关注的呢?一共有3个:

①activityManagerService

②powerManagerService

③windowManagerService

要想支持watchdog的检查,就需要让这些service实现monitor接口,然后watchdog就会调用它们的monitor函数进行检查了。检查的地方是在watchdog::HandlerChecker类的run中:

public void run() {

// mMonitors是所有monitor的集合

final int size = mMonitors.size();

//检查各个service

for (int i = 0 ; i < size ; i++) {

synchronized (Watchdog.this) {

mCurrentMonitor = mMonitors.get(i);

}

mCurrentMonitor.monitor(); //调用该service的monitor函数

}

synchronized (Watchdog.this) {

//如果没有问题,就设置mCompleted为真

mCompleted = true;

mCurrentMonitor = null;

}

}

那么,service是如何判断自己的健康状况的呢?我们以ActivityManagerService为例,先看看它是如何把自己交给watchdog检查的:

①在其构造函数中有:

Watchdog.getInstance().addMonitor(this); //把自己加入到watchdog的检查队列中

②ActivityManagerService自己重载的monitoe函数:

public void monitor() { //执行死锁检查

synchronized (this) { }

}

原来,watchdog最怕系统服务死锁,对于这种情况也只能采取杀系统的方法了~

1.4总结

其实watchdog是周期性地调用需要进行状态检查的服务自己的monitor函数来判断该服务是否死锁。然后根据返回结果来判断系统是否需要重启。

最新文章

  1. [转]Teach Yourself Programming in Ten Years——用十年教会自己编程
  2. 1310. ACM Diagnostics
  3. JavaScript中的arguments,callee,caller
  4. 敏捷软件开发 VS. 传统软件工程
  5. CAD 二次开发--属性块
  6. nice
  7. js获取url传递参数
  8. Inside TSQL Querying - Chapter 2. Physical Query Processing
  9. C#窗体的加载等待(BackgroundWorker控件)实现
  10. UI篇--布局问题
  11. careercup-中等难度 17.4
  12. Java批量处理数据
  13. tq2440+fedora安装qt4.5
  14. Truncated incorrect DOUBLE value错误
  15. 增强学习 | Q-Learning
  16. 转:【Java并发编程】之二十三:并发新特性—信号量Semaphore(含代码)
  17. 好代码是管出来的——浅谈.Net Core的代码管理方法与落地(更新中...)
  18. ARM汇编指令格式
  19. Java 程序员必备的 15 个框架,前 3 个地位无可动摇!
  20. InnoDB存储引擎概览

热门文章

  1. Drupal7新装一个主题时页面白屏,如何设置一个默认主题?
  2. 表格和网页ico图标
  3. Oracle ORA-01722 无效数字
  4. unix环境高级编程一书中部分错误处理函数
  5. 初识Java程序,编写简单代码?
  6. vue.js 图表chart.js使用
  7. 50 道 CSS 基础面试题及答案
  8. Python知识点入门笔记——特色数据类型(元组)
  9. 从0到n-1中随机等概率输出m个不同的数
  10. 初识java,编写hello world语句