0. 前言
Watchdog字面上是“看门狗”的意思,有做过嵌入式低层的朋友应该知道,为了防止嵌入式系统MCU里的程序因为干扰而跑飞,专门在MCU里设计了一个定时器电路,叫做看门狗。当MCU正常工作的,每隔一段时间会输出一个信号给看门狗,也就是所谓的喂狗。如果程序跑飞,MCU在规定的时间内没法喂狗,这时看门狗就会直接触发一个reset信号,让CPU重新启动。
在Android系统的framework中,设计了一个系统服务Watchdog,它类似于一个软件看门狗,用来保护重要的系统服务。它的源代码位于:
frameworks/base/services/core/java/com/android/server/Watchdog.java
流程图
1. 获取WatchDog 对象
public class Watchdog extends Thread { }
想要分析一个功能代码,可能先从本身的源头找起,对于Java 类首先看的就是类的定义以及构造构造函数啦!
从这里看 WatchDog 其实一个Thread,这个Thread 可能比较特殊而已,至于怎么特殊,下面会在SystemServer 分析的时候说明。那对于一个Thread,核心的操作部分就是run() 函数了,这个最重要的部分会放在最后解析。
再来看下WatchDog 的构造函数:
private Watchdog() { }
WatchDog 构造函数是private,对于外界获取对象的接口为:
public static Watchdog getInstance() {
if (sWatchdog == null) {
sWatchdog = new Watchdog();
}
return sWatchdog;
}
外界获取WatchDog 就是通过getInstance(),至于这个“外界”后面会补充。
2. WatchDog 的启动
/frameworks/base/services/java/com/android/server/SystemServer.java private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) { t.traceBegin("startBootstrapServices"); // Start the watchdog as early as possible so we can crash the system server // if we deadlock during early boot t.traceBegin("StartWatchdog"); final Watchdog watchdog = Watchdog.getInstance(); watchdog.start(); t.traceEnd(); ... t.traceBegin("InitWatchdog"); watchdog.init(mSystemContext, mActivityManagerService); t.traceEnd(); ... }
从第一节得知WatchDog 是一个单独的 thread,而且是以单例的形式存在,则需要确定其启动的地方。
这部分主要作用:
创建WatchDog 实例;
运行WatchDog thread;
在AMS 启动后,执行init 接口;
在system_server 进程中会启动WatchDog 线程,专门用来给system_server 中的其他线程喂狗。
3. 构造函数
private Watchdog() {
//线程名为watchdog
super("watchdog");
//将fg thread 单独提出作为主要的checker
mMonitorChecker = new HandlerChecker(FgThread.getHandler(),
"foreground thread", DEFAULT_TIMEOUT);
mHandlerCheckers.add(mMonitorChecker);
//创建主线程的checker
mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),
"main thread", DEFAULT_TIMEOUT));
//创建UI thread 的checker
mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),
"ui thread", DEFAULT_TIMEOUT));
//创建Io thread 的checker
mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),
"i/o thread", DEFAULT_TIMEOUT));
//创建display thread 的checker
mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),
"display thread", DEFAULT_TIMEOUT));
//创建animation thread 的checker
mHandlerCheckers.add(new HandlerChecker(AnimationThread.getHandler(),
"animation thread", DEFAULT_TIMEOUT));
//创建surface animation thread 的checker
mHandlerCheckers.add(new HandlerChecker(SurfaceAnimationThread.getHandler(),
"surface animation thread", DEFAULT_TIMEOUT));
//fg thread的checker 中添加对binder 的checker
addMonitor(new BinderThreadMonitor());
// 添加对watchdog 相关目录的监控
mOpenFdMonitor = OpenFdMonitor.create();
mInterestingJavaPids.add(Process.myPid());
// See the notes on DEFAULT_TIMEOUT.
assert DB ||
DEFAULT_TIMEOUT > ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;
}
3.1 设定线程名
设WatchDog 的线程名为 watchdog
3.2 创建 HandlerChecker
WatchDog 中靠 HandlerChecker 来完成check 工作,每个 HandlerChecker 伴随一个Handler,即一个独立的 Looper 和 Thread。
WatchDog 在创建的时候指定对 FgThread、MainThread、UIThread、IoThread、DisplayThread、AnimationThread、SurfaceAnimationThread 等thread 的监控,当然后期可以通过接口动态添加到check list(mHandlerCheckers) 中,例如通过接口 addThread(),详细看第 5.2 节。
- MainThread 是在 SystemServer 运行的时候创建的
frameworks/base/services/java/com/android/server/SystemServer.java
private void run() {
...
Looper.prepareMainLooper();
...
}
- 其他的Thread 最终都是继承自HandlerThread
HandlerChecker 有3 个参数分别是Handler 对象、name、以及触发watchdog 的最大时间间隔,详细的HandlerChecker 看下面第6节。
3.3 添加对binder 的监控
private static final class BinderThreadMonitor implements Watchdog.Monitor {
@Override
public void monitor() {
Binder.blockUntilThreadAvailable();
}
}
monitor 会调用到native 层 IPCThreadState
frameworks/native/libs/binder/IPCThreadState.cpp
void IPCThreadState::blockUntilThreadAvailable()
{
pthread_mutex_lock(&mProcess->mThreadCountLock);
while (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads) {
ALOGW("Waiting for thread to be free. mExecutingThreadsCount=%lu mMaxThreads=%lu\n",
static_cast<unsigned long>(mProcess->mExecutingThreadsCount),
static_cast<unsigned long>(mProcess->mMaxThreads));
pthread_cond_wait(&mProcess->mThreadCountDecrement, &mProcess->mThreadCountLock);
}
pthread_mutex_unlock(&mProcess->mThreadCountLock);
}
BinderThreadMonitor 会被添加到fg thread 中,主要是用于确认binder 是否有出现不够用的情况,例如,假设binder 的max thread 为15个,超过15后就需要check 是否存在binder 阻塞。
3.4 创建OpenFdMonitor
public static OpenFdMonitor create() { // Only run the FD monitor on debuggable builds (such as userdebug and eng builds). if (!Build.IS_DEBUGGABLE) { return null; } final StructRlimit rlimit; try { rlimit = android.system.Os.getrlimit(OsConstants.RLIMIT_NOFILE); } catch (ErrnoException errno) { Slog.w(TAG, "Error thrown from getrlimit(RLIMIT_NOFILE)", errno); return null; } final File fdThreshold = new File("/proc/self/fd/" + (rlimit.rlim_cur - FD_HIGH_WATER_MARK)); return new OpenFdMonitor(new File("/data/anr"), fdThreshold); }
- 只有 debug 版本才做fd monitor,例如 userdebug 或 eng;
- 主要是确认 /data/anr 路径的正常性
3.5 mInterestingJavaPids
主要为了AMS dump stack 时使用,通过接口 processStarted 添加interest pid,AMS 中每次start 一个process 的时候,都会调用handleProcessStartedLocked 最终调用WatchDog.processStarted 添加到WatchDog 中,而process 也是有限定的,如下:
private static boolean isInterestingJavaProcess(String processName) {
return processName.equals(StorageManagerService.sMediaStoreAuthorityProcessName)
|| processName.equals("com.android.phone");
}
public void processStarted(String processName, int pid) {
if (isInterestingJavaProcess(processName)) {
Slog.i(TAG, "Interesting Java process " + processName + " started. Pid " + pid);
synchronized (this) {
mInterestingJavaPids.add(pid);
}
}
}
4. init 函数
public void init(Context context, ActivityManagerService activity) {
mResolver = context.getContentResolver();
mActivity = activity;
context.registerReceiver(new RebootRequestReceiver(),
new IntentFilter(Intent.ACTION_REBOOT),
android.Manifest.permission.REBOOT, null);
}
注册了reboot 的广播,软重启的操作时在这里进行的
final class RebootRequestReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context c, Intent intent) {
if (intent.getIntExtra("nowait", 0) != 0) {
rebootSystem("Received ACTION_REBOOT broadcast");
return;
}
Slog.w(TAG, "Unsupported ACTION_REBOOT broadcast: " + intent);
}
}
void rebootSystem(String reason) { 系统中WatchDog 日志分
Slog.i(TAG, "Rebooting system because: " + reason);
IPowerManager pms = (IPowerManager)ServiceManager.getService(Context.POWER_SERVICE);
try {
pms.reboot(false, reason, false);
} catch (RemoteException ex) {
}
}
5. 其他几个重要函数
5.1 addMonitor()
public void addMonitor(Monitor monitor) { synchronized (this) { mMonitorChecker.addMonitorLocked(monitor);//通过此接口会将monitor添加到fg thread中 } }
本函数是将 monitor 添加到 mMonitorChecker 中监控;
public interface Monitor {
void monitor();
}
例如AMS 构造函数中:
Watchdog.getInstance().addMonitor(this);
5.2 addThread()
public void addThread(Handler thread) {
addThread(thread, DEFAULT_TIMEOUT);
}
public void addThread(Handler thread, long timeoutMillis) {//通过此接口新建HandlerChecker
synchronized (this) {
final String name = thread.getLooper().getThread().getName();
mHandlerCheckers.add(new HandlerChecker(thread, name, timeoutMillis));
}
}
通过本函数新建HandlerChecker 并添加到 mHandlerCheckers 中,然后进行 check 该线程是否阻塞,默认timeout 为60s;
例如 AMS 构造函数中:
Watchdog.getInstance().addThread(mHandler);
6. HandlerChecker类
在分析WatchDog 核心函数run 之前,先来分析下核心处理类 HandlerChecker。
6.1 HandlerChecker 是一个Runnable
public final class HandlerChecker implements Runnable {
6.2 HandlerChecker的构造
HandlerChecker(Handler handler, String name, long waitMaxMillis) {
mHandler = handler;
mName = name;
mWaitMax = waitMaxMillis;
mCompleted = true;
}
构造参数有三个:
- handler 用来发消息,通过handler 确定thread;
- name 为HandlerChecker对应thread 的描述时使用;
- waitMaxMillis 为监控时最大时长,默认为60s;
6.3 核心变量
private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
private final ArrayList<Monitor> mMonitorQueue = new ArrayList<Monitor>();
一个HandlerChecker 中可以添加多个monitor,为了安全考虑,防止在monitor check 过程中添加新的monitor,将过程分两部分:
- 每次HandlerChecker 在schedule check的时候,只检查 mMonitors;
- schedule check 过程中,新添加的monitor 都临时存放在 mMonitorQueue 中;
详细看scheduleCheckLocked() 函数,即第 6.4 节。
6.4 scheduleCheckLocked
public void scheduleCheckLocked() {
if (mCompleted) { //step 1
// Safe to update monitors in queue, Handler is not in the middle of work
mMonitors.addAll(mMonitorQueue);
mMonitorQueue.clear();
}
if ((mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling())
|| (mPauseCount > 0)) { //step 2
mCompleted = true;
return;
}
if (!mCompleted) { //step 3
// we already have a check in flight, so no need
return;
}
mCompleted = false; //step 4
mCurrentMonitor = null;
mStartTime = SystemClock.uptimeMillis();
mHandler.postAtFrontOfQueue(this);
}
逻辑比较简单,见代码中注释,将逻辑分为4个部分:
- step1: 新的 schedule 时 mCompleted 为 true,会将 mMonitorQueue 中的 monitor copy到mMonitor 中,用以在 run() 函数中 check;
- step2:如果 handle 的Looper 处于 polling 状态,但是没有 monitor 添加进来,或者是HandlerChecker 处于pause 状态,不做 schedule check,直接指定 schedule 完成状态;
- step3:如果 HandlerChecker 中有monitor 或者是 Looper 中有消息在处理,会跳过 step2, 如果是第一次执行 step3 的 case 是不会处理,如果 mCompleted 此时为false,那肯定是schedule 已经执行过一次,但是30s 的时候并没有执行完成,于是这里直接return,为了再给一次机会,再等 30s 等待 monitor 或者 msg 处理完成;
- step4:能进入 step4,是因为上一次的 schedule 顺利执行完成,run() 会在顺利执行完成将mCompleted 置为true,不会进入 step3;
6.5 run 函数
public void run() {
final int size = mMonitors.size();
for (int i = 0 ; i < size ; i++) {
synchronized (Watchdog.this) {
mCurrentMonitor = mMonitors.get(i);
}
mCurrentMonitor.monitor();
}
synchronized (Watchdog.this) {
mCompleted = true;
mCurrentMonitor = null;
}
}
通过一个循环分别调用每一个 monitor 的 monitor() 函数,如果其中一个阻塞了,则无法退出该循环,mCompleted 状态也不会变成 true,如果 mCompleted 为false,WatchDog 只能通过时间来进一步确认状态,在下面一节中会详细说明。
用 AMS 中的 monitor 来举例:
frameworks/base/services/core/java/com/android/server/am/AMS.java
public void monitor() {
synchronized (this) { }
}
AMS 的 monitor 就是为了 check AMS 是否死锁了。
7. WatchDog 的run() 函数
上面的基本函数都大概解析完了,对于一个Thread 那最重要的肯定还是run() 函数,这里也是WatchDog 的监测机制所在,因为代码比较多,这里裁剪分析。
step 1:建立死循环
WatchDog 在整个系统运行过程中都需要存在的,除非了进程或是系统重启了,不然WatchDog 需要长期运行。
public void run() {
boolean waitedHalf = false;
while (true) {
...
}
}
step2:创建schedule
synchronized (this) {
long timeout = CHECK_INTERVAL;
for (int i=0; i<mHandlerCheckers.size(); i++) {
HandlerChecker hc = mHandlerCheckers.get(i);
hc.scheduleCheckLocked();
}
注意两点:
timeout 默认为30s,所有的schedule 启动完会wait 30s 等待所有的schedule 都能顺利执行完;
启动schedule 会加上锁,与HandlerChecker 的run 函数互斥,这就要求,在wait 之前,HandlerChecker 的runnable 是无法运行的,wait的时候会释放this 对象的锁,runnable 才可以顺利执行(wait/synchronized 使用);
step3:收集check 完成状态
final int waitState = evaluateCheckerCompletionLocked(); if (waitState == COMPLETED) { // The monitors have returned; reset waitedHalf = false; continue; } else if (waitState == WAITING) { // still waiting but within their configured intervals; back off and recheck continue; } else if (waitState == WAITED_HALF) { if (!waitedHalf) { Slog.i(TAG, "WAITED_HALF"); // We've waited half the deadlock-detection interval. Pull a stack // trace and wait another half. ArrayList<Integer> pids = new ArrayList<>(mInterestingJavaPids); ActivityManagerService.dumpStackTraces(pids, null, null, getInterestingNativePids(), null); waitedHalf = true; } continue; }
状态分:
- COMPLETED:顺利完成
- WAITING:有可能HandlerChecker中的monitor 比较多,而部分monitor 占用时间比较长,这就导致有些monitor 执行不到30s时,wait 就结束了,这时候会返回WAITING 状态,继续等待下一个schedule;
- WATIED_HALF:同WAITING,会继续等待下一个schedule,不同于WAITING,WAITED_HALF 会通过AMS dump 一次stack;
- OVERDUE:有monitor 执行超过了60s,这个是不允许的,step4 会处理OVERDUE的情况
step4:处理OVERDUE 情况
blockedCheckers = getBlockedCheckersLocked();
subject = describeCheckersLocked(blockedCheckers);
11-06 00:05:28.603 2197 2441 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)
11-06 00:05:28.603 2197 2441 W Watchdog: foreground thread stack trace:
11-06 00:05:28.603 2197 2441 W Watchdog: at com.android.server.am.ActivityManagerService.monitor(ActivityManagerService.java:23862)
11-06 00:05:28.604 2197 2441 W Watchdog: at com.android.server.Watchdog$HandlerChecker.run(Watchdog.java:211)
11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.Handler.handleCallback(Handler.java:790)
11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.Handler.dispatchMessage(Handler.java:99)
11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.Looper.loop(Looper.java:164)
11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.HandlerThread.run(HandlerThread.java:65)
11-06 00:05:28.604 2197 2441 W Watchdog: at com.android.server.ServiceThread.run(ServiceThread.java:46)
11-06 00:05:28.604 2197 2441 W Watchdog: ActivityManager stack trace:
11-06 00:05:28.604 2197 2441 W Watchdog: at com.android.server.am.ActivityManagerService.idleUids(ActivityManagerService.java:23305)
11-06 00:05:28.604 2197 2441 W Watchdog: at com.android.server.am.ActivityManagerService$MainHandler.handleMessage(ActivityManagerService.java:2428)
11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.Handler.dispatchMessage(Handler.java:106)
11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.Looper.loop(Looper.java:164)
11-06 00:05:28.604 2197 2441 W Watchdog: at android.os.HandlerThread.run(HandlerThread.java:65)
11-06 00:05:28.604 2197 2441 W Watchdog: at com.android.server.ServiceThread.run(ServiceThread.java:46)
11-06 00:05:28.604 2197 2441 W Watchdog: *** GOODBYE
具体的可以看 Android 系统中WatchDog 日志分析
总结:
Android 中的WatchDog 主要是监测系统中重要服务,例如AMS、WMS 等,当注册的monitor 无法通过检测,或者是消息处理超时的时候就会触发WatchDog,最后可能会引起系统的重启。
附加:WatchDog 类图
文章转载: