特别声明:本系列文章LiAnLab.org著作权所有,转载请注明出处。by  @宋宝华Barry

最近马不停蹄往返进行Linux技术讲座,所以本回delay了一周。上回书说到Android中生死与共的Zygote和SystemServer,今回书我们讲SystemServer的watchdog,也为软件架构自愈系列的终结篇。

第三只狗:朝廷鹰犬

2012年7月,网名为“吴法天”、人称中国十大“五毛”的中国政法大学副教授吴丹红,和网名为“此是燕云”的四川电视台记者周燕,“微博约架”北京朝阳公园南门。众多网友前去围观,一场鸡飞狗跳的闹剧就此上演。吴法天被打,天朝为之轰动。笔者观此视频,对于吴被打,稍有振动,而最震慑心灵的是,视频中,吴被众人齐呼“走狗”,却是有相当的讽刺意味。“走狗”,基本上是与“朝廷鹰犬”同等的概念,可笑的是,替现政府说话的人,目前成为“走狗”,换回70年前,又是怎样的观景?当年彼是绿林,今日彼为走狗,想起小时候看过的电影《金镖黄天霸》,黄天霸曾经是伟大的无产阶级领袖,绿林好汉的三统领,后投靠清廷,于是成为“鹰犬”,成功升级为与明朝东厂同等的地位。政治是个卑鄙的把戏,历史上屡次的成功者,基本上都是最卑鄙者。

“走狗”、“鹰犬”的特点是,对主人卑躬屈膝,对民间进行秘密监控、暴力镇压、实施酷刑等,搞到人人自危。在Android的世界里,这样的“走狗”也必然存在。在SystemServer启动一些service后,它需要不断监视一些重要service的状况,一旦发现某个service有异动,思想上与统治者长期不一致,势必要采取行动。在SystemService的众多service中,重点监控对象包括ActivityManagerService 、PowerManagerService、WindowManagerService等,这个监控透过Android的watchdog实现 。

在一个典型的嵌入式系统中,会存在一个硬件看门狗,正常运转的系统会周期喂狗,否则证明系统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:

    // for watchdogpublic void monitor() {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:

public static Watchdog getInstance() {if (sWatchdog == null) {sWatchdog = new Watchdog();}return sWatchdog;}private Watchdog() {super("watchdog");mHandler = new HeartbeatHandler();}

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

