• WatchDog工作原理


    Android系统中,有硬件WatchDog用于定时检测关键硬件是否正常工作,类似地,在framework层有一个软件WatchDog用于定期检测关键系统服务是否发生死锁事件。

    watchdog的源码很简单,主要有两个功能

    1监控system_server中几个关键的锁,原理就是在android_fg线程中尝试加锁

    2监控几个常用线程的执行时间,原理就是在这几个线程中执行任务

    WatchDog初始化

    在SystemServer.startOtherServices()方法进行初始化

    system_server进程启动的过程中初始化WatchDog,主要进行三个动作:

    • 创建watchdog对象,该对象本身继承于Thread;
    • 注册reboot广播;
    • 调用start()开始工作。

    1.创建Watchdog对象

    public class Watchdog extends Thread {
        //所有的HandlerChecker对象组成的列表,HandlerChecker对象类型
        final ArrayList<HandlerChecker> mHandlerCheckers = new ArrayList<>();
        ...
    
        private Watchdog() {
            super("watchdog");
            //将前台线程加入队列
            mMonitorChecker = new HandlerChecker(FgThread.getHandler(),
                    "foreground thread", DEFAULT_TIMEOUT);
            mHandlerCheckers.add(mMonitorChecker);
            //将主线程加入队列
            mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),
                    "main thread", DEFAULT_TIMEOUT));
            //将ui线程加入队列
            mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),
                    "ui thread", DEFAULT_TIMEOUT));
            //将i/o线程加入队列
            mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),
                    "i/o thread", DEFAULT_TIMEOUT));
            //将display线程加入队列
            mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),
                    "display thread", DEFAULT_TIMEOUT));
            addMonitor(new BinderThreadMonitor());
        }
    
    }

    Watchdog继承于Thread,创建的线程名为”watchdog”。mHandlerCheckers队列包括、 主线程,fg, ui, io, display线程的HandlerChecker对象。

    2.HandlerChecker对象

    public final class HandlerChecker implements Runnable {
        private final Handler mHandler; //Handler对象
        private final String mName; //线程描述名
        private final long mWaitMax; //最长等待时间
        //记录着监控的服务
        private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
        private boolean mCompleted; //开始检查时先设置成false
        private Monitor mCurrentMonitor;
        private long mStartTime; //开始准备检查的时间点
    
        HandlerChecker(Handler handler, String name, long waitMaxMillis) {
            mHandler = handler;
            mName = name;
            mWaitMax = waitMaxMillis;
            mCompleted = true;
        }
    }

    HandlerChecker继承自Runnable对象,里面维护着监控服务Monitor对象列表,使用Watchdog.addMonitor方法将服务监听者加入列表

       addMonitor(new BinderThreadMonitor());

    这里监控Binder线程, 将monitor添加到HandlerChecker的成员变量mMonitors列表中。将BinderThreadMonitor对象加入该线程。

    private static final class BinderThreadMonitor implements Watchdog.Monitor {
        public void monitor() {
            Binder.blockUntilThreadAvailable();
        }
    }

    blockUntilThreadAvailable最终调用的是IPCThreadState,监控是否有可用的binder进程。

    3.Watchdog start

    当调用Watchdog.getInstance().start()时,则进入线程“watchdog”的run()方法, 该方法分成两部分:

    • 用于监测是否触发超时;
    • 当触发超时则输出各种信息。

    4.Watchdog run

    public void run() {
        boolean waitedHalf = false;
        while (true) {
            final ArrayList<HandlerChecker> blockedCheckers;
            final String subject;
            final boolean allowRestart;
            int debuggerWasConnected = 0;
            synchronized (this) {
                long timeout = CHECK_INTERVAL; //CHECK_INTERVAL=30s
                for (int i=0; i<mHandlerCheckers.size(); i++) {
                    HandlerChecker hc = mHandlerCheckers.get(i);
                    //执行所有的Checker的监控方法, 每个Checker记录当前的mStartTime[见小节3.2]
                    hc.scheduleCheckLocked();
                }
    
                if (debuggerWasConnected > 0) {
                    debuggerWasConnected--;
                }
    
                long start = SystemClock.uptimeMillis();
                //通过循环,保证执行30s才会继续往下执行
                while (timeout > 0) {
                    if (Debug.isDebuggerConnected()) {
                        debuggerWasConnected = 2;
                    }
                    try {
                        wait(timeout); //触发中断,直接捕获异常,继续等待.
                    } catch (InterruptedException e) {
                        Log.wtf(TAG, e);
                    }
                    if (Debug.isDebuggerConnected()) {
                        debuggerWasConnected = 2;
                    }
                    timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);
                }
    
                //评估Checker状态【见小节3.3】
                final int waitState = evaluateCheckerCompletionLocked();
                if (waitState == COMPLETED) {
                    waitedHalf = false;
                    continue;
                } else if (waitState == WAITING) {
                    continue;
                } else if (waitState == WAITED_HALF) {
                    if (!waitedHalf) {
                        //首次进入等待时间过半的状态
                        ArrayList<Integer> pids = new ArrayList<Integer>();
                        pids.add(Process.myPid());
                        //输出system_server和3个native进程的traces【见小节4.2】
                        ActivityManagerService.dumpStackTraces(true, pids, null, null,
                                NATIVE_STACKS_OF_INTEREST);
                        waitedHalf = true;
                    }
                    continue;
                }
                ... //进入这里,意味着Watchdog已超时【见小节4.1】
            }
            ...
        }
    }
    
    public static final String[] NATIVE_STACKS_OF_INTEREST = new String[] {
        "/system/bin/mediaserver",
        "/system/bin/sdcard",
        "/system/bin/surfaceflinger"
    };

    该方法主要功能:

    1. 执行所有的Checker的监控方法scheduleCheckLocked()
      • 当mMonitor个数为0(除了android.fg线程之外都为0)且处于poll状态,则设置mCompleted = true;
      • 当上次check还没有完成, 则直接返回.
    2. 等待30s后, 再调用evaluateCheckerCompletionLocked来评估Checker状态;
    3. 根据waitState状态来执行不同的操作:
      • 当COMPLETED或WAITING,则相安无事;
      • 当WAITED_HALF(超过30s)且为首次, 则输出system_server和3个Native进程的traces;
      • 当OVERDUE, 则输出更多信息.

    由此,可见当触发一次Watchdog, 则必然会调用两次AMS.dumpStackTraces, 也就是说system_server和3个Native进程的traces 的traces信息会输出两遍,且时间间隔超过30s.

    5.scheduleCheckLocked

    public final class HandlerChecker implements Runnable {
        ...
        public void scheduleCheckLocked() {
            if (mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling()) {
                mCompleted = true; //当目标looper正在轮询状态则返回。
                return;
            }
    
            if (!mCompleted) {
                return; //有一个check正在处理中,则无需重复发送
            }
            mCompleted = false;
    
            mCurrentMonitor = null;
            // 记录当下的时间
            mStartTime = SystemClock.uptimeMillis();
            //发送消息,插入消息队列最开头, 见下方的run()方法
            mHandler.postAtFrontOfQueue(this);
        }
    
        public void run() {
            final int size = mMonitors.size();
            for (int i = 0 ; i < size ; i++) {
                synchronized (Watchdog.this) {
                    mCurrentMonitor = mMonitors.get(i);
                }
                //回调具体服务的monitor方法
                mCurrentMonitor.monitor();
            }
    
            synchronized (Watchdog.this) {
                mCompleted = true;
                mCurrentMonitor = null;
            }
        }
    }

    该方法主要功能:

    向Watchdog的监控线程的Looper池的最头部执行该HandlerChecker.run()方法, 在该方法中调用monitor(),执行完成后会设置mCompleted = true.

    那么当handler消息池处理当前的消息, 导致迟迟没有机会执行monitor()方法, 则会触发watchdog.

    其中postAtFrontOfQueue(this),该方法输入参数为Runnable对象,根据消息机制, 最终会回调HandlerChecker中的run方法,该方法会循环遍历所有的Monitor接口,具体的服务实现该接口的monitor()方法。

    可能的问题,如果有其他消息不断地调用postAtFrontOfQueue()也可能导致watchdog没有机会执行;或者是每个monitor消耗一些时间,累加起来超过1分钟造成的watchdog. 这些都是非常规的Watchdog.

    Watchdog处理流程

    public void run() {
        while (true) {
            synchronized (this) {
                ...
                //获取被阻塞的checkers 【见小节4.1.1】
                blockedCheckers = getBlockedCheckersLocked();
                // 获取描述信息 【见小节4.1.2】
                subject = describeCheckersLocked(blockedCheckers);
                allowRestart = mAllowRestart;
            }
    
            EventLog.writeEvent(EventLogTags.WATCHDOG, subject);
    
            ArrayList<Integer> pids = new ArrayList<Integer>();
            pids.add(Process.myPid());
            if (mPhonePid > 0) pids.add(mPhonePid);
            //第二次以追加的方式,输出system_server和3个native进程的栈信息【见小节4.2】
            final File stack = ActivityManagerService.dumpStackTraces(
                    !waitedHalf, pids, null, null, NATIVE_STACKS_OF_INTEREST);
    
            //系统已被阻塞1分钟,也不在乎多等待2s,来确保stack trace信息输出
            SystemClock.sleep(2000);
    
            if (RECORD_KERNEL_THREADS) {
                //输出kernel栈信息【见小节4.3】
                dumpKernelStackTraces();
            }
    
            //触发kernel来dump所有阻塞线程【见小节4.4】
            doSysRq('l');
    
            //输出dropbox信息【见小节4.5】
            Thread dropboxThread = new Thread("watchdogWriteToDropbox") {
                public void run() {
                    mActivity.addErrorToDropBox(
                            "watchdog", null, "system_server", null, null,
                            subject, null, stack, null);
                }
            };
            dropboxThread.start();
    
            try {
                dropboxThread.join(2000); //等待dropbox线程工作2s
            } catch (InterruptedException ignored) {
            }
    
            IActivityController controller;
            synchronized (this) {
                controller = mController;
            }
            if (controller != null) {
                //将阻塞状态报告给activity controller,
                try {
                    Binder.setDumpDisabled("Service dumps disabled due to hung system process.");
                    //返回值为1表示继续等待,-1表示杀死系统
                    int res = controller.systemNotResponding(subject);
                    if (res >= 0) {
                        waitedHalf = false;
                        continue; //设置ActivityController的某些情况下,可以让发生Watchdog时继续等待
                    }
                } catch (RemoteException e) {
                }
            }
    
            //当debugger没有attach时,才杀死进程
            if (Debug.isDebuggerConnected()) {
                debuggerWasConnected = 2;
            }
            if (debuggerWasConnected >= 2) {
                Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process");
            } else if (debuggerWasConnected > 0) {
                Slog.w(TAG, "Debugger was connected: Watchdog is *not* killing the system process");
            } else if (!allowRestart) {
                Slog.w(TAG, "Restart not allowed: Watchdog is *not* killing the system process");
            } else {
                Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + subject);
                //遍历输出阻塞线程的栈信息
                for (int i=0; i<blockedCheckers.size(); i++) {
                    Slog.w(TAG, blockedCheckers.get(i).getName() + " stack trace:");
                    StackTraceElement[] stackTrace
                            = blockedCheckers.get(i).getThread().getStackTrace();
                    for (StackTraceElement element: stackTrace) {
                        Slog.w(TAG, " at " + element);
                    }
                }
                Slog.w(TAG, "*** GOODBYE!");
                //杀死进程system_server【见小节4.6】
                Process.killProcess(Process.myPid());
                System.exit(10);
            }
            waitedHalf = false;
        }
    }

    Watchdog检测到异常的信息收集工作:

    • AMS.dumpStackTraces:输出Java和Native进程的栈信息;
    • WD.dumpKernelStackTraces:输出Kernel栈信息;
    • doSysRq
    • dropBox

    收集完信息后便会杀死system_server进程。此处allowRestart默认值为true, 当执行am hang操作则设置不允许重启(allowRestart =false), 则不会杀死system_server进程.

    将所有执行时间超过1分钟的handler线程或者monitor都记录下来.

    • 当输出的信息是Blocked in handler,意味着相应的线程处理当前消息时间超过1分钟;
    • 当输出的信息是Blocked in monitor,意味着相应的线程处理当前消息时间超过1分钟,或者monitor迟迟拿不到锁;

    对于触发watchdog时,生成的dropbox文件的tag是system_server_watchdog,内容是traces以及相应的blocked信息。

    当杀死system_server进程,从而导致zygote进程自杀,进而触发init执行重启Zygote进程,这便出现了手机framework重启的现象。

    总结

    Watchdog是一个运行在system_server进程的名为”watchdog”的线程::

    • Watchdog运作过程,当阻塞时间超过1分钟则触发一次watchdog,会杀死system_server,触发上层重启;
    • mHandlerCheckers记录所有的HandlerChecker对象的列表,包括foreground, main, ui, i/o, display线程的handler;
    • mHandlerChecker.mMonitors记录所有Watchdog目前正在监控Monitor,所有的这些monitors都运行在foreground线程。
    • 有两种方式加入Watchdog监控
      • addThread():用于监测Handler线程,默认超时时长为60s.这种超时往往是所对应的handler线程消息处理得慢;
      • addMonitor(): 用于监控实现了Watchdog.Monitor接口的服务.这种超时可能是”android.fg”线程消息处理得慢,也可能是monitor迟迟拿不到锁;

        以下情况,即使触发了Watchdog,也不会杀掉system_server进程:

    • monkey: 设置IActivityController,拦截systemNotResponding事件, 比如monkey.
    • hang: 执行am hang命令,不重启;
    • debugger: 连接debugger的情况, 不重启;
    • 对于Looper Checker而言,会判断线程的消息队列是否处于空闲状态。 如果被监测的消息队列一直闲不下来,则说明可能已经阻塞等待了很长时间

    • 对于Monitor Checker而言,会调用实现类的monitor方法,譬如上文中提到的AMS.monitor()方法, 方法实现一般很简单,就是获取当前类的对象锁,

    • 如果当前对象锁已经被持有,则monitor()会一直处于wait状态,直到超时,这种情况下,很可能是线程发生了死锁

    监控Handler线程(Looper Checker)

    Watchdog监控的线程有:

    (默认地DEFAULT_TIMEOUT=60s,调试时才为10s方便找出潜在的ANR问题)

    目前watchdog会监控system_server进程中的以上8个线程:

    • 前7个线程的Looper消息处理时间不得超过1分钟;
    • PackageManager线程的处理时间不得超过10分钟;

    监控同步锁(Monitor Checker)

    能够被Watchdog监控的系统服务都实现了Watchdog.Monitor接口,并实现其中的monitor()方法。运行在android.fg线程, 系统中实现该接口类主要有:

    • ActivityManagerService
    • WindowManagerService
    • InputManagerService
    • PowerManagerService
    • NetworkManagementService
    • MountService
    • NativeDaemonConnector
    • BinderThreadMonitor
    • MediaProjectionManagerService
    • MediaRouterService
    • MediaSessionService
    • BinderThreadMonitor

    输出信息

    watchdog在check过程中出现阻塞1分钟的情况,则会输出:

    1. AMS.dumpStackTraces:输出system_server和3个native进程的traces
      • 该方法会输出两次,第一次在超时30s的地方;第二次在超时1min;
    2. WD.dumpKernelStackTraces,输出system_server进程中所有线程的kernel stack;
      • 节点/proc/%d/task获取进程内所有的线程列表
      • 节点/proc/%d/stack获取kernel的栈
    3. doSysRq, 触发kernel来dump所有阻塞线程,输出所有CPU的backtrace到kernel log;
      • 节点/proc/sysrq-trigger
    4. dropBox,输出文件到/data/system/dropbox,内容是trace + blocked信息
    5. 杀掉system_server,进而触发zygote进程自杀,从而重启上层framewor
  • 相关阅读:
    HDU 5716 带可选字符的多字符串匹配(ShiftAnd)
    挑战程序设计竞赛 2.2 一往直前!贪心法
    再叩首
    2016 Multi-University Training Contest 1 总结
    2016 Multi-University Training Contest 2 总结
    2016 Multi-University Training Contest 3 总结
    2016 Multi-University Training Contest 4 总结
    2016 Multi-University Training Contest 5&6 总结
    2016 Multi-University Training Contest 7 总结
    2016 Multi-University Training Contest 8 总结
  • 原文地址:https://www.cnblogs.com/krislight1105/p/10180514.html
Copyright © 2020-2023  润新知