今天我们要整的是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:
static void sigchld_handler(int s)
{
write(signal_fd, &s, 1);
}
说到这里,我们要特别点名批评一下信号啊。这位同学很不厚道啊,作为一个异步闯入的事件,经常在别的同学“工程进行中”的时候跑进来,把人吓成这样了:
我在一些公司上Linux课的时候,很多人估计不记得学什么了,都还给我了。但是不晓得还记住我的一句话不,“中断是万恶之源”,一是耗油耗电,二是非法侵占。你想啊,凡是异步的都是恐怖的,都是打断正常业务逻辑的,你下X片,他给你罚款3000,这种属于乱发中断抢钱吧?信号之于用户空间多线程,正如中断之于内核,所以,你要特别注意线程与信号间的安全和死锁啊。多线程编程里面,上了信号就不是闹着好玩的。一般有线程访问临界资源时屏蔽信号、信号处理线程化等方式处理。特别要批评很多长着大脑从不想问题的同学啊,很多同学那就是一瞎写啊,应该怎么规划线程,怎么用信号那都是随心所欲啊。你看人家sigchld_handler(),干完一票就跑,那就是正确的信号处理函数伟大的游击战术啊。你千万不要在信号处理函数里搞什么会战啊。
看看init进程如何处理收到的消息呢,源代码情景分析这样的文章最恶心,你看了五年跟没有看一样,基本上不知所云,所以我们不搞那一套。中国的Linux开发者太痛苦,很多人把源代码情景分析当小说在读,一定从头读到尾,其实那个最多一新华字典,应该是参考书而非通读读物,读完了,除了精神崩溃以外,就是精神彻底崩溃,天天让你背新华字典你还活地下去不?所以,你还是看我的《Linux设备驱动开发详解》,看我的纵横谈比较实惠也比较符合人性。
init在这里接收消息并进一步处理:
if (ufds[2].revents == POLLIN) {
/* we got a SIGCHLD - reap and restart as needed */
read(signal_recv_fd, tmp, sizeof(tmp));
while (!wait_for_one_process(0))
;
continue;
}
好吧,进去看看wait_for_one_process()吧,请注意我不是要搞情景分析,我只抓一点点代码出来。情景分析之类的恐怖片看多了,会影响三观。各位同学在竖立正确三观的前提下,请自行研读android/system/core/init。所谓正确的三观,就是不是为了读代码而读代码,而是为了分析问题而找代码啃。情景分析为什么三观不正,是因为完全不先提出问题,分析问题,最后研读代码,而是上来一锤子拿代码砸晕你先。绝望啊!
static int wait_for_one_process(int block)
{
…
while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR ); // 干掉僵尸
svc = service_find_by_pid(pid);
if (!svc) { // 无主僵尸
ERROR("untracked pid %d exited\n", pid);
return 0;
}
NOTICE("process '%s', pid %d exited\n", svc->name, pid);
…
/* oneshot processes go into the disabled state on exit */
if (svc->flags & SVC_ONESHOT) { //不投胎的僵尸
svc->flags |= SVC_DISABLED;
}
/* disabled processes do not get restarted automatically */
if (svc->flags & SVC_DISABLED) {
notify_service_state(svc->name, "stopped");
return 0;
}
now = gettime();
if (svc->flags & SVC_CRITICAL) { //重要僵尸
//短时间内反复挂,重启
if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
…
sync();
__reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, "recovery");
return 0;
}
} else {
svc->time_crashed = now;
svc->nr_crashed = 1;
}
}
svc->flags |= SVC_RESTARTING; //标记重启中
/* Execute all onrestart commands for this service. */
list_for_each(node, &svc->onrestart.commands) { //执行onrestart里面的命令
cmd = node_to_item(node, struct command, clist);
cmd->func(cmd->nargs, cmd->args);
}
notify_service_state(svc->name, "restarting");
return 0;
}
特别留意加中文注释的几个地方:
1. 无主游魂
挂的时候有“untracked pid %d exited”打印的进程属于init的子进程,但是并没有在 init.rc里面注册service。典型地,那些先父仙游后被托孤给init的进程!实在是太悲惨了,这样的进程,直到死都还没户口,实在是屌丝中的蚕丝。看到这种进程,读书人一声长叹啊!咱们沪上海漂估计就是这种了。谁叫你不是土生土长的上海人fork出来的呢?
土生土长的上海人,有户口的service是这样的:
service adbd /sbin/adbd
user adb
group adb
2. 拿一次性签证的ONESHOT service
untracked的进程像没有户口的上海工程师,ONESHOT的进程像拿single entry签证赴美的人,搞一把没第二次机会除非再签。需要在service下面加上oneshot,这样的service挂了就挂了,不会想着投胎,正如:
servicebootsound /system/bin/playmp3
user media
group audio
oneshot
此外,如果service被加了“disabled”标记,也不会自动启动而需要显示地启动。
3. 可以投胎的女鬼
其他的进程就爽了,因为被“svc->flags |= SVC_RESTARTING”盖章了,死了会重新投胎, 下次init执行到restart_processes()的时候就可以重启之,而且之前顺带还可以在投胎的时候执行点什么,如想投胎个好人家什么、定点空投到某人等,就像聊斋之小谢、聊斋之鲁公女等。咱们就不要像蒲松龄同学那么yy了,美丽女鬼投胎来报答我们的机率不高了,好像我们前世没做过什么好事?不过我们一直在期待!
这个动作通过onrestart指定:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
socket zygote stream 666
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
在这里,我们要哀叹一下, 兰若寺真不是个好地方,Android就好多了,只要你有户口,又不是ONESHOT,你就可以马上重新投胎,聂小倩啊聂小倩,你真是死不逢地啊!于是你造就了一段荡气回肠的流传千古的爱恋。张国荣《倩女幽魂》,我最爱的电影之一。
中国广电总局真是个奇怪的地方,硬是把聂小倩从女鬼变成了狐妖,因为他们说这个世界没有鬼,难道这个世界就有妖吗?我懂了,鬼是精神的,妖是物质的,俺们那个是唯物论的。电视剧里人鬼恋的意境全无,本文忠实于蒲松龄同学原著,聂小倩为女鬼,鉴定完毕。
4. 压死Android的最后一根稻草
如果一个service是critical的,而它又在短时间内反复挂,restart后又总是夭折,我们很可能不能再让它这么痛苦下去了,唯一的方法就是让它解脱,于是整个系统重启。这样的service包括:
service ueventd /sbin/ueventd
critical
service servicemanager /system/bin/servicemanager
user system
critical
onrestart restart zygote
onrestart restart media
Android,第一只狗,就是这样负责一个挂掉的service的重启的。下集我们讲第二只狗:生死与共的Zygote与SystemServer。
第二只狗:忠犬八公
方今之世,正道不昌,邪魔丛生。撞伤不如撞死,因此各种难以理解之行为层出不穷,撞后用天涯明月刀杀人者有之,撞后人民女教师裸体阻救援者有之,这是怎样地一种非人间的浓厚的悲凉?对人生命权的尊重实为最基础之人性,残杀同类,见死阻救,实为丧失最基础之人性。热泪在心中,汇成河流。我们曾经以无限崇高的主义,无比正义的革命的名义杀人,肆意践踏人最基本的尊严,而悲剧的后果是,热血已成过去,激情亦然淡去,主义仍如浩渺 ,人性却遭沦丧。
人性已泯,狗性依然,如太平洋的风。本回书,我们说第二只狗,忠犬八公。1925年5月,八公的主人上野因病猝然去世,然而八公犬并不懂人事,依然每天到涩谷站去等候主人的归来。直到最后死去。1935年3月,八公因患丝虫性象皮病而死亡。十年生死两茫茫,不思量,自难忘。所谓爱者,大抵如此。在这个小三流行的世界,什么样的人还珍视婚姻的责任和当初的誓言?
本回书我们要谈的是Zygote和SystemServer的生死与共,执子之手,与子偕老,Zygote和SystemServer用全部的生命来演绎这段忠贞的爱情,当死去时,与子成蝶,如果有来生,我们还在一起。
Zygote是Android系统的核心,受精卵的意思,Android framework大家族的祖先。她的妈贵姓呢?就是上回书里说的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应该是万念俱灰,其实真地没有活着的必要。
在Android中,SystemServer是由 Zygote分叉出来的,相关代码位于dalvik/vm/native/dalvik_system_Zygote.c中:
static void Dalvik_dalvik_system_Zygote_forkSystemServer(
const u4* args, JValue* pResult)
{
pid_t pid;
pid = forkAndSpecializeCommon(args, true);
/* The zygote process checks whether the child process has died or not. */
if (pid > 0) {
int status;
LOGI("System server process %d has been created", pid);
gDvm.systemServerPid = pid;
/* There is a slight window that the system server process has crashed
* but it went unnoticed because we haven't published its pid yet. So
* we recheck here just to make sure that all is well.
*/
if (waitpid(pid, &status, WNOHANG) == pid) {
LOGE("System server process %d has died. Restarting Zygote!", pid);
kill(getpid(), SIGKILL);
}
}
RETURN_INT(pid);
}
其中的forkAndSpecializeCommon()会fork出SystemServer,Zygote马上用waitpid(pid, &status,WNOHANG)等待SystemServer的死亡,注意其中的参数为WNOHANG,意思就是说等不等地到SystemServer退出,waitpid()都不会阻塞,如果发现SystemServer死了,它会无条件调用kill(getpid(), SIGKILL)殉情。这个瞬间的等待按照注释是为了防止很小的一段时间窗口里,真正等SystemServer死的代码还没注意到SystemServer的PID。SIGKILL牛就牛在是不能被忽略的信号,这点和CTRL+C对应的SIGINT不同。
有些同学就要问了,一个进程莫名其妙地kill掉了自己,有时候我们还强行用kill命令去杀死Linux的进程,这个时候它原本申请的内存什么的,不是泄露了吗?譬如我malloc了一些内存,你杀死我的时候我还没free,这些内存不是侧露了吗?我已经反复在各个公司演讲的时候说了,内存泄露分为两种境界,一个是人死了,钱还没花完,你malloc的内存还没释放进程就死了,我们说,这个问题在Linux不存在,进程是个资源封装的单位,进程挂的时候,资源会被内核释放掉的,死的时候还仅仅有个僵尸而已。第二个境界是,人活着,钱没了,这个问题才是Linux真正担心的,一个多线程的程序,运行过程中反复申请和释放内存,但是释放的与申请的不对应,就慢慢地吃内存,这个进程的内存消耗曲线振荡上升,直到耗尽内存。所以,在Linux世界里,我们不用担心人死了,钱还没花完的问题 ,党会帮我们处理的。我们要担心的是,人活着,钱没了的问题。
话说HTC Android手机号称的1秒快速启动,根据我们的跟踪就是关机时候杀进程,然后suspend,之后resume回来,由于前面进程都杀了,所以你看到个干净的桌面。所以,神马都是浮云啊。你收回了黄岩岛,油价还是要涨的,他们会说我们是花了钱收回来的。
废话少说,你刚才还说Zygote和SystemServer 生死与共,怎么就才等了一下呢?不是要负责一辈子的吗?我们回到forkAndSpecializeCommon(),继续挖掘:
static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer)
{
…
setSignalHandler();
dvmDumpLoaderStats("zygote");
pid = fork();
if (pid == 0) {
int err;
/* The child process */
…
}
return pid;
}
其中的setSignalHandler()会设置SIGCHLD的信号处理函数,而这个信号处理函数就会判断是否SystemServer死了,如果是,就自杀殉情:
static void setSignalHandler()
{
int err;
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sigchldHandler;
err = sigaction (SIGCHLD, &sa, NULL);
if (err < 0) {
LOGW("Error setting SIGCHLD handler: %s", strerror(errno));
}
}
static void sigchldHandler(int s)
{
pid_t pid;
int status;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
…
/*
* If the just-crashed process is the system_server, bring down zygote
* so that it is restarted by init and system server will be restarted
* from there.
*/
if (pid == gDvm.systemServerPid) {
LOG(LOG_INFO, ZYGOTE_LOG_TAG,
"Exit zygote because system server (%d) has terminated\n",
(int) pid);
kill(getpid(), SIGKILL);
}
}
…
}
当SystemServer死去,Zygote的花轿在它坟前路过,见Zygote走出轿来,脱去红装,一身素服,缓缓地走到坟前, 跪下来放声大哭,霎时间风雨飘摇,雷声大作,“轰”的一声,坟墓裂开了,Zygote似乎又见到了SystemServer那温柔的面庞,她微笑着纵身跳了进去。接着又是一声巨响,坟墓合上了。这时风消云散,雨过天晴,各种野花在风中轻柔地摇曳,一对美丽的蝴蝶从坟头飞出来,在阳光下自由地翩翩起舞。
当Zygote也死去,由于Zygote是有户口的,上回书中的第一只狗, 肩负其使命,会重启Zygote,于是SystemServer也随Zygote重启,生生世世不分离。
这里要特别说明的是,当zygote死去的时候,上回书中的init中的wait_for_one_process()会透过给 “- zygote_pid”发SIGKILL,从而杀死Zygote对应的进程组,因此整个Java世界都宣告结束:
static int wait_for_one_process(int block)
{
...
while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR );
...
if (!(svc->flags & SVC_ONESHOT)) {
kill(-pid, SIGKILL);
NOTICE("process '%s' killing any children in process group\n", svc->name);
}
...
}
由于几乎所有的Java应用都依赖于SystemServer中的service,如果SystemServer崩溃,Zygote不死并且不导致整个Java世界死亡,实际上系统没有任何办法把状态恢复到SystemServer崩溃之前的状态 ,那么各个apk所看到的SystemServer中各个service的状态也无法恢复,所以整个Java世界死亡并重启就成为唯一的选择。
第三只狗:朝廷鹰犬
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 watchdog
public 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 {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MONITOR: {
//调用每个monitor
final int size = mMonitors.size();
for (int i = 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的方法学。谁可以帮我p一副图?把下面这个宇宙大爆炸图中p出个Android,旁边再p出个苦B程序员呢?
在朝廷鹰犬的监控之下,无法与中央保持一致的service革命者就这样被watchdog给揪了出来。鹰犬问道,你只要放弃你的信念,保持喂狗,就可以活。而他(她)毅然选择了慷慨负死。威廉·华莱士道:“跟着我,你们将失去生命,但是,你们将获得自由” 。
谨以本回,献给中国历史上前赴后继的伟大的为天下人谋求永福的人们。
作者:宋宝华