public void run() {boolean waitedHalf = false;while (true) {mCompleted = false;mHandler.sendEmptyMessage(MONITOR);//引发HeartbeatHandler在相应线程处理MONITOR消息synchronized (this) {long timeout = TIME_TO_WAIT;//周期性等待long start = SystemClock.uptimeMillis();while (timeout > 0 && !mForceKillSystem) {try {wait(timeout);} catch (InterruptedException e) {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, "Debugger connected: Watchdog is *not* killing the system process");}waitedHalf = false;}}

这个run函数实现比较简单, 周期性地设置mCompleted变量为假,通知心跳handler去调用各个monitor,而心跳handler会调用各个service的monitor,如果各个monitor都返回了,心跳handler会将mCompleted设置为真(这个过程是不是和使用硬件看门狗的情况下把定时器复位惊人的相似?因此,我们可以把这个过程看做“喂狗”)。否则,经过2次等待watchgod的run()发现mCompleted还为假,就证明hang了。我们看看心跳handler调用各个monitor的过程并“喂狗”的过程:

final class HeartbeatHandler extends Handler {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MONITOR: {//调用每个monitorfinal int size = mMonitors.size();for (int i = 0 ; i < size ; i++) {mCurrentMonitor = mMonitors.get(i);mCurrentMonitor.monitor();}synchronized (Watchdog.this) {mCompleted = true; //修改mCompleted为truemCurrentMonitor = 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的方法学。谁可以帮我p一副图?把下面这个宇宙大爆炸图中p出个Android,旁边再p出个苦B程序员呢?

在朝廷鹰犬的监控之下,无法与中央保持一致的service革命者就这样被watchdog给揪了出来。鹰犬问道,你只要放弃你的信念,保持喂狗,就可以活。而他(她)毅然选择了慷慨负死。威廉·华莱士道:“跟着我,你们将失去生命,但是,你们将获得自由” 。

谨以本回,献给中国历史上前赴后继的伟大的为天下人谋求永福的人们。


汝体吾此心,

于啼泣之余,

亦以天下人为念,

当亦乐牺牲吾身与汝身之福利,

为天下人谋永福也。

汝其勿悲!


——宋宝华

Android架构纵横谈之——软件自愈能力 (3)相关推荐

  1. Android架构纵横谈之——软件自愈能力 (1)

    本系列2012年的时候发表在我的blog上面,现搬到公众号 笔者决定,从今天开始,连载Android架构纵横谈系列.之所以叫纵横谈而不是叫别的题目,是因为整个系列是横着竖着乱弹琴,可以说是阴阳不分,黑 ...

  2. Android架构纵横谈之二—基于性能的考虑(1)

    By LiAnLab.org / 宋宝华 <Android架构纵横谈之一--软件自愈能力>已经谈地告了一个段落.接下来这个系列二我们谈Android性能方面的考虑.Android系 统组件 ...

  3. Android架构纵横谈之二——基于性能的考虑(1)

    特别声明:本系列文章LiAnLab.org著作权所有,转载请注明出处.by  @宋宝华Barry <Android架构纵横谈之一--软件自愈能力>已经谈地告了一个段落.接下来这个系列二我们 ...

  4. Android架构 系统如何保证运行无误

    Android架构纵横谈之--软件自愈能力 来自:http://www.uml.org.cn/mobiledev/201209255.asp 笔者决定,从今天开始,连载Android架构纵横谈系列.之 ...

  5. 思考ANDROID架构(4):HOW-TO, 如何从API洞悉软件的话语权

    思考ANDROID架构(4):HOW-TO, 如何从API洞悉软件的话语权 前言:许多人会认为,提供接口(如API),其目的是要去服务别人(如App).然而,这只是一个视角而已,还有许多视角来看待AP ...

  6. Android架构:认识简法设计与EIT软件造形(序)

    Android架构:认识简法设计与EIT软件造形 内容 Ⅰ 简单法则(减法设计) Ⅱ 减法设计是战略,造形是会赢的战术 Ⅲ EIT软件造形是什么? Ⅳ EIT造形的用途 Ⅴ EIT造形的特性 Ⅵ EI ...

  7. Android架构师亲述:我从某度外包到字节,你知道我经历了什么吗?

    Android架构师亲述:我从某度外包到字节,你知道我经历了什么吗? 朋友在某大型互联网公司担任架构师.刚毕业那年,他以外包的身份进入了某度,那个时候几乎每一天都很焦虑,大家技术能力没差太多,为何我是 ...

  8. 如何避免贫穷和忙碌,在2018年你需要这样提升自己 2018年01月07日 00:00:00 2099 热文导读 | 点击标题阅读 Java和Android架构2017年总结:文章精选 吊炸天!74

    如何避免贫穷和忙碌,在2018年你需要这样提升自己 2018年01月07日 00:00:00 2099 热文导读 | 点击标题阅读 Java和Android架构2017年总结:文章精选 吊炸天!74款 ...

  9. 王家林最受欢迎的一站式云计算大数据和移动互联网解决方案课程 V1之Android架构设计和实现完整训练:HALFrameworkNative ServiceAndroid ServiceBes

    如何理解Android架构设计的初心并开发出搭载Android系统并且具备深度定制和软硬整合能力特色产品,是本课程解决的问题. 课程以Android的五大核心:HAL.Binder.NativeSer ...

最新文章

  1. 时隔3年,华为又有人入选IEEE Fellow
  2. LeetCode Ugly Number
  3. 20172307 结对编程项目-四则运算 第二周 阶段总结
  4. linux之在当前目录下删除不包含aa的文件
  5. svn之bash: syntax error near unexpected token `(‘ 解决办法
  6. P2467-[SDOI2010]地精部落【dp】
  7. 解决SQLServer占用80端口问题
  8. 华为m6升级鸿蒙,华为鸿蒙系统再传喜讯!14款华为旧旗舰喜提新系统:大幅度换血...
  9. 蚂蚁森林用户须知_蚂蚁森林刷能量漏洞(轻松读懂规则)
  10. 【代码笔记】多线程游戏开发——伏魔记:第一步——开始游戏界面实现(一)...
  11. 百度竞价推广地域是如何选择的?
  12. 塔防游戏c语言源代码,转经典塔防游戏TowersTrap-[lua复刻版本,附全部lua源代码]
  13. CSS中的四种定位以及top和margin-top的区别
  14. 数据库学生表,课程表,选课表
  15. 【java学习】集合框架
  16. 【Linux】RHCE -- RHCSA 认证考试 模拟练习题解析
  17. 干货|Stakeholder利益相关者讲解
  18. Openstack之Keystone组件解析
  19. 【恋恋不忘你xp主题】_8.4
  20. redis设置过期时间与直接detele key有什么区别

热门文章

  1. 阅读分享:Utilizing BERT Intermediate Layers for ABSA and NLI
  2. CSDN中空格的常用输入方法
  3. python计算多项式的方法
  4. CVPR 2019 论文汇总(按方向划分,0409 更新中)[转载]
  5. 【Linux】Linux根文件系统扩容
  6. 背诵, 背诵, 背诵, 我就是死也要带走
  7. UGUI背包实现详解之一UGUI精准拖拽
  8. Python磁力获取器命令行工具 torrent-cli
  9. 戴尔,更换固态硬盘后no bootable devices found
  10. 微信的这几个实用功能,你知道吗?