Android架构纵横谈之——软件自愈能力(转载)Shadow

Android架构纵横谈之——软件自愈能力

笔者决定,从今天开始,连载Android架构纵横谈系列。之所以叫纵横谈而不是叫别的题目,是因为整个系列是横着竖着乱弹琴,可以说是阴阳不分,黑白颠倒,望湘园里望湘园。我不谈任何一个小的点,比如启动过程、某个HAL移植、一个具体的nativeservice或者Javaservice,我要谈的是横穿在其中的设计思想,因此,我谈的任何一个方面,都有可能涉及到Android从内核到应用的所有层面。

欢迎广大读者通过本博客交流,也可透过我的新浪微博“@宋宝华Barry”讨论,亲,记得给好评喽!这个纵横谈既然决定乱弹,我们就不要那么严肃了,人民邮电出版社的老黄让我在《Linux设备驱动开发详解》里一直严肃着,让我装深沉,我郁闷啊,我独立的人格不为人们所理解啊。这个系列没人管,我们放开了整。《Linux设备驱动开发详解》曾经是年度10大畅销书啊,这个系列怎么也是年度10大瞎扯吧?

今天我们要整的是Android软件架构的超强自愈能力。自愈说白了,就是不小心被人k了,不进医院,自己躺了几天,并且辅助心灵阿q精神胜利疗法,就又活蹦乱跳了。作为一个屌丝,咱们也只能这样了。咱们像小强一样活着,不断自愈。Android估计也是个资深屌丝啊,它可以说处处都自愈。你想啊,一个Android,啥都要整啊,里面多少组件啊,Zygote啊,Dalvik啊,SystemServer啊,各种service和框架啊,你妹的,丈母娘要买房,老婆要买车,随时要跪搓衣板,不具备自愈能力还能混吗?

说到这里,我们要稍微严肃点,这边开始严肃认真地进行打劫活动。自愈能力,其实是电信、工控、航天等嵌入式系统的基本要求,Android作为大型软件,挂的时候我们往往看到一个桌面退隐江湖,新的桌面马上起来,而不是直接死机了。死了个东方不败,又来个任我行啊,这就是Android的江湖规则。自愈极其重要,我们平时说的看门狗就是典型的自愈方式之一。看门狗干什么的,就是在那蹲点,情况不对比如动车要撞了,就开始“汪汪”了。没有看门狗是不行的,如果神九的软件跑飞了,不恢复回来,那我们的宇航员就变太空垃圾了。有人要问了,软件不跑飞不就行了吗?软件不跑飞,我已经失业,哪里还有机会来这里纵横乱弹琴呢?

Android里面有几个地方都有东西“蹲点”,虽然不是明确的看门狗。

第一只狗:带你去投胎

Android的第一只狗在init进程里面,init进程会通过捕获SIGCHLD得知其子进程的死亡,并据情况决定是否重启之。记住,在Linux的世界里,在子进程死亡的时候,父进程会收到一个SIGCHLD信号,而后父进程通过wait()系统调用,清理子进程僵尸。大家都知道,一个进程死亡的时候,如果它还有子进程,典型地,子进程会被托孤给init进程,这种情况非常普遍,所以任何一个Linux系统,它的init程序,少不了要做一件事情,就是反复通过wait()清理僵尸,否则Linux系统就会尸横遍野,整个一部生化危机啊有木有?Linux是个怎样残酷的世界啊,我艰于呼吸视听啊,哪里还能有什么言语?永远都是白发人送黑发人,父进程清理子进程。

Android的init进程为SIGCHLD绑定了信号处理函数sigchld_handler(),并创建了一个socket用于接收该函数中发送的socket消息。sigchld_handler()函数只是简单的派送一个消息到该socket:

staticvoidsigchld_handler(ints){write(signal_fd,&s,1);}说到这里,我们要特别点名批评一下信号啊。这位同学很不厚道啊,作为一个异步闯入的事件,经常在别的同学“工程进行中”的时候跑进来,把人吓的。

我在一些公司上Linux课的时候,很多人估计不记得学什么了,都还给我了。但是不晓得还记住我的一句话不,“中断是万恶之源”,一是耗油耗电,二是非法侵占。你想啊,凡是异步的都是恐怖的,都是打断正常业务逻辑的,你下X片,他给你罚款3000,这种属于乱发中断抢钱吧?信号之于用户空间多线程,正如中断之于内核,所以,你要特别注意线程与信号间的安全和死锁啊。多线程编程里面,上了信号就不是闹着好玩的。一般有线程访问临界资源时屏蔽信号、信号处理线程化等方式处理。特别要批评很多长着大脑从不想问题的同学啊,很多同学那就是一瞎写啊,应该怎么规划线程,怎么用信号那都是随心所欲啊。你看人家sigchld_handler(),干完一票就跑,那就是正确的信号处理函数伟大的游击战术啊。你千万不要在信号处理函数里搞什么会战啊。

init在这里接收消息并进一步处理:

if(ufds[2].revents==POLLIN){/*wegotaSIGCHLD-reapandrestartasneeded*/read(signal_recv_fd,tmp,sizeof(tmp));while(!wait_for_one_process(0));continue;}好吧,进去看看wait_for_one_process()吧,请注意我不是要搞情景分析,我只抓一点点代码出来。情景分析之类的恐怖片看多了,会影响三观。各位同学在竖立正确三观的前提下,请自行研读android/system/core/init。所谓正确的三观,就是不是为了读代码而读代码,而是为了分析问题而找代码啃。情景分析为什么三观不正,是因为完全不先提出问题,分析问题,最后研读代码,而是上来一锤子拿代码砸晕你先。绝望啊!

1.无主游魂

挂的时候有“untrackedpid%dexited”打印的进程属于init的子进程,但是并没有在init.rc里面注册service。典型地,那些先父仙游后被托孤给init的进程!实在是太悲惨了,这样的进程,直到死都还没户口,实在是屌丝中的蚕丝。看到这种进程,读书人一声长叹啊!咱们沪上海漂估计就是这种了。谁叫你不是土生土长的上海人fork出来的呢?

土生土长的上海人,有户口的service是这样的:

serviceadbd/sbin/adbduseradbgroupadb2.拿一次性签证的ONESHOTservice

untracked的进程像没有户口的上海工程师,ONESHOT的进程像拿singleentry签证赴美的人,搞一把没第二次机会除非再签。需要在service下面加上oneshot,这样的service挂了就挂了,不会想着投胎,正如:

servicebootsound/system/bin/playmp3usermediagroupaudiooneshot此外,如果service被加了“disabled”标记,也不会自动启动而需要显示地启动。

3.可以投胎的女鬼

其他的进程就爽了,因为被“svc->flags|=SVC_RESTARTING”盖章了,死了会重新投胎,下次init执行到restart_processes()的时候就可以重启之,而且之前顺带还可以在投胎的时候执行点什么,如想投胎个好人家什么、定点空投到某人等,就像聊斋之小谢、聊斋之鲁公女等。咱们就不要像蒲松龄同学那么yy了,美丽女鬼投胎来报答我们的机率不高了,好像我们前世没做过什么好事?不过我们一直在期待!

这个动作通过onrestart指定:

servicezygote/system/bin/app_process-Xzygote/system/bin--zygote--start-system-serversocketzygotestream666onrestartwrite/sys/android_power/request_statewakeonrestartwrite/sys/power/stateononrestartrestartmediaonrestartrestartnetd在这里,我们要哀叹一下,兰若寺真不是个好地方,Android就好多了,只要你有户口,又不是ONESHOT,你就可以马上重新投胎,聂小倩啊聂小倩,你真是死不逢地啊!于是你造就了一段荡气回肠的流传千古的爱恋。张国荣《倩女幽魂》,我最爱的电影之一。中国广电总局真是个奇怪的地方,硬是把聂小倩从女鬼变成了狐妖,因为他们说这个世界没有鬼,难道这个世界就有妖吗?我懂了,鬼是精神的,妖是物质的,俺们那个是唯物论的。电视剧里人鬼恋的意境全无,本文忠实于蒲松龄同学原著,聂小倩为女鬼,鉴定完毕。

4.压死Android的最后一根稻草

serviceueventd/sbin/ueventdcriticalserviceservicemanager/system/bin/servicemanagerusersystemcriticalonrestartrestartzygoteonrestartrestartmediaAndroid,第一只狗,就是这样负责一个挂掉的service的重启的。下集我们讲第二只狗:生死与共的Zygote与SystemServer。

上回书我们说到Android里的init会监测init.rc中启动的service并根据情况重启之。今回书我们说Android中生死与共的Zygote和SystemServer。

第二只狗:忠犬八公

方今之世,正道不昌,邪魔丛生。撞伤不如撞死,因此各种难以理解之行为层出不穷,撞后用天涯明月刀杀人者有之,撞后人民女教师裸体阻救援者有之,这是怎样地一种非人间的浓厚的悲凉?对人生命权的尊重实为最基础之人性,残杀同类,见死阻救,实为丧失最基础之人性。热泪在心中,汇成河流。我们曾经以无限崇高的主义,无比正义的革命的名义杀人,肆意践踏人最基本的尊严,而悲剧的后果是,热血已成过去,激情亦然淡去,主义仍如浩渺,人性却遭沦丧。

人性已泯,狗性依然,如太平洋的风。本回书,我们说第二只狗,忠犬八公。1925年5月,八公的主人上野因病猝然去世,然而八公犬并不懂人事,依然每天到涩谷站去等候主人的归来。直到最后死去。1935年3月,八公因患丝虫性象皮病而死亡。十年生死两茫茫,不思量,自难忘。所谓爱者,大抵如此。在这个小三流行的世界,什么样的人还珍视婚姻的责任和当初的誓言

本回书我们要谈的是Zygote和SystemServer的生死与共,执子之手,与子偕老,Zygote和SystemServer用全部的生命来演绎这段忠贞的爱情,当死去时,与子成蝶,如果有来生,我们还在一起。

Zygote是Android系统的核心,受精卵的意思,Androidframework大家族的祖先。她的妈贵姓呢?就是上回书里说的init。Zygote是Java世界的生产者,Android的女娲,她通过runSelectLoopMode()不断监听来自应用程序的透过ActivityManagerService的启动需求,并fork出相应的进程。而SystemServer是Android世界里的核心价值,SurfaceFlinger以及Java服务如PowerManagerService、WindowManagerService、ActivityManagerService等都是他启动的,并成为他的一部分,他带的嗷嗷叫的兵,共同运行于SystemServer进程空间。可以说,SystemServer的崩溃基本意味着Android的Framework的崩溃。SystemServer是Android里兵马大元帅。基本上,在Android的世界里,能与SystemServer和Zygote彼此相配的,也就只有对方。那么SystemServer百战而死后,Zygote应该是万念俱灰,其实真地没有活着的必要。

有些同学就要问了,一个进程莫名其妙地kill掉了自己,有时候我们还强行用kill命令去杀死Linux的进程,这个时候它原本申请的内存什么的,不是泄露了吗?譬如我malloc了一些内存,你杀死我的时候我还没free,这些内存不是侧露了吗?我已经反复在各个公司演讲的时候说了,内存泄露分为两种境界,一个是人死了,钱还没花完,你malloc的内存还没释放进程就死了,我们说,这个问题在Linux不存在,进程是个资源封装的单位,进程挂的时候,资源会被内核释放掉的,死的时候还仅仅有个僵尸而已。第二个境界是,人活着,钱没了,这个问题才是Linux真正担心的,一个多线程的程序,运行过程中反复申请和释放内存,但是释放的与申请的不对应,就慢慢地吃内存,这个进程的内存消耗曲线振荡上升,直到耗尽内存。所以,在Linux世界里,我们不用担心人死了,钱还没花完的问题,党会帮我们处理的。我们要担心的是,人活着,钱没了的问题。

话说HTCAndroid手机号称的1秒快速启动,根据我们的跟踪就是关机时候杀进程,然后suspend,之后resume回来,由于前面进程都杀了,所以你看到个干净的桌面。所以,神马都是浮云啊。你收回了黄岩岛,油价还是要涨的,他们会说我们是花了钱收回来的。

废话少说,你刚才还说Zygote和SystemServer生死与共,怎么就才等了一下呢?不是要负责一辈子的吗?我们回到forkAndSpecializeCommon(),继续挖掘:

staticpid_tforkAndSpecializeCommon(constu4*args,boolisSystemServer){…setSignalHandler();dvmDumpLoaderStats("zygote");pid=fork();if(pid==0){interr;/*Thechildprocess*/…}returnpid;}其中的setSignalHandler()会设置SIGCHLD的信号处理函数,而这个信号处理函数就会判断是否SystemServer死了,如果是,就自杀殉情:

当Zygote也死去,由于Zygote是有户口的,上回书中的第一只狗,肩负其使命,会重启Zygote,于是SystemServer也随Zygote重启,生生世世不分离。

这里要特别说明的是,当zygote死去的时候,上回书中的init中的wait_for_one_process()会透过给“-zygote_pid”发SIGKILL,从而杀死Zygote对应的进程组,因此整个Java世界都宣告结束:

staticintwait_for_one_process(intblock){...while((pid=waitpid(-1,&status,block0:WNOHANG))==-1&&errno==EINTR);...if(!(svc->flags&SVC_ONESHOT)){kill(-pid,SIGKILL);NOTICE("process'%s'killinganychildreninprocessgroup\n",svc->name);}...}由于几乎所有的Java应用都依赖于SystemServer中的service,如果SystemServer崩溃,Zygote不死并且不导致整个Java世界死亡,实际上系统没有任何办法把状态恢复到SystemServer崩溃之前的状态,那么各个apk所看到的SystemServer中各个service的状态也无法恢复,所以整个Java世界死亡并重启就成为唯一的选择。

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

第三只狗:朝廷鹰犬

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

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

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

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

了解了侦测servicehang的原理,我们需要一套方法来实现这个侦测,这个过程透过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函数在一新线程被执行:

finalclassHeartbeatHandlerextendsHandler{@OverridepublicvoidhandleMessage(Messagemsg){switch(msg.what){caseMONITOR:{//调用每个monitorfinalintsize=mMonitors.size();for(inti=0;i

结合第一回和第二回,我们基本可以看出整个Android软件自愈过程的“环环相扣”,成为一个多么完美的体系啊!SystemServer监控重要service,重要servicehang则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给揪了出来。鹰犬问道,你只要放弃你的信念,保持喂狗,就可以活。而他(她)毅然选择了慷慨负死。威廉·华莱士道:“跟着我,你们将失去生命,但是,你们将获得自由”。

THE END
1.AppStore上的“LogE”LogE.cc is the leading online B2B Marketplace/ Procurement as a Service platform for your FMCG in Jordan and soon in other countries in the GCC. LogE Mobile Ap…https://apps.apple.com/cn/app/id1492792008
2.loge在java中怎么表达java中log的info是什么loge 在java 中怎么表达 java中log的info是什么 一个完整的软件,日志是必不可少的。程序从开发、测试、维护、运行等环节,都需要向控制台或文件等位置输出大量信息。这些信息的输出, 在很多时候是使用System.out.println()无法完成的。 日志信息根据用途与记录内容的不同,分为调试日志、运行日志、异常日志等。https://blog.51cto.com/u_16099361/9796205
3.LogViewer64位1.8.0.0LogViewer官方最新下载软件功能 1、导入日志文件,并显示出日志内容。 2、保存会话。当日志分析到一半时可以将会话保存,当下次打开可以恢复会话。 3、编辑功能,可以编辑导入的任何文本。 4、logviewer pro支持过滤功能,选择需要过滤的词语并将筛选符合条件的日志导出供下一步分析。 https://soft.3dmgame.com/down/291860.html
4.上海朗珈软件有限公司朗珈病理系统上海朗珈软件有限公司秉持“专业成熟?信任之选”的责任感,着力打造规范的软件研发和服务体系,是一家专注于病理软件的公司。朗珈病理系统覆盖不同级别医院用户对病理科室管理、多院区、区域病理中心及远程会诊等多种需求,为医院病理信息化提供完整的解决方案。https://www.logene.com/