Android WatchDog正解

转载:http://blog.csdn.net/21cnbao/article/details/7748729

在一个典型的嵌入式系统中,会存在一个硬件看门狗,正常运转的系统会周期喂狗,否则证明系统hang了。硬件看门狗存在相当大的局限性,它只能监控整个系统而不能监控单个的进程或者线程。于是发明软件看门狗意义重大。SystemServer会用到软件看门狗来确保AM 、PM、WM这几个服务hang住之后,退出SystemServer进程,从而进入上回书中的Zygote与SystemServer的生死与共。注意service是hang而不是死,绕到个死胡同里出不来了。镜湖女侠决定了革命,你再怎么去说服她也没有用了。这些重要的service运行于SystemServer的进程空间,在祖国的怀抱里长大,它们热爱祖国热爱人民,只是步调偏了,不按照当局规定的方式“爱国”,当局透过Java里面去synchronized某锁拿思想汇报的时候长时间拿不到。

我们随便读一下PowerManagerService.java的源代码,会发现几乎所有的重要操作都会透过synchronized其中的mLocks进行,咱们抓几个看看:


很显然,如果我们故意去synchronizedmLocks这个锁,如果长时间拿不到,基本可认定PowerManagerService在某个操作里面hang了,短时间或者直接就拿到了,证明一切正常。PowerManagerService里面实现了monitor成员函数来synchronized mLocks:

[java]  view plain copy
  1. // for watchdog  
  2.   
  3. public void monitor() {  
  4.   
  5.     synchronized (mLocks) { }  
  6.   
  7. }  

      同样的道理,对于其他的几个service,我们也可以用类似的方法来验证它们是否hang。同志们啊,很多国内的书或者文档里面把这个侦测hang的过程说成是侦测死锁,含义其实是大错特错了,也把广大的读者搞地不知所云。因为死锁只是引起hang的一种可能,hang的含义则更加广泛。缓刑或者监外执行的罪犯,周期性地会去派出所报告工作,如果没去报告,很可能不是被家里人死锁在房间里打不开门了,更可能是跑路了。

同样,国内很多的教科书以内核cs8900驱动为例讲解网络设备驱动,我也反复在各大公司澄清过cs8900 那就是一反面教材,是一个平台相关信息渗透入与平台无关驱动代码的典型错误范例,需要拨乱反正啊有木有?

了解了侦测service hang的原理,我们需要一套方法来实现这个侦测,这个过程透过watchdog来实现。在Android中,watchdog继承于thread类,代码位于frameworks/base/services/java/com/android/server/Watchdog.java。采用单例模式,提供getInstance() 接口,同时在watchdog创建的过程中,会创建一个用于消息处理的HeartbeatHandler:

[java]  view plain copy
  1. public static Watchdog getInstance() {  
  2.         if (sWatchdog == null) {  
  3.             sWatchdog = new Watchdog();  
  4.         }  
  5.   
  6.         return sWatchdog;  
  7.     }  
  8.   
  9.     private Watchdog() {  
  10.         super("watchdog");  
  11.         mHandler = new HeartbeatHandler();  
  12.     }  

SystemServer会调用watchdog的start函数,从而让继承于thread的Watchdog的run函数在一新线程被执行:

[java]  view plain copy
  1. public void run() {  
  2.         boolean waitedHalf = false;  
  3.         while (true) {  
  4.             mCompleted = false;  
  5.             mHandler.sendEmptyMessage(MONITOR);//引发HeartbeatHandler在相应线程处理MONITOR消息  
  6.   
  7.             synchronized (this) {  
  8.                 long timeout = TIME_TO_WAIT;//周期性等待  
  9.   
  10.                 long start = SystemClock.uptimeMillis();  
  11.                 while (timeout > 0 && !mForceKillSystem) {  
  12.                     try {  
  13.                         wait(timeout);  
  14.                     } catch (InterruptedException e) {  
  15.                         Log.wtf(TAG, e);  
  16.                     }  
  17.                     timeout = TIME_TO_WAIT - (SystemClock.uptimeMillis() - start);  
  18.                 }  
  19.   
  20.                 if (mCompleted && !mForceKillSystem) {  
  21.                     // monitor都返回了,mCompleted为真  
  22.                     waitedHalf = false;  
  23.                     continue;  
  24.                 }  
  25.   
  26.                 if (!waitedHalf) {  
  27.                     // monitor没有都返回,再进行第二次等待判断  
  28.                     waitedHalf = true;  
  29.                     continue;  
  30.                 }  
  31.             }  
  32.   
  33.             // 第2次了,monitor还没都返回,确实hung了  
  34.             if (!Debug.isDebuggerConnected()) {  
  35.                 //SystemServer自杀  
  36.                 Process.killProcess(Process.myPid());  
  37.                 System.exit(10);  
  38.             } else {  
  39.                 Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process");  
  40.             }  
  41.   
  42.             waitedHalf = false;  
  43.         }  
  44.     }  
这个run函数实现比较简单, 周期性地设置mCompleted变量为假,通知心跳handler去调用各个monitor,而心跳handler会调用各个service的monitor,如果各个monitor都返回了,心跳handler会将mCompleted设置为真(这个过程是不是和使用硬件看门狗的情况下把定时器复位惊人的相似?因此,我们可以把这个过程看做“喂狗”)。否则,经过2次等待watchgod的run()发现mCompleted还为假,就证明hang了。我们看看心跳handler调用各个monitor的过程并“喂狗”的过程:

[java]  view plain copy
  1. final class HeartbeatHandler extends Handler {  
  2.         @Override  
  3.         public void handleMessage(Message msg) {  
  4.             switch (msg.what) {  
  5.                 case MONITOR: {  
  6.                     //调用每个monitor  
  7.                     final int size = mMonitors.size();  
  8.                     for (int i = 0 ; i < size ; i++) {  
  9.                         mCurrentMonitor = mMonitors.get(i);  
  10.                         mCurrentMonitor.monitor();  
  11.                     }  
  12.   
  13.                     synchronized (Watchdog.this) {  
  14.                         mCompleted = true//修改mCompleted为true  
  15.                         mCurrentMonitor = null;  
  16.                     }  
  17.                 } break;  
  18.             }  
  19.         }  
  20.     }  

而PowerManagerService等service如何进入上面代码中的for循环呢?那是因为他们正好实现了这个Watchdog.Monitor接口,并透过Watchdog.getInstance().addMonitor(this)加到了monitor列表里面。

结合第一回和第二回,我们基本可以看出整个Android软件自愈过程的“环环相扣”,成为一个多么完美的体系啊!SystemServer监控重要service,重要service hang则SystemServer死,SystemServer死则Zygote监控到,Zygote也死并且杀死整个Java世界,Zygote死则init监控到,init重新启动Zygote,之后SystemServer、service又进入重生过程,与死亡的过程相反。

这个过程我们看到了多进程之间的千丝万缕的联系,以及多进程模型的魅力和设计哲学,程序员喜欢孤立的去看待自己的程序而不是总揽全局,而从体系架构的角度去思考问题,我们更加能清楚我们位于整个软件的哪个组成部分。和宇宙大爆炸比起来,咱屌丝们的房子、车子都不过是过眼云烟,所以我们要尽快把自己的人生理想调整到配合宇宙大爆炸的方向上来。从Android的init、Zygote、SystemServer和重要service的彼此关联中,我们至少体会到了一些什么,整天盯着自己的几个函数玩,不在宇宙里玩,玩地多不过瘾啊?关系上是“宇宙大爆炸->Android-> 程序员”,不是“函数->模块->Android”,这是研究Android的方法学。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值