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,我们就介绍到这里。另外,它还能检查内存的使用情况,这一部分内容读者可以自行研究。