世事难料,保持低调

由于Android的SystemServer内有一票重要Service,所以在进程内有一个软件实现的Watchdog机制,用于监视SystemServer中各Service是否正常工作。如果超过一定时间(默认30秒),就dump现场便于分析,再超时(默认60秒)就重启SystemServer保证系统可用性。同时logcat中会打印类似下面信息:

W Watchdog: *** WATCHDOG KILLING SYSTEM PROCESS: Blocked in monitor com.android.server.am.ActivityManagerService on foreground thread (android.fg), Blocked in handler on ActivityManager (ActivityManager), Blocked in handler on WindowManager thread (WindowManager)

主要实现代码位于/frameworks/base/services/core/java/com/android/server/Watchdog.java和/frameworks/base/core/jni/android_server_Watchdog.cpp。大体的框架很简单。Watchdog是SystemServer中的独立线程,它隔一定时间间隔会向各监视线程调度一次检查操作。这个检查操作当中会调用已注册的Monitor对象。如果Monitor对象上产生死锁,或是关键线程hang住,那么该检查必定不能按时结束,这样就被Watchdog检查到。

先来看看总体类图。因为是唯一的,Watchdog实现为Singleton。其中维护HandlerChecker数组,对应要检查的线程。HandlerChecker数组中有Monitor数组,对应要检查的Monitor对象。要接受检查的对象需要实现Monitor接口。

初始化是从SystemServer的startOtherServices()中开始的,其大体流程如下:

首先,在SystemServer.java中,会创建Watchdog并启动。

472Slog.i(TAG, "Init Watchdog");473final Watchdog watchdog = Watchdog.getInstance();474watchdog.init(context, mActivityManagerService);…1120Watchdog.getInstance().start();在Watchdog的构造函数中,会为每个要检查的线程创建HandlerChecker,并加到mHandlerCheckers这个队列中。首先是FgThread。它继承自ServiceThread,是一个单例,负责那些常规的前台操作,它不应该被后台的操作所阻塞。在Watchdog.java中:215mMonitorChecker = new HandlerChecker(FgThread.getHandler(),216"foreground thread", DEFAULT_TIMEOUT);217mHandlerCheckers.add(mMonitorChecker);

接下来,对于System Server的主线程,UI线程,IO线程和Display线程,分别做相同操作,这坨线程和FgThread一样都继承自ServiceThread。在init()函数中,接下来会调用registerReceiver()来注册系统重启的BroadcastReceiver。在收到系统重启广播时会执行RebootRequestReceiver的onReceive()函数,继而调用rebootSystem()重启系统。它允许其它模块(如CTS)通过发广播来让系统重启。

然后,各个需要被Watchdog监视的Service需要将自己进行注册。它们都实现了Watchdog.Monitor接口,其中主要是monitor()函数。例如ActivityManagerService:

2150Watchdog.getInstance().addMonitor(this);2151Watchdog.getInstance().addThread(mHandler);其中addMonitor()将自身放到foreground thread的HandlerChecker的monitor队列中,addThread()根据当前线程的Handler创建HandlerChecker并放到mHandlerCheckers队列中。monitor()的实现一般很简单,只是尝试去获得锁再释放锁。如果有deadlock,就会卡住无法返回。18767 public void monitor() {18768synchronized (this) { }18769 }

回到SystemServer中,通过Watchdog的start()方法启动Watchdog线程,Watchdog.run()被执行。

Watchdog的主体是一个循环。在每一次循环中,所有HandlerChecker的scheduleCheckLocked()函数被调用。其中主要是往被监视线程的Looper中放入HandlerChecker对象,HandlerChecker本身作为Runnable,是线程的可执行体。因此当被监视线程把它从Looper中拿出来后,它的run()函数被调用。然后,Watchdog.run()等待最长30秒后,调用evaluateCheckerCompletionLocked()检查各HandlerChecker结果状态。一个HandlerChecker结果状态有四种,COMPLETED(0),WAITING(1), WAITED_HALF(2)和OVERDUE(3)。分别代表目标Looper已处理monitor,延时小于30秒,延时大于30秒小于60秒,延时大于60秒。最终的总状态是它们的最大值(也就是意味着最坏情况的那种情况)。如果总状态是COMPLETED,WAITING或WAITED_HALF,则进入循环下一轮。注意如果是WAITED_HALF,也就是说等了大于30秒,,需要调用AMS.dumpStackTraces()来dump栈。如果状态为WAITED_HALF,进入下一轮循环后,又会等待最长30秒。假设有线程阻塞,对于阻塞线程的HandlerChecker,它的延迟超过60秒,导致总状态为OVERDUE。这里会调用getBlockedCheckersLocked()和describeCheckersLocked()打印出是哪个Handler阻塞了。在Eventlog中打出信息后,把当前pid和相关进程pid放入待杀列表。然后和上面一样调用AMS.dumpStackTraces()打印栈。之后等待2秒等stacktrace写完。如有需要还会调用dumpKernelStackTraces()将kernel部分的stacktrace打出来。本质上是读取/proc/[pid]/task下的线程的和相应的/proc/[tid]/stack文件。然后调用doSyncRq()通知kernel把阻塞线程信息和backtrace打印出来(通过写/proc/sysrq-trigger)。之后会创建专门的线程来将信息写入到Dropbox,该线程会执行AMS.addErrorToDropBox()。Dropbox是Android中的一套日志记录系统,作用是将系统出错信息记录下来并且保留一段时间,避免被覆盖。当出现crash, wtf, lowmem或者Watchdog被触发都会通过Dropbox来记录。

425Thread dropboxThread = new Thread("watchdogWriteToDropbox") {426public void run() {427mActivity.addErrorToDropBox(428"watchdog", null, "system_server", null, null,429subject, null, stack, null);430}431};432dropboxThread.start();433try {434dropboxThread.join(2000); // wait up to 2 seconds for it to return.435} catch (InterruptedException ignored) {}DropBoxManagerService在SystemServer的startOtherServices()中添加,信息默认存放路径为/data/system/dropbox。DropBoxManagerService实现了IDropBoxManagerService服务接口,Client通过DropBoxManager来访问服务。在Watchdog中调用AMS.addErrorToDropBox()后,该函数会起工作线程(因为涉及I/O),将前面得到的stack信息dump到Dropbox,并且获取最近的logcat,最后通过DBMS的addText()接口进行存储。接下来,如果设置了ActivityController就调用其systemNotResponding()接口(IActivityController是给测试开发时用的接口,用于监视AMS里的行为)。然后判断Debugger是否连着和是否允许restart。如果没有连着debugger且允许restart,就开始大开杀戒了。467Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + subject);…476Slog.w(TAG, "*** GOODBYE!");477Process.killProcess(Process.myPid());478System.exit(10);因为Watchdog和SystemServer是同一进程,这里Watchdog kill掉了自己,也就是kill了SystemServer。因它是主要进程,杀掉后会被init重启。多对自己说“我能行,我一定可以”,

世事难料,保持低调

相关文章:

你感兴趣的文章:

标签云: