Android 系统中的 WatchDog 详解

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 中监控;

 
 
  1. public interface Monitor {

  2. void monitor();

  3. }

例如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 类图

 

文章转载:

Android 系统中的 WatchDog 详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值