Android WatchDog正解
转载:http://blog.****.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:
- //forwatchdog
- publicvoidmonitor(){
- synchronized(mLocks){}
- }
同样的道理,对于其他的几个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:
- publicstaticWatchdoggetInstance(){
- if(sWatchdog==null){
- sWatchdog=newWatchdog();
- }
- returnsWatchdog;
- }
- privateWatchdog(){
- super("watchdog");
- mHandler=newHeartbeatHandler();
- }
SystemServer会调用watchdog的start函数,从而让继承于thread的Watchdog的run函数在一新线程被执行:
- publicvoidrun(){
- booleanwaitedHalf=false;
- while(true){
- mCompleted=false;
- mHandler.sendEmptyMessage(MONITOR);//引发HeartbeatHandler在相应线程处理MONITOR消息
- synchronized(this){
- longtimeout=TIME_TO_WAIT;//周期性等待
- longstart=SystemClock.uptimeMillis();
- while(timeout>0&&!mForceKillSystem){
- try{
- wait(timeout);
- }catch(InterruptedExceptione){
- Log.wtf(TAG,e);
- }
- timeout=TIME_TO_WAIT-(SystemClock.uptimeMillis()-start);
- }
- if(mCompleted&&!mForceKillSystem){
- //monitor都返回了,mCompleted为真
- waitedHalf=false;
- continue;
- }
- if(!waitedHalf){
- //monitor没有都返回,再进行第二次等待判断
- waitedHalf=true;
- continue;
- }
- }
- //第2次了,monitor还没都返回,确实hung了
- if(!Debug.isDebuggerConnected()){
- //SystemServer自杀
- Process.killProcess(Process.myPid());
- System.exit(10);
- }else{
- Slog.w(TAG,"Debuggerconnected:Watchdogis*not*killingthesystemprocess");
- }
- waitedHalf=false;
- }
- }
- finalclassHeartbeatHandlerextendsHandler{
- @Override
- publicvoidhandleMessage(Messagemsg){
- switch(msg.what){
- caseMONITOR:{
- //调用每个monitor
- finalintsize=mMonitors.size();
- for(inti=0;i<size;i++){
- mCurrentMonitor=mMonitors.get(i);
- mCurrentMonitor.monitor();
- }
- synchronized(Watchdog.this){
- mCompleted=true;//修改mCompleted为true
- mCurrentMonitor=null;
- }
- }break;
- }
- }
- }
而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的方法学。