Android 系统稳定性 - Watchdog

2023-12-05 09:10

本文主要是介绍Android 系统稳定性 - Watchdog,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转载:http://rayleeya.iteye.com/blog/1963408#comments

 

【原创】Android 系统稳定性 - Watchdog           

  • 博客分类:
  • Android
  • 移动开发
Android 稳定性 Watchdog 重启

文章都为原创,转载请注明出处,未经允许而盗用者追究法律责任。 

很久之前写的了,留着有点浪费,共享之(文章没有完全写完)。 

编写者:李文栋 

第3章 系统进程的Watchdog

3.1 Watchdog简介

        对于像笔者这样没玩过硬件的纯软程序员来说,第一次看到这个家伙的时候真心一头雾水,只是觉得这个名字很有意思。一番调查后发现,Watchdog机制最早来源于硬件,在计算机系统中,单片机的工作容易受到来自外界电磁场的干扰,而陷入死循环,系统无法继续工作,为了解决这个问题,便产生了一种专门用于监测单片机程序运行状态的芯片,俗称"看门狗"(Watchdog)。

 

        “看门狗”本身是一个定时器电路,内部会不断的进行计时(或计数)操作。计算机系统和“看门狗”有两个引脚相连接,正常运行时每隔一段时间就会通过其中一个引脚向“看门狗”发送信号,“看门狗”接收到信号后会将计时器清零并重新开始计时。而一旦系统出现问题,进入死循环或任何阻塞状态,不能及时发送信号让“看门狗”的计时器清零,当计时结束时,“看门狗”就会通过另一个引脚向系统发送“复位信号”,让系统重启。

       这样看来,向“看门狗”发送信号就像是“喂狗”,计时器就是“看门狗”的胃,当计时结束,狗饿了,就一口把系统咬死,让它重生。

        软件上的看门狗技术的思想和影响类似,例如Linux自带的Watchdog。下面我们来看看Android系统进程的这条小狗吧。

3.2 系统进程的Watchdog

        Android系统进程中的Watchdog(以下简称WD)自然是用来监测系统进程的,它和硬件上的WD有什么区别呢?系统进程中维护着大量的服务对象,其中有一些非常重要的对象,例如ActivityManagerService, WindowManagerService等,这些服务对象能够被正常访问对系统的运行来说至关重要,暂且称它们为关键对象。这些关键对象可能同时会被多个线程使用,所以需要在操作这些对象的地方使用同步锁将它们保护起来,确保对象状态的一致性。但是如果某个线程锁住关键对象后长时间没有释放锁,其他线程无法使用对象完成后续的任务,那么系统就会处于停滞状态无法运行,此时就需要让系统重启以恢复到一个正常的运行状态。检测这些关键服务对象是否被锁住,和重启系统的操作就是由WD完成的。

        WD是如何完成这项神圣的使命的呢?我们先来了解一下WD的创建和启动,再来剖析它的结构和流程。

3.2.1 Watchdog初始化和启动

        WD对象是一个单例,是在系统启动过程中的ServerThread线程中时创建的。

ServerThread.java → run()

Slog.i(TAG, "Init Watchdog");

Watchdog.getInstance().init(context, battery, power, alarm,

ActivityManagerService.self());

        需要注意的是WD的构造方法,其内部创建了一个HeartbeatHandler类型的对象,后面会详细介绍它,不过可以肯定的是,这个Handler实例绑定了ServerThread线程的Looper。

Watchdog.java → Watchdog()

private Watchdog() {

        super("watchdog");

        mHandler = new HeartbeatHandler();

}

        再看一下init方法具体做了什么。

Watchdog.java → init()

publicvoid init(Context context, BatteryService battery,

        PowerManagerService power, AlarmManagerService alarm,

        ActivityManagerService activity) {

        //保存了几个要用到的服务对象

        mResolver = context.getContentResolver();

        mBattery = battery;

        mPower = power;

        mAlarm = alarm;

        mActivity = activity;

 

        //注册两个BroadcastReceiver,来接收重启的消息

        context.registerReceiver(new RebootReceiver(),

        new IntentFilter(REBOOT_ACTION));

        mRebootIntent = PendingIntent.getBroadcast(context,

                0, new Intent(REBOOT_ACTION), 0);

 

        context.registerReceiver(new RebootRequestReceiver(),

        new IntentFilter(Intent.ACTION_REBOOT),

                android.Manifest.permission.REBOOT, null);

 

        mBootTime = System.currentTimeMillis(); //记录启动时间

}

        init方法很简单,涉及的内容后面会做介绍。

        完成初始化后,WD还没有启动运行。看一下WD的类声明可以知道,它是Thread类的子类,可以想到WD是在自己的线程中实现“定时器”的功能的,这很合理,要实现类似于独立硬件完成的计时工作,用独立线程完成对所在进程的监控是再好不过的。启动WD线程是在ServerThread线程的最后阶段完成的。

ActivityManagerService.self().systemReady(new Runnable() {

        publicvoid run() {

        ... ...

        Watchdog.getInstance().start();

        ... ...

        }

);

3.2.2 Watchdog结构剖析

        为了描述方便,先给出WD的类图。



 

        WD的结构还是比较简单的,下面我们分几个部分来分析。

1. Watchdog

        WD的“定时器”的功能是在单独的线程中完成的,所以WD本身继承了Thread,如前面所说,它是在ActivityManagerService的systemReady方法中启动的。

2. HeartbeatHandler

        Android系统进程的WD和硬件的“看门狗”的思想是一致的,但是实现方式上不同。WD线程在计时的过程中并不是被动的等待系统的“喂狗”信号,而是在每轮计时的开始向ServerThread(以下简称ST)线程发一个检测消息,ST接收到消息后开始遍历Monitor对象集合,尝试获取每个对象的锁,这个消息检测过程就是在HeartbeatHandler(以下简称“HH”)中实现的。需要注意的是,HH绑定的是ST线程,ST作为系统进程的主线程执行检测操作。

3. Monitor和被监控的服务对象

        WD监控的是系统进程中几个关键的服务对象,对这类对象进行抽象定义,便有了Monitor接口,它只有一个monitor方法。WD对实现了此接口的对象进行监控,其内部有一个存放Monitor对象的集合,任何对象只要实现了Monitor接口,并且通过WD的addMonitor方法注册进集合即可被监控。

        在GingerBread之前,被监控服务对象只<!-- 确认一下是否是从2.3开始的 -->有ActivityManagerService、WindowManagerService和PowerManagerService,在此之后又增加了4个,分别是NetworkManagementService、MountService、NativeDaemonConnector和InputManager。

        Monitor接口的实现方法很简单,例如AMS的实现:

ActivityManagerService.java → monitor()

publicvoid monitor() {

        synchronized (this) { }

}

        可以看到所谓的“监控服务对象”,说白了就是对这些对象进行死锁检测,如果能够顺利的获得被监控对象的锁则认为系统运行正常,如果长时间没有获得则认为系统处于停滞状态,需要采取措施了。

4. RebootReceiver和RebootRequestReceiver

 



3.2.3 Watchdog工作流程

        WD的工作流程主要就是WD线程和HH线程之间的交互,先从WD的run方法看起。

Watchdog.java → run()

publicvoid run() {

        boolean waitedHalf = false;//➀记录是否已经等待了一半

        while (true) {

        mCompleted = false;//用一个布尔变量标记是否完成死锁检测

        //➁向HH发送检测信号,它是在ST线程中执行的

        mHandler.sendEmptyMessage(MONITOR);

 

        synchronized (this) {

        //发送检测信号后会等待检测操作完成,等待时间在正常运行的情况下是30秒

        long timeout = TIME_TO_WAIT;

        long start = SystemClock.uptimeMillis();

        while (timeout > 0 && !mForceKillSystem) {

        try {

                wait(timeout);

                ... ...

        }

.        .. ...

        通过以上代码可以知道,监控过程就是一个死循环,每次循环都会做一轮死锁检测。有两个需要注意的点,说明如下:

➀ WD的这个“定时器”每轮检测的超时时间是30秒,但是30秒超时后WD并不会马上重启系统,而是将waitedHalf设置为true,认为只是等待了一半的时间,也就是说WD想多给那个被锁住的对象一次机会,做两轮检测,如果仍然超时再杀也不晚。WD还是很有人情味的,后面会看到waitedHalf何时被设置为true的。

➁ 每轮的检测操作不是由WD线程自己完成的,而是发送一个消息给HH,由HH所绑定的ST线程完成。它是怎么做的呢?接下来转到HH一探究竟。

finalclass HeartbeatHandler extends Handler {

        ... ...

        caseMONITOR: {

        ... ...

        finalint size = mMonitors.size();

        for (int i = 0 ; i < size ; i++) {

                //记录下当前正在被检测的Monitor对象,这很重要

                mCurrentMonitor = mMonitors.get(i);

                mCurrentMonitor.monitor();//检测死锁

        }

        synchronized (Watchdog.this) {

                mCompleted = true;//标记检测完成,WD线程会用此判断是否完成

                mCurrentMonitor = null;

        ... ...

        逻辑很简单,不多解释,只是请留意HH做完死锁检测后没有用notifyAll唤醒WD线程,所以正常情况下WD线程会在超时后再继续下一轮检测。HH比较会偷懒。

接下来又回到WD线程。

        ... ... //WD线程结束wait等待

        if (mCompleted && !mForceKillSystem) {

                //如果检测成功,则重置waitedHalf标记,继续下一轮检测

                waitedHalf = false;

                continue;

        }

 

        if (!waitedHalf) {

                //执行到这里,说明检测过程阻塞了,没有完成,并且waitedHalf为false,说明

                //这是死锁检测失败的第一轮检测。通过AMS将系统进程中各个线程的函数调用栈

                //输出到/data/anr/traces.txt文件中,同时也会输出几个重要的native进程的

                //backtrace,以便提供更多信息来定位问题,因为Java层的阻塞很有可能是native

                //层的阻塞造成的。

                ArrayList<Integer> pids = new ArrayList<Integer>();

                pids.add(Process.myPid());

                ActivityManagerService.dumpStackTraces(true, pids, null, null,

                        NATIVE_STACKS_OF_INTEREST);

                waitedHalf = true;//设置为true,说明WD线程等了一轮了

                continue;//再来一轮,给个机会

        }

        如果接下来的第二轮死锁检测仍然失败,则上述的代码就不会执行,继续往下走。

        //此后便是为了能够方便分析死锁原因,而输出的各种类型的日志信息

        final String name = (mCurrentMonitor != null) ?

        mCurrentMonitor.getClass().getName() : "null";

        //➀记录正在执行死锁检测的对象

        EventLog.writeEvent(EventLogTags.WATCHDOG, name);

 

        //➁再次输出系统进程的函数栈信息

        ArrayList<Integer> pids = new ArrayList<Integer>();

        pids.add(Process.myPid());

        //同时输出com.android.phone进程的函数栈,因为电话系统对于手机来说是最重要的

        //模块,自然要重点对待

        if (mPhonePid > 0) pids.add(mPhonePid);

        final File stack = ActivityManagerService.dumpStackTraces(

                !waitedHalf, pids, null, null, NATIVE_STACKS_OF_INTEREST);

        ... ...

 

        if (RECORD_KERNEL_THREADS) {

                dumpKernelStackTraces();//输出一部分Kernel的信息帮助定位问题

        }

 

        ... ...

        //➂在一个子线程中输出到DropBox中

        mActivity.addErrorToDropBox(

                "watchdog", null, "system_server", null, null,

                name, null, stack, null);

        ... ...

 

        //如果调试器没有链接则直接退出进程

        if (!Debug.isDebuggerConnected()) {

                Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + name);

                Process.killProcess(Process.myPid());

                System.exit(10);

        } else {//如果正在Debug,那你就可以断点调试了

        ... ...

有三个关键点需要注意:

➀ 记录正在执行死锁检测的对象,如果name为”null”,其实就相当于ServerThread线程还没有执行HH的handleMessage方法,就在其他地方阻塞了。所以要特别注意,如果在分析trace信息时发现没有因为被检测的关键服务对象而发生阻塞,那么就需要看看ServerThread线程的函数调用栈,确定真正的阻塞原因。

➁ 在进行系统重启前会做两轮死锁检测,第一轮会新建traces.txt文件,但第二轮会在原有文件的基础上续写,所以你会在trace信息中看到两次系统进程各个线程的函数调用栈信息。

➂ 在一个子线程中输出到DropBox中,所以如果这次保存在traces.txt中的死锁信息没有来得及查看就被覆盖了,那么可以到/data/system/dropbox目录下找到这次日志的备份。

Watchdog的实现说白了其实就是在一个线程中建立消息循环,通过Message和成员变量在线程间进行通讯,这和Handler机制的本质是一样的。

至此,WD的工作流程介绍完了,还算比较简单,在本章的最后附上了WD的流程图。接下来会介绍一些WD检测到死锁后导致重启的问题的分析方法,对于Android系统工程师来说,处理这类问题肯定是家常便饭了。

3.3 Watchdog引起的重启问题分析方法

 

3.3.1 被监测对象死锁

(待续)

3.3.2 ServerThread线程阻塞

(待续)

Watchdog的流程图


 

这篇关于Android 系统稳定性 - Watchdog的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/456967

相关文章

基于Python实现一个简单的题库与在线考试系统

《基于Python实现一个简单的题库与在线考试系统》在当今信息化教育时代,在线学习与考试系统已成为教育技术领域的重要组成部分,本文就来介绍一下如何使用Python和PyQt5框架开发一个名为白泽题库系... 目录概述功能特点界面展示系统架构设计类结构图Excel题库填写格式模板题库题目填写格式表核心数据结构

Linux系统中的firewall-offline-cmd详解(收藏版)

《Linux系统中的firewall-offline-cmd详解(收藏版)》firewall-offline-cmd是firewalld的一个命令行工具,专门设计用于在没有运行firewalld服务的... 目录主要用途基本语法选项1. 状态管理2. 区域管理3. 服务管理4. 端口管理5. ICMP 阻断

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

Windows 系统下 Nginx 的配置步骤详解

《Windows系统下Nginx的配置步骤详解》Nginx是一款功能强大的软件,在互联网领域有广泛应用,简单来说,它就像一个聪明的交通指挥员,能让网站运行得更高效、更稳定,:本文主要介绍W... 目录一、为什么要用 Nginx二、Windows 系统下 Nginx 的配置步骤1. 下载 Nginx2. 解压

如何确定哪些软件是Mac系统自带的? Mac系统内置应用查看技巧

《如何确定哪些软件是Mac系统自带的?Mac系统内置应用查看技巧》如何确定哪些软件是Mac系统自带的?mac系统中有很多自带的应用,想要看看哪些是系统自带,该怎么查看呢?下面我们就来看看Mac系统内... 在MAC电脑上,可以使用以下方法来确定哪些软件是系统自带的:1.应用程序文件夹打开应用程序文件夹

windows系统上如何进行maven安装和配置方式

《windows系统上如何进行maven安装和配置方式》:本文主要介绍windows系统上如何进行maven安装和配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录1. Maven 简介2. maven的下载与安装2.1 下载 Maven2.2 Maven安装2.

使用Python实现Windows系统垃圾清理

《使用Python实现Windows系统垃圾清理》Windows自带的磁盘清理工具功能有限,无法深度清理各类垃圾文件,所以本文为大家介绍了如何使用Python+PyQt5开发一个Windows系统垃圾... 目录一、开发背景与工具概述1.1 为什么需要专业清理工具1.2 工具设计理念二、工具核心功能解析2.

Linux系统之stress-ng测压工具的使用

《Linux系统之stress-ng测压工具的使用》:本文主要介绍Linux系统之stress-ng测压工具的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、理论1.stress工具简介与安装2.语法及参数3.具体安装二、实验1.运行8 cpu, 4 fo

ubuntu20.0.4系统中安装Anaconda的超详细图文教程

《ubuntu20.0.4系统中安装Anaconda的超详细图文教程》:本文主要介绍了在Ubuntu系统中如何下载和安装Anaconda,提供了两种方法,详细内容请阅读本文,希望能对你有所帮助... 本文介绍了在Ubuntu系统中如何下载和安装Anaconda。提供了两种方法,包括通过网页手动下载和使用wg

ubuntu系统使用官方操作命令升级Dify指南

《ubuntu系统使用官方操作命令升级Dify指南》Dify支持自动化执行、日志记录和结果管理,适用于数据处理、模型训练和部署等场景,今天我们就来看看ubuntu系统中使用官方操作命令升级Dify的方... Dify 是一个基于 docker 的工作流管理工具,旨在简化机器学习和数据科学领域的多步骤工作流。