systemserver进程监控者--watchdog

news2024/11/17 0:38:47

戳蓝字“牛晓伟”关注我哦!

用心坚持输出易读、有趣、有深度、高质量、体系化的技术文章,技术文章也可以有温度。

本文摘要

本文同样采用自述的方式来介绍systemserver进程的监控者watchdog,通过本文您将了解watchdog的作用,它是如何工作的。(文中代码基于Android13)

本文大纲

1. 我是谁

2. 我的启动

3. 我的工作原理

4. 总结

1. 我是谁

我的名字叫watchdog,翻译为中文是看门狗,说实话我不喜欢这个名字,大家叫我进程监控者吧,这名字才符合我做的事情,我运行在一个单独的线程同时我也是单例,也就是在systemserver进程只有我一个实例,谁要是想使用我就调用我的 getInstance 方法。

既然是进程监控者,那我监控的是systemserver进程,说的更具体点我会定时监控systemserver进程内具有Handler的线程是否存在耗时方法是否存在死锁是否存在长时间持有锁 的情况。这里可要明确一点并不是说我会检测systemserver进程中所有的具有 Handler 的线程和所有的锁,而是哪些线程和锁需要我监控的话“告知”我,我才会去监控它们。我监控的可并不是只有一个线程或者锁,我可是监控着很多的线程和锁。

如果检测到有耗时方法/死锁/长时间持有锁的情况,那我就会把systemserver进程杀掉zygote进程检测到systemserver进程死掉的话也会“自杀”,init进程会重新启动zygote进程,zygote进程会启动systemserver进程。

或许你们会觉得我做的事情微不足道,但是我做的事情对于systemserver进程的稳定性来说那是至关重要的。如果没有我会发生什么情况呢?比如ActivityManagerService内的代码出现了死锁,那有可能从用户感官上来看就是整个界面会出现卡死不动的现象,因为是死锁导致的,死锁是无解的,那如果没有我整个界面就一直处于卡死不动的状态,用户只能强制重新启动设备。

接下来看下我是如何启动的吧。

2. 我的启动

在介绍systemserver进程的时候提到过启动各种 service 分为启动 bootstrap services、启动 core services、启动 other services 这三个阶段,因为我确实非常的重要也确实会被各种 service 提前使用到,因此会在第一阶段第一个启动我。

我运行在一个单独的线程,启动我后我的 run 方法就开始执行,至此我就启动成功了。我的启动是不是非常简单,接下来给大家展示下我的工作原理这个重头戏吧。

下面是相关代码,有兴趣自行取阅:

//SystemServer类
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
  //Watchdog是单例,因此调用getInstance方法获取它的实例,该方法在下面
  final Watchdog watchdog = Watchdog.getInstance();
  //调用它的start方法,该方法在下面
  watchdog.start();
  
  省略代码······
  
  watchdog.init(mSystemContext, mActivityManagerService); 

  省略代码······
}

//Watchdog类
public static Watchdog getInstance() {
  if (sWatchdog == null) {
     sWatchdog = new Watchdog();
  }

  return sWatchdog;
}

//私有构造方法
private Watchdog() {
  //new 一个线程
  mThread = new Thread(this::run, "watchdog");
  
  省略代码······
  
}

//start方法
public void start() {
  //调用线程的start方法
  mThread.start();
}

3. 我的工作原理

老规矩还是先看一幅图,下图展示了我的工作原理:

结合上面的图文用文字来总结下我的工作原理:

  1. 遍历所有的 HandlerChecker,并依次执行每个 HandlerChecker 的 scheduleCheckLocked 方法。

  2. 等待 timeout 时间后

  3. 检查所有的 HandlerChecker 的状态 (它们的状态有 COMPLETED、WAITING、WAITED_HALF、OVERDUE四种)

    3.1 若没有 OVERDUE 状态的 HandlerChecker 则会继续从第 1 步开始重新执行

    3.2 若有 OVERDUE 状态的 HandlerChecker,则会进行超时处理,比如收集日志,杀掉systemserver进程

我的工作原理是不是特别的简单,就是定时的重复执行上面的操作,除非检查到了超时的 HandlerChecker 才会退出。大家是不是对 HandlerChecker 不熟悉啊,那我就先来介绍下它,再来细细梳理下上面流程。

3.1 HandlerChecker是啥?

HandlerChecker的作用是监控具有Handler的线程是否有耗时方法 或者 是否有死锁或长时间持锁的情况。HandlerChecker 会对应一个 Handler,它也有自己的 timeout (超时时间),下面是它的类图

HandlerChecker 它实现了 Runnable的主要原因是它可以被 Handler 通过 post 的方式被执行,接下来下看下 HandlerChecker 的关键属性。

3.1.1 HandlerChecker 的关键属性

mHander

该属性是 Handler 类型的,一个 HandlerChecker 对应一个 Handler,HandlerChecker 其实就是监控 mHander 对应的线程是否存在耗时方法

mWaitMax

该属性的作用相当于 timeout,也就是每个被监控的 Handler 都会对应自己的超时时间,若执行时间超过 mWaitMax 则认为当前的 Handler 对应的线程存在耗时方法或者有死锁或者长时间持有锁的情况

mCompleted

是一个 boolean 值,为 false 则代表当前的 Handler 对应的线程存在耗时方法或者有死锁或者长时间持有锁的情况

mStartTime

代表执行的开始时间

mPauseCount

大于0则代表当前的 HandlerChecker 暂停工作

mMonitors

它是一个 Monitor 的集合,Monitor 主要作用是用来监控锁,也就是一个 HandlerChecker 是可以监控很多锁的。

3.1.2 HandlerChecker 的关键方法

scheduleCheckLocked

该方法是 HandlerChecker 开始执行的入口方法,该方法会有一个参数 handlerCheckerTimeoutMillis,该参数的作用就是告诉 HandlerChecker 的超时时间是多少。

getCompletionStateLocked

该方法的作用是返回执行状态, 主要有 COMPLETED、WAITING、WAITED_HALF、OVERDUE 四个状态。COMPLETED 代表已经执行完毕,WAITING 代表执行时间还没超过 timeout 的一半,WAITED_HALF 代表执行时间超过 timeout 的一半,OVERDUE 代表执行时间已经超过 timeout。

看了 HandlerChecker 的类信息后,那来看下 HandlerChecker 是如何做到监控的。

3.1.3 HandlerChecker 如何监控

老规矩还是来看一张图

图解

  1. 先把 mComplete 置为 false,在调用 mHander 的 postAtFrontOfQueue 方法,参数为当前 HandlerChecker 对象 (postAtFrontOfQueue 方法会把 Message 放入 Handler 对应的 MessageQueue 的最前面,保证它会被优先执行)

  2. 如果 run 方法被顺利执行,会遍历 mMonitors,并且执行每个 Monitor 的 monitor 方法,若所有的 Monitor都被顺利执行,则会把 mComplete 置为 true 代表执行完毕,不存在耗时方法和死锁情况。

Watchdog会在等待一段时间后,获取当前 HandlerChecker 的状态,如果状态为 OVERDUE,则代表超时。
HandlerChecker 若出现超时,则大致有以下几个原因:

  1. HandlerChecker 对应的 run 方法没有被执行,主要是因为 mHander 对应的 Looper 中正在被执行的 Message 确实出现了耗时情况

  2. mMonitors 中有些 Monitor 的 monitor 方法出现了超时情况

3.1.4 添加被监控的 Handler

systemserver进程中如果有 Handler 被监控的话,可以调用WatchdogaddThread 方法,在Watchdog的构造方法中已经有部分 Handler 被添加了,如下代码:

//Watchdog 类的构造方法
private Watchdog() {
        省略代码······
        //它主要用来监控各种锁
        mMonitorChecker = new HandlerChecker(FgThread.getHandler(),
                "foreground thread");
        mHandlerCheckers.add(withDefaultTimeout(mMonitorChecker));
        
        //监控systemserver的主线程的Handler
        mHandlerCheckers.add(withDefaultTimeout(
                new HandlerChecker(new Handler(Looper.getMainLooper()), "main thread")));
        //UiThread主要用来显示UI,监控它的 Handler
        mHandlerCheckers.add(withDefaultTimeout(
                new HandlerChecker(UiThread.getHandler(), "ui thread")));
        //IoThread主要用来io操作,监控它的Handler
        mHandlerCheckers.add(withDefaultTimeout(
                new HandlerChecker(IoThread.getHandler(), "i/o thread")));
        //DisplayThread主要与显示有关
        mHandlerCheckers.add(withDefaultTimeout(
                new HandlerChecker(DisplayThread.getHandler(), "display thread")));
        // AnimationThread动画相关
        mHandlerCheckers.add(withDefaultTimeout(
                 new HandlerChecker(AnimationThread.getHandler(), "animation thread")));
       省略代码······
    }

3.1.5 添加被监控的锁

systemserver进程中的锁如果想要被监控的话,可以调用WatchdogaddMonitor 方法,添加非常的简单,下面用 ActivityManagerService 来演示如何添加的

//ActivityManagerService类,它实现了Watchdog.Monitor接口
public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback, ActivityManagerGlobalLock {
  public ActivityManagerService(Context systemContext, ActivityTaskManagerService atm) {
    省略代码······
    Watchdog.getInstance().addMonitor(this);
    省略代码······
  }

  public void monitor() {
    //获取到锁后,不执行任何代码,立即释放锁;若获取不到锁会一直阻塞
    synchronized (this) { } 
  }
}

其实也已经有很多的锁被添加进来了,如 ActivityManagerService、PowerManagerService、binder线程的锁 (处理任务的binder线程数比binder启动的最大binder线程数大的话会处于阻塞状态,否则不阻塞)

3.1.6 小结

HandlerChecker 会对应一个 Handler 和 零个或多个 Monitor,Monitor 的作用是监控是否死锁或者长时间持有锁,可以调用Watchdog的 addTHread 方法和 addMonitor 方法把需要被监控的 Handler 和锁进行监控,systemserver中已经有很多的 Handler 和锁被监控了。关于 HandlerChecker 介绍到此,那在回过头来重新梳理下我的工作原理

3.2 再来看 scheduleCheckLocked 方法

Watchdog会遍历所有的 HandlerChecker,并且执行它的 scheduleCheckLocked 方法,该方法的作用其实就是告诉每个 HandlerChecker 可以去执行监控逻辑了,在执行之前每个 HandlerChecker 的状态也有所区别,比如有的 HandlerChecker 已经在执行中了,有的已经执行完毕了,有的可以先暂时暂停等。

如下是相关代码,自行取阅:

//Watchdog类
public void scheduleCheckLocked(long handlerCheckerTimeoutMillis) {
    //超时时间
    mWaitMax = handlerCheckerTimeoutMillis;
    
    //为true,则把mMonitorQueue添加到mMonitors
    if (mCompleted) {
      mMonitors.addAll(mMonitorQueue);
      mMonitorQueue.clear();
    }
    //mPauseCount 大于0 说明需要暂停
    if ((mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling())
                    || (mPauseCount > 0)) { 
      //暂停则把 mCompleted 置为true
      mCompleted = true;
      return;
    }
    //还在运行,则直接返回
    if (!mCompleted) {
      // we already have a check in flight, so no need
      return;
    }
            
    //重置各种信息
    mCompleted = false;
    mCurrentMonitor = null;
    mStartTime = SystemClock.uptimeMillis();
    //放入MessageQueue的最前面,保证被优先执行
    mHandler.postAtFrontOfQueue(this); 
}

3.3 检查所有的 HandlerChecker 状态

Watchdog在等待一段时间后 (wait(timeout)),会从所有的 HandlerChecker 中选取最大的状态值,如下图:

每个 HandlerChecker 都会根据自己的执行状态和执行时间返回自己的状态值,状态值有 COMPLETED、WAITING、WAITED_HALF、OVERDUE 四种,它们都是整数类型的,它们的值是从小到大,也就时候 COMPLETED < WAITING < WAITED_HALF < OVERDUE,为啥要设置这几个状态值呢?

其主要作用是每个 HandlerChecker 的 timeout 超时时间有可能不一样,HandlerChecker 对应的 Handler 的线程执行的方法需要的时间也不一样,因此我Watchdog需要知道每个 HandlerChecker 的执行状态,到底执行了多久,还有多久没执行完毕,这样我对于每个 HandlerChecker 心里有底了。

从所有的 HandlerChecker 中获取到最大的状态值有如下几种情况:

  1. 若是 WAITED_HALF,则代表该 HandlerChecker 已经执行了多余 timeout 一半的时间,该 HandlerChecker 有可能会出现超时,因此这时候会收集日志,不会跳出循环

  2. 若是 OVERDUE,则有 HandlerChecker 出现了超时,则会进行超时处理,跳出循环

  3. 若是 COMPLETED 和 WAITING,则会重新执行,不会跳出循环

3.4 超时处理

检测到有超时的 HandlerChecker 会走到这步,超时处理其实主要做了以下几个工作:

  1. 收集日志
  2. 把超时的 HandlerChecker 的堆栈信息收集起来
  3. 杀掉SystemServer进程

zygote进程检测到systemserver进程死掉的话也会“自杀”,init进程会重新启动zygote进程,zygote进程会启动systemserver进程,进而启动launcher

4. 总结

我是Watchdog,为了systemserver进程的稳定性,我会定时监控具有Handler线程是否有耗时方法是否出现了死锁或者长时间持有锁的情况,若监控到以上这些情况,我会把systemserver进程杀掉,导致整个系统重新启动;若没有发现异常情况,则会循环进行监控。

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2076199.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【机器学习】梯度下降算法

梯度下降算法 这篇博客更加详细&#xff0c;以下只是我个人的理解 梯度下降算法原理讲解——机器学习-CSDN博客 梯度下降算法是一种优化算法&#xff0c;通过梯度下降找到函数最小值时的自变量值。 其基本思想是沿着梯度方向的反方向更新参数&#xff0c;直到逼近函数的极值…

【LLM大模型】生成式人工智能大型语言模型的安全性:概述

生成性AI大型语言模型&#xff08;LLMs&#xff09;的安全性&#xff1a;概述 具有生成性AI能力的大型语言模型&#xff08;如ChatGPT&#xff09;正面临加速采用和创新。生成性AI&#xff08;GAI&#xff09;的日益普及不可避免地引发了对这些模型相关风险和安全性的担忧。本…

Linux 下 gdb 的使用

目录 一、概述1、安装和启动 GDB 二、 GDB 常用命令1、查看源码2、断点2.1 设置断点2.2 查看断点信息2.3 删除断点2.4 激活/禁用断点2.5 观察断点2.6 捕获信号2.7 线程中断 3、查看信息3.1 查看数据3.2 查看内存3.3 查看栈信息3.4 查看栈帧信息 4、运行、调试5、编辑和搜索 一、…

MacOS安装 Python 和 PyCharm

MacOS安装 Python3.12.5 和 PyCharm 小阿呜有话说一、MacOS安装PythonPython官网下载 二、MacOS安装PyCharmPyCharm官网下载 叮嘟&#xff01;这里是小啊呜的学习课程资料整理。好记性不如烂笔头&#xff0c;今天也是努力进步的一天。一起加油进阶吧&#xff01; 小阿呜有话说 …

发完朋友圈就“退款”?黑神话的玩家是否都是“忠实粉丝”?

​声明&#xff1a;此篇为 ai123.cn 原创文章&#xff0c;转载请标明出处链接&#xff1a;https://ai123.cn/2228.html 《黑神话&#xff1a;悟空》自上线以来&#xff0c;便引发了玩家社区的广泛讨论。游戏的退款现象主要受到了一些技术问题和个人体验差异的影响。部分玩家因遇…

容器的ip地址不稳定问题、联合文件系统、核对时间、制作基础镜像

在docker中部署线上考试系统 1、部署前端服务器 # 上传本地下载的dist文件&#xff0c;因为上传的是目录&#xff0c;加-r选项 scp -r D:\云计算\压缩包\项目\dist root192.168.2.50:/root/ # 创建基础容器 [rootdocker ~]# docker run -it --name c0 centos:latest /bi…

SolidityFoundry BitMap

写合约的时候&#xff0c;记录某个账户的bool状态很常见&#xff0c;例如是否领取空投等&#xff0c;传统的写法mapping(uint256>bool)中一个slot只能存储一个账户的信息&#xff0c;在其他语言中&#xff0c;我们经常会用到bitmap来表示标志位&#xff0c;如果我们可以将bi…

银行卡三要素验证如何用Java进行调用

一、什么是银行卡三要素验证&#xff1f; 银行卡三要素验证又叫银行卡三要素核验、银行卡三要素校验、银行卡实名认证、银行卡三元素验证&#xff0c;即输入银行卡卡号、姓名、身份证号码&#xff0c;验证此三要素是否一致&#xff0c;该接口支持所有带银联标识的银行卡。 二…

langchain入门系列之五 初探代理

代理的核心思想是使用LLM来选择要采取的一系列动作。 在链式结构中&#xff0c;一系列动作是硬编码的&#xff08;在代码中&#xff09;。 在代理中&#xff0c;使用语言模型作为推理引擎来确定要采取的动作及其顺序。 代理 这是负责决定下一步采取什么动作的类。 这是由语言…

lidar3607.2 lidar360mls7.2 强大的雷达点云数据处理应用软件

1、LiDAR360是一款强大的激光雷达点云数据处理和分析平台&#xff0c;拥有超过10种先进的点云数据处理算法&#xff0c;可同时处理超过300G点云数据。平台包含丰富的编辑工具和自动航带拼接功能&#xff0c;可为地形、林业、矿山和电力行业&#xff08;参考LiPowerline软件&…

【HarmonyOS 4.0】@BuilderParam 装饰器

1. BuilderParam 装饰器 BuilderParam 装饰器用于装饰自定义组件(struct)中的属性&#xff0c;其装饰的属性可作为一个UI结构的占位符&#xff0c;待创建该组件时&#xff0c;可通过参数为其传入具体的内容。参数必须满足俩个条件&#xff1a; 2.1 参数类型必须是个函数&#x…

前端使用canvas绘制简单工作流-react

效果图如下&#xff1a; 目前只做了绘制部分&#xff0c;绘制方式也比较简单&#xff0c;点击工具栏中需要绘制的图形&#xff0c;在画布上左键点击将会绘制一个图形出来&#xff0c;工具栏选中第一个&#xff0c;再点击其他图像&#xff0c;长按鼠标左键可以移动&#xff0c;删…

丢掉Beyond Compare吧!新款文件差异对比工具WinMerge更具性价比!

今天想和大家分享一款非常实用的免费开源文件比较工具&#xff1a;WinMerge。 作为一名长期从事互联网行业的人&#xff0c;我经常需要处理大量的文档和代码文件&#xff0c;文件对比工具在我的日常工作中可谓是必不可少的“左膀右臂”。 也相信很多朋友在处理多个文档内容或者…

96页PPT集团战略解码会工具与操作流程

德勤集团在战略解码过程中通常会用到以下一些具体工具&#xff1a; 一、平衡计分卡&#xff08;Balanced Scorecard&#xff09; 财务维度&#xff1a; 明确关键财务指标&#xff0c;如营业收入、利润、投资回报率等。你可以通过分析历史财务数据和行业趋势&#xff0c;确定…

HUSB381A:带线PD适配器的绝佳选择

HUSB381A是慧能泰半导体全新推出的一款采用SOP8封装&#xff0c;集成MOS的USB PD Source芯片&#xff0c;带CC1和CC2引脚&#xff0c;支持不可分离线缆&#xff08;Captive Cable&#xff09;PD适配器和纯PD快充充电器应用。HUSB381A支持最大功率20V5A 100W应用&#xff0c;支持…

单片机驱动彩屏最简方案:单片机_RA8889最小开发板驱动控制TFT彩屏介绍(一)方案架构

本文介绍使用单片机RA8889来驱动和控制彩屏的最小方案。文章从RA8889的架构功能、硬件电路设计及软件设计三个方面来说明。 小编已发布多篇文章介绍了单片机RA8889来驱动控制彩屏&#xff0c;但是仍有不少单片机玩家可能对驱动彩屏还不算熟悉&#xff0c;在此加推一个短篇介绍…

审计发现 FBI 的数据存储管理存在重大漏洞

据The Hacker News消息&#xff0c;美国司法部监察长办公室 &#xff08;OIG&#xff09; 的一项审计发现&#xff0c; FBI 在库存管理和处置涉及机密数据的电子存储媒体方面存在“重大漏洞”。 OIG 的审计显示&#xff0c;FBI 对包含敏感但未分类 &#xff08;SBU&#xff09…

橙子投屏,轻松连接大屏幕

对于某腾、某爱、某酷投屏大家在熟悉不过了吧&#xff0c;一款非常好用的投屏软件&#xff0c;但是使用起来还是限制颇多&#xff0c;比如有犷郜&#xff0c;还必须同步使用手机App才能实现投屏功能&#xff0c;关键还得开会员&#xff0c;劝退不少小伙伴。但是现在手机往往占据…

API代理指南:跨境业务的数据桥梁

在当今全球化的经济环境中&#xff0c;跨境业务已成为企业拓展国际市场、实现业务增长的重要途径。但面临着法律法规差异、网络复杂性和数据安全等诸多挑战。为了有效应对这些挑战&#xff0c;API&#xff08;应用程序编程接口&#xff09;代理成为了跨境业务中不可或缺的一部分…

接口自动化框架设计必备利器之参数传递

在我们设计自动化测试框架的时候&#xff0c;我们会经常将测试数据保存在外部的文件&#xff08;如Excel、YAML&#xff09;中&#xff0c;实现测试脚本与测试数据解耦&#xff0c;方便后期维护。 当涉及到业务场景接口用例时&#xff0c;由于接口与接口存在关联关系&#xff…