4.5.3 Watchdog分析

本章我们没有对SystemServer做更进一步的分析,不过作为拓展内容,这里想介绍一下Watchdog。Watch Dog的中文意思是“看门狗”。我依稀记得其最初存在的意义是因为早期嵌入式设备上的程序经常“跑飞”(比如说电磁干扰等),所以专门设置了一个硬件看门狗,每隔一段时间,看门狗就去检查一下某个参数是不是被设置了,如果发现该参数没有被设置,则判断为系统出错,然后就会强制重启。

在软件层面上,Android对SystemServer的参数是否被设置也很谨慎,所以专门为它增加了一条看门狗,可它看的是哪个门呢?就是看几个重要Service的门,一旦发现Service出了问题,就会杀掉system_server,而这也会使zygote随其一起自杀,最后导致重启Java世界。

我们先把SystemServe使用Watchdog的调用流程总结一下,然后以此为切入点来分析Watchdog。SS和Watchdog的交互流程可以总结为以下三个步骤:

Watchdog.getInstance().init()

Watchdog.getInstance().start()

Watchdog.getInstance().addMonitor()

这三个步骤都非常简单。先看第一步。

1.创建和初始化Watchdog

getInstance用于创建Watchdog,一起来看看,代码如下所示:


[—>Watchdog.java]

public static Watchdog getInstance(){

if(sWatchdog==null){

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

}

return sWatchdog;

}

public class Watchdog extends Thread

//Watchdog从线程类派生,所以它会在一个单独的线程中执行。

private Watchdog(){

super("watchdog");

//构造一个Handler,Handler的详细分析见第5章,读者可以简单地把它看作是消息处理的地方。

//它在handleMessage函数中处理消息。

mHandler=new HeartbeatHandler();

//GlobalPssCollected和内存信息有关。

mGlobalPssCollected=new GlobalPssCollected();

}


这条看门狗诞生后,再来看看init函数,代码如下所示:


[—>Watchdog.java]

public void init(Context context,BatteryService battery,

PowerManagerService power,AlarmManagerService alarm,

ActivityManagerService activity){

mResolver=context.getContentResolver();

mBattery=battery;

mPower=power;mAlarm=alarm;

mActivity=activity;

……

mBootTime=System.currentTimeMillis();//得到当前时间

……

}


至此,看门狗诞生的知识就介绍完了,下面我们就让它动起来。

2.看门狗跑起来

SystemServer调用Watchdog的start函数,这将导致Watchdog的run在另外一个线程中被执行。代码如下所示:


[—>Watchdog.java]

public void run(){

boolean waitedHalf=false;

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

mCompleted=false;//false表明各个服务的检查还没完成。

/*

mHandler的消息处理是在另外一个线程上,这里将给那个线程的消息队列发条消息,请求Watchdog检查Service是否工作正常。

*/

mHandler.sendEmptyMessage(MONITOR);

synchronized(this){

long timeout=TIME_TO_WAIT;

long start=SystemClock.uptimeMillis();

//注意这个小while循环的条件,mForceKillSystem为true也会导致退出循环。

while(timeout>0&&!mForceKillSystem){

try{

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

}catch(InterruptedException e){

}

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

}

//mCompleted为true,表示service一切正常。

if(mCompleted&&!mForceKillSystem){

waitedHalf=false;

continue;

}

//如果mCompleted不为true,看门狗会尽责地再检查一次。

if(!waitedHalf){

……

waitedHalf=true;

continue;//再检查一次

}

}

//已经检查过两次了,还是有问题,这回是真有问题了,所以SS需要把自己干掉。

if(!Debug.isDebuggerConnected()){

Process.killProcess(Process.myPid());

System.exit(10);//干掉自己

}

……

waitedHalf=false;

}

}


这个run函数看起来还是比较简单,就是:

隔一段时间给另外一个线程发送一条MONITOR消息,那个线程将检查各个Service的健康情况。而看门狗会等待检查结果,如果第二次还没有返回结果,那么它会杀掉SS。

下面来看看检查线程究竟是怎么检查Service的。

3.列队检查

这么多Service,哪些是看门狗比较关注的呢?一共有三个Service是需要交给Watchdog检查的:


ActivityManagerService

PowerManagerService

WindowManagerService


要想支持看门狗的检查,就需要让这些Service实现monitor接口,然后Watchdog就会调用它们的monitor函数进行检查了。检查的地方是在HeartbeatHandler类的handleMessage中,代码如下所示:


[—>Watchdog.java:HeartbeatHandler]

final class HeartbeatHandler extends Handler{

@Override

public void handleMessage(Message msg){

switch(msg.what){

……

case MONITOR:{

……

long now=SystemClock.uptimeMillis();

final int size=mMonitors.size();

//检查各个服务,并设置当前检查的对象为mCurrentMonitor。

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

mCurrentMonitor=mMonitors.get(i);

mCurrentMonitor.monitor();//检查这个对象。

}

//如果没问题,则设置mCompleted为真。

synchronized(Watchdog.this){

mCompleted=true;

mCurrentMonitor=null;

}

}break;

}

}

}


那么,Service的健康情况是怎么判断的呢?我们以PowerManagerService为例,先看看它是怎么把自己交给看门狗检查的,代码如下所示:


[—>PowerManagerService.java]

PowerManagerService()

{

……

//在构造函数中把自己加入Watchdog的检查队列。

Watchdog.getInstance().addMonitor(this);

}


而Watchdog调用各个monitor函数到底又检查了些什么呢?再看看它实现的monitor函数吧。


[—>PowerManagerService.java]

public void monitor(){

//原来monitor检查的就是这些Service是不是发生死锁了!

synchronized(mLocks){}

}


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

说明 这种情况,我只碰到过一次,原因是有一个函数占着锁,但长时间没有返回。没返回的原因是这个函数需要和硬件交互,而硬件又没有及时返回。

关于Watchdog,我们就介绍到这里。另外,它还能检查内存的使用情况,这一部分内容读者可以自行研究。