android 开机启动流程分析(14)SystemServer WatchDog解读

    xiaoxiao2022-07-13  169

    该系列文章总纲链接:专题分纲目录 android 开机启动流程分析

    本章关键点总结 & 说明:

    这里因为整体的导图太大,因此截取一部分 ,方便大家看的清楚:

    这一部分实际上使用的就是SystemServer 运行的步骤,在上图中关注➕“watchdog”部分即可。同时,下面的图是开机启动流程分析 持续迭代的效果,可放大观看。

    SS中watchDog

    看门狗最初存在的意义:早期嵌入式设备上的程序经常“跑飞”(比如说电磁干扰等),所以专门有个硬件看门狗;每隔一段时间,看门狗就去检查一下某个参数是不是被设置了,如果该参数没有被定期设置,强制重启系统。为了看几个重要的service的门,一出问题就可以kill掉它们;软件层面上Android对SystemServer对参数是否被设置也很谨慎,system_server这样就使zygote随其一起自杀,最后导致重启Java世界。 Watchdog的使用是在SystemServer->startOtherServices方法中实现,如下所示:

    ... //单例模式获取引用->new Watchdog final Watchdog watchdog = Watchdog.getInstance(); watchdog.init(context, mActivityManagerService); ... Watchdog.getInstance().start();

    接下来主要从3个方面分析问题:创建和初始化WatchDog,WatchDog的监听和注册和WatchDog运行

    1 创建和初始化Watchdog 在systemserver中会先使用watchdog的getInstance方法获取watchdog对象,构造方式如下:

    private Watchdog() { super("watchdog"); //为每个要检查的线程创建HandlerChecker,并加到mHandlerCheckers这个队列中。 //{说明:监听的几个线程均继承自ServiceThread,均为单例模式} /** FgThread负责那些常规的前台操作,它不应该被后台的操作所阻塞。 {说明:在addMonitor时会直接调用mMonitorChecker.addMonitor方法。 同时WatchDog只有mMonitorChecker上添加了监听器Monitor,监听器里实现了扫描检测锁情况的具体步骤} */ 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. //在SS中引用,该looper就是SS的,即用于SS的检测 mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()), "main thread", DEFAULT_TIMEOUT)); // Add checker for shared UI thread,IO thread,display thread. mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(), "ui thread", DEFAULT_TIMEOUT)); mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(), "i/o thread", DEFAULT_TIMEOUT)); mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(), "display thread", DEFAULT_TIMEOUT)); }

    接下来分析init的调用栈(注意:在SystemServer中进行的初始化):

    watchdog->init方法 ->context.registerReceiver(new RebootRequestReceiver(),...); {这里注册了系统重启的BroadcastReceiver,收到会触发Receiver的onReceive方法} ->RebootRequestReceiver->onReceive -->rebootSystem("Received ACTION_REBOOT broadcast"); {同时它也允许其它模块(如CTS)通过发广播来让系统重启}

    2 watchdog的注册和被监听

    @1 watchDog的addThread和addMonitor方法调用栈与意义 @@1.1 addThread的调用栈:

    addThread->mHandlerCheckers.add(new HandlerChecker(thread, name, timeoutMillis)); {即实际上只是在HandlerChecker数组中添加了一个HandlerChecker而已}

    @@1.2 addMonitor的调用栈:

    addMonitor(Monitor monitor)->mMonitorChecker.addMonitor(monitor); {调用了FgThread(mMonitorChecker)对应的HandlerChecker的addMonitor方法}

    @2 通过Watchdog在AMS的使用来分析watchdog的监听必备条件

    //首先要实现Watchdog.Monitor接口并实现接口Monitor的monitor方法 ...ActivityManagerService ...implements Watchdog.Monitor,...{ public ActivityManagerService(Context systemContext) { ... //使用addMonitor将自己添加到FgThread线程监听的监听队列中 Watchdog.getInstance().addMonitor(this); //添加一个HandlerChecker到watchdog的对应队列中去 Watchdog.getInstance().addThread(mHandler); ... } ... /** 在这里获得锁来确保我们没有死锁,实现monitor接口 只要能执行就说明没问题,如果死锁就会卡主无法返回 */ public void monitor() { synchronized (this) { } } ... }

    监听service的必要条件:实现Monitor接口,且在初始化时通过addMonitor将其加入到FgThread的监听队列中去说明:实现了Watchdog的Monitor接口的服务有:AMS、WMS、PMS、IMS、MS(MountService)、NMS(NetWorkManagerService)等

    3 watchdog运行

    watchdog运行关键代码如下所示:

    public void run() { boolean waitedHalf = false;//记录检测结果的一个变量 while (true) {//无限循环,检测运行状态 final ArrayList<HandlerChecker> blockedCheckers; ... synchronized (this) { long timeout = CHECK_INTERVAL;//每两次检测的间隔时间30s for (int i=0; i<mHandlerCheckers.size(); i++) { //获取所有的HandlerChecker HandlerChecker hc = mHandlerCheckers.get(i); /** HandlerChecker->scheduleCheckLocked ->mHandler.postAtFrontOfQueue {这里会通过handler机制执行当前run方法} ->HandlerChecker->run {这里会执行当前HandlerChecker所有的Monitors} {说明:HandlerChecker的消息传递机制是:使用scheduleCheckLocked发送消息,使用run方法来处理消息} */ hc.scheduleCheckLocked(); } ... /** 注意:我们使用uptimemillis()在这里是因为我们不想增加设备sleep时我们等待的时间。 如果设备sleep,那么我们正在等待的东西超时也sleep,不会有机会运行,造成扰乱。 */ long start = SystemClock.uptimeMillis(); while (timeout > 0) { ... wait(timeout);//等待检查的结果 ... timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start); } /** evaluateCheckerCompletionLocked {遍历所有HandlerChecker,获取每个Checker状态,从中找出最大值} {HandlerChecker结果状态有四种,COMPLETED(0),WAITING(1), WAITED_HALF(2)和OVERDUE(3) 分别代表目标Looper已处理monitor,延时<30秒,30秒<延时<60秒,延时>60秒} */ final int waitState = evaluateCheckerCompletionLocked(); if (waitState == COMPLETED) {//{即时返回} waitedHalf = false; //The monitors have returned; reset continue; } else if (waitState == WAITING) {//{delay_time<30} //仍在等待配置区间内;回来复查 continue; } else if (waitState == WAITED_HALF) {//{30<delay_time<60} if (!waitedHalf) { //我们已经等待了一半的死锁检测间隔。提取AMS堆栈跟踪并等待另一半。 waitedHalf = true; } continue; } //一般走到这里,都是delay_time>60 //对于阻塞线程的HandlerChecker,它的延迟超过60秒,导致总状态为OVERDUE。 blockedCheckers = getBlockedCheckersLocked();//获取阻塞的HandlerChecker subject = describeCheckersLocked(blockedCheckers);//打印阻塞的HandlerChecker ... } /** 能到这里,说明系统已经接近崩溃(hung},接下来收集stack trace,杀掉这些进程以便于系统重启 {日志保存:通知kernel把阻塞线程信息和backtrace打印出来,创建专门线程将信息写入到Dropbox} {判断Debugger是否连着和是否允许restart。如果没有连着debugger且允许restart,就会杀掉进程} */ ... waitedHalf = false; } }

    watchdog运行时的整个原理如下:

    创建watchdog对象->注册Receiver接收reboot消息->对HandlerChecker队列中每一个HandlerChecker,都添加monitor到其中->运行时检测各个monitor,根据monitor获取锁的延迟时间来获得一个状态->根据各类状态判断系统是否崩溃{>60就崩溃,30到60之间就再来一次,30以内就重新来,即时返回就再次循环检测}

    注意:

    Watchdog和SystemServer是同一进程,这里Watchdog kill掉了自己,也就是SystemServer。Dropbox是Android中的一套日志记录系统,作用是将系统出错信息记录下来并且保留一段时间,避免被覆盖。当出现crash, wtf, lowmem或者Watchdog被触发都会通过Dropbox来记录。

     

    最新回复(0)