Android SystemServer 中 WatchDog 机制介绍

一、WatchDog 简介

早期手机平台上通常是在设备中增加一个硬件看门狗,软件系统必须定时的向看门狗硬件中写值来表示自己没出故障(俗称“喂狗”),否则超过了规定的时间看门狗就会重新启动设备。

Android 的 SystemServer 是一个非常复杂的进程,里面运行的服务超过五十种,是最可能出问题的进程,因此有必要对 SystemServer 中运行的各种线程实施监控。但是如果使用硬件看门狗的工作方式,每个线程隔一段时间去喂狗,不但非常浪费CPU,而且会导致程序设计更加复杂。因此 Android 开发了 WatchDog 类作为软件看门狗来监控 SystemServer 中的线程。一旦发现问题,WatchDog 会杀死 SystemServer 进程。

Watchdog 工作原理

Watchdog 主要服务对象为 Framework 层用系统 service 和主要线程,它的工作原理是循环向被监控线程消息队列中投递 Runnable,来检查在指定时间内是否返回,如果超时不返回,则视为死锁,记录该 watchdog 记录,并做后续 dump 处理,然后 kill 掉当前 SystemServer 进程,SystemServer 的父进程 Zygote 接收到 SystemServer 的死亡信号后,会杀死自己,Zygote 进程死亡的信号传递到 Init 进程后,Init 进程会杀死 Zygote 进程所有的子进程并重启 Zygot e并上报 Framework crash,这样整个手机相当于重启一遍。通常 SystemServer 出现问题和 kernel 并没有关系,所以这种“软重启”大部分时候都能够解决问题,而且这种“软重启”的速度更快,对用户的影响也更小。

二、WatchDog 的启动

WatchDog 是在 SystemServer 进程中被初始化和启动的,在 SystemServer 的 run 方法中,各种Android 服务被注册和启动,其中也包括了WatchDog 的初始化和启动,代码如下:

final Watchdog watchdog = Watchdog.getInstance();   
watchdog.init(context, mActivityManagerService); 

在 SystemServer 中 startOtherServices() 的后半段,在 AmS 的 SystemReady 接口的 CallBack 函数中实现 WatchDog 的启动:

Watchdog.getInstance().start();

首先来看 Watchdog 的构造方法:

private Watchdog() {
    super("watchdog");
    //众多 HandlerChecker 中 mMonitorChecker 是最特殊的一个,
    //它的作用是用来检测监听的服务是否死锁 
    mMonitorChecker = new HandlerChecker(FgThread.getHandler(),
            "foreground thread", DEFAULT_TIMEOUT);
    mHandlerCheckers.add(mMonitorChecker);
    // Add checker for main thread.  We only do a quick check since there
    // can be UI running on the thread.
    mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),
            "main thread", DEFAULT_TIMEOUT));
    // Add checker for shared UI thread.
    mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),
            "ui thread", DEFAULT_TIMEOUT));
    // And also check IO thread.
    mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),
            "i/o thread", DEFAULT_TIMEOUT));
    // And the display thread.
    mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),
            "display thread", DEFAULT_TIMEOUT));

    // Initialize monitor for Binder threads.
    addMonitor(new BinderThreadMonitor());

    mOpenFdMonitor = OpenFdMonitor.create();

    // See the notes on DEFAULT_TIMEOUT.
    assert DB ||
            DEFAULT_TIMEOUT > ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;
}

三、WatchDog两个主要作用

1. 检测关键服务是否存在死锁或阻塞

关键服务通过addMonitor()方法向WatchDog注册,WatchDog通过 foreground thread的HandlerChecker回调服务实现的monitor()方法,来判断是否死锁或阻塞。

public void addMonitor(Monitor monitor) {  
    mMonitors.add(monitor);  
}

一般关键服务(如WmS等),都已经实现Watchdog.Monitor接口,并会把自己添加到watchdog的monitor列表中


@Override  
public void monitor() {  
    synchronized (mWindowMap) { }  
 }
// 把自己添加到 Watchdog monitors 中
Watchdog.getInstance().addMonitor(this);  

我们来看Watchdog中addMonitor()方法实现:

public void addMonitor(Monitor monitor) {  
    synchronized (this) {  
        if (isAlive()) {  
            throw new RuntimeException("Monitors can't be added once the Watchdog is running");  
        }  
        //最终会把自己添加到mMonitorChecker的monitor列表中
        mMonitorChecker.addMonitor(monitor);  
    }  
}  

2. 检测服务主线程是否阻塞

通过向服务主线程发送 Message 来确认,外部服务也可以通过 Watchdog.java addThread() 方法加入到检测列表中。

public void addThread(Handler thread, long timeoutMillis) {  
    synchronized (this) {  
        if (isAlive()) {  
            throw new RuntimeException("Threads can't be added once the Watchdog is running");  
        }  
        final String name = thread.getLooper().getThread().getName();
        //每个新加入的thread,WatchDog都会为其创建一个HandlerChecker对象
        mHandlerCheckers.add(new HandlerChecker(thread, name, timeoutMillis)); 
    }  
} 

四、Watchdog的工作机制

watchdog流程图.jpg

Watchdog 本身也是一个线程,我们来看它的 run() 方法:

public void run() {
    boolean waitedHalf = false;
    while (true) {
        final List<HandlerChecker> blockedCheckers;
        synchronized (this) {
            long timeout = CHECK_INTERVAL;
            // Make sure we (re)spin the checkers that have become idle within
            // this wait-and-check interval
            for (int i=0; i<mHandlerCheckers.size(); i++) {
                HandlerChecker hc = mHandlerCheckers.get(i);
                //调用每个HandlerChecker的scheduleCheckLocked() 方法
                ① hc.scheduleCheckLocked();
            }
            long start = SystemClock.uptimeMillis();
            while (timeout > 0) {
                try {
                    wait(timeout);
                }
                timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);
            }
            
            boolean fdLimitTriggered = false;
            if (mOpenFdMonitor != null) {
                fdLimitTriggered = mOpenFdMonitor.monitor();
            }
                
            if (!fdLimitTriggered) {
                ② final int waitState = evaluateCheckerCompletionLocked();
                if (waitState == COMPLETED) { //线程状态正常,重新轮询
                    // The monitors have returned; reset
                    waitedHalf = false;
                    continue;
                } else if (waitState == WAITING) {//处于阻塞状态,但监测时间小于30s,继续监测
                    // still waiting but within their configured intervals; back off and recheck
                    continue;
                 //处于阻塞状态,监测时间已经超过30s,开始dump一些系统信息,然后继续监测30s
                } else if (waitState == WAITED_HALF) {
                    if (!waitedHalf) {
                        // We've waited half the deadlock-detection interval.  Pull a stack
                        // trace and wait another half.
                        ArrayList<Integer> pids = new ArrayList<Integer>();
                        pids.add(Process.myPid());
                        waitedHalf = true;
                    }
                    continue;
                }

                // something is overdue!
                //上面三种状态都不满足,表明是OVERDUE状态:检测超时
                blockedCheckers = getBlockedCheckersLocked();
                subject = describeCheckersLocked(blockedCheckers);
            } else {
                blockedCheckers = Collections.emptyList();
                subject = "Open FD high water mark reached";
            }
            allowRestart = mAllowRestart;
        }

        Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + subject);
        //杀死系统进程
        Process.killProcess(Process.myPid());
        System.exit(10);

        waitedHalf = false;
    }
}

首先分析 scheduleCheckLocked(),它的主要作用是执行 HandlerChecker 的 run() 方法:

public void scheduleCheckLocked() {
    if (mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling()) {
        mCompleted = true;
        return;
    }

    if (!mCompleted) {
        // we already have a check in flight, so no need
        return;
    }
    //每次重新检测handlerchecker对象时,都会把mCompleted置为false,等检测发现状态正常时会再把它置为true
    mCompleted = false;
    mCurrentMonitor = null;
    //这个变量很关键,设置每个HandlerChecker对象检测的开始时间,后面计算时间时会用到
    mStartTime = SystemClock.uptimeMillis();
    //在相应的handler中执行HandlerChecker的run()方法 
    mHandler.postAtFrontOfQueue(this);
}

HandlerChecker 的 run() 方法,在每一个检测周期中 watchdog 会使用
foreground thread 的 HandlerChecker 回调服务注册的 monitor() 方法给服务的关键区上锁并马上释放,以检测关键区是否存在死锁或阻塞;

@Override
public void run() {
    final int size = mMonitors.size();
    // for循环用来检测监听列表中是否有阻塞,而且只有mMonitorChecker会走进此循环,
    //其余的handlerChecker因为mMonitors为空,都不会执行此循环
    for (int i = 0 ; i < size ; i++) {
        synchronized (Watchdog.this) {
            mCurrentMonitor = mMonitors.get(i);
        }
        mCurrentMonitor.monitor();
    }

    synchronized (Watchdog.this) {
        mCompleted = true;
        mCurrentMonitor = null;
    }
}

再来看 evaluateCheckerCompletionLocked() 方法,主要作用是等待 30s 后,开始检测 HandlerChecker 的状态。

WatchDog 检测是以 HandlerChecker 对象为单位,它的状态分为四种:

State Function
COMPLETED 检测对象运行正常
WAITING 检测不到 30s,不算超时,继续监听
WAITED_HALF 检测已超时 30s,开始 dump 关键信息,但还会继续监听 30s
OVERDUE 检测超时,准备 kill 当前进程
private int evaluateCheckerCompletionLocked() {
    int state = COMPLETED;
    for (int i=0; i<mHandlerCheckers.size(); i++) {
        HandlerChecker hc = mHandlerCheckers.get(i);
        //调用 getCompletionStateLocked() 获取当前 state
        state = Math.max(state, hc.getCompletionStateLocked());
    }
    return state;
}

//获得当前HandlerChecker的状态,注意每个HandlerChecker的mStartTime都不相同
public int getCompletionStateLocked() {
    if (mCompleted) {
        return COMPLETED;
    } else {
        long latency = SystemClock.uptimeMillis() - mStartTime;
        if (latency < mWaitMax/2) {
            return WAITING;
        } else if (latency < mWaitMax) { //注意:这里的 mWaitMax  = 60s
            return WAITED_HALF;
        }
    }
    return OVERDUE;
}

最后分析 getBlockedCheckersLocked(),获得阻塞或死锁的线程信息

private ArrayList<HandlerChecker> getBlockedCheckersLocked() {
    ArrayList<HandlerChecker> checkers = new ArrayList<HandlerChecker>();
    for (int i=0; i<mHandlerCheckers.size(); i++) {
        HandlerChecker hc = mHandlerCheckers.get(i);
        if (hc.isOverdueLocked()) {
            checkers.add(hc);
        }
    }
    return checkers;
}

打印阻塞或死锁线程的信息:

private String describeCheckersLocked(List<HandlerChecker> checkers) {
    StringBuilder builder = new StringBuilder(128);
    for (int i=0; i<checkers.size(); i++) {
        if (builder.length() > 0) {
            builder.append(", ");
        }
        builder.append(checkers.get(i).describeBlockedStateLocked());
    }
    return builder.toString();
}

public String describeBlockedStateLocked() {
    // mCurrentMonitor == null 表示出现阻塞
    if (mCurrentMonitor == null) {
        return "Blocked in handler on " + mName + " (" + getThread().getName() + ")";
    } else {
        // mCurrentMonitor == null 表示出现死锁
        return "Blocked in monitor " + mCurrentMonitor.getClass().getName()
                + " on " + mName + " (" + getThread().getName() + ")";
    }
}

注意通过 monitor() 方法检查死锁针对不同线程之间的,而服务主线程是否阻塞是针对主线程,所以通过 sendMessage() 方式是只能检测主线程是否阻塞,而不能检测是否死锁,因为如果服务主线程和另外一个线程发生死锁(如另外一个线程synchronized 关键字长时间持有某个锁,不释放),此时向主线程发送 Message,主线程的Handler是可以继续处理的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 有些东西天天接触,也不见得就喜欢,有些东西许久不碰,也不见得就不喜欢。 武侠对我来说,属于后者。 初中的时候住...
    射手座兔夫人阅读 285评论 0 4
  • 当初从杭城来到宁城,临时起意,只做了一天的决定,就回来了。 花了五年的时间下定决心要在杭城发展,但一个转折点,让我...
    寓言兮阅读 201评论 0 0
  • Cookie是什么(还有session)? cookie 和 session 都是用来储存一些 浏览器用户的信息,...
    CoTe白夜阅读 3,002评论 0 3