1 watchdog分析

Watchdog就是“看门狗”。其最初存在的意义是因为以前嵌入式设备上的程序经常跑飞(电磁干扰之类的),所以专门设置了一个硬件看门狗,每个一段时间,看门狗就去检查一下某个参数是否被设置了,如果发现该参数没有被设置,则判断为系统出错,然后就会强制重启。

在软件层面上,Android对Systemserver的参数是否被设置也很谨慎,所以专门为它增加了一条看门狗,它主要看几个重要service的门。一旦发现service出现问题,就会kill system_server进程,进而导致zygote进程自杀,最后导致java世界重启。

我们先把systemServer使用watchdog的调用流程总结一下,然后以此为切入点来分析watchdog。SS和watchdog的交互流程可以总结如下:

①watchdog.getInstance.Init()

②watchdog.getInstance.start()

③watchdog.getInstance.addMonitor()

1.1 创建和初始化watchdog

GetInstance用于创建watchdog,代码如下:

public static Watchdog getInstance() {

if (sWatchdog == null) {

sWatchdog = new Watchdog(); //使用了单实例模式。

}

return sWatchdog;

}

//构造函数

private Watchdog() {

super("watchdog");

/*为每一个我们想检查的普通线程初始化一个handler检测器。需要注意的是:我们并不马上检查后台线程,因为这样可能会执行更长的操作且无法保证这些操作的耗时是合适的。

我们大家共享的前台线程是主要的检查者,同时我们也会派遣监视器进行检查并做一些其他的工作。

Handler是系统消息处理的地方,会在后面进行详细的分析*/

mMonitorChecker = new HandlerChecker(FgThread.getHandler(),

"foreground thread", DEFAULT_TIMEOUT);

mHandlerCheckers.add(mMonitorChecker);

//为主线程添加一个checker,鉴于可能会是一个UI正运行在主线程中,所以我们只在这里做一个快速的检查

mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),

"main thread", DEFAULT_TIMEOUT));

// 为共享的UI线程添加一个checker

mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),

"ui thread", DEFAULT_TIMEOUT));

// 同时为io线程添加一个checker

mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),

"i/o thread", DEFAULT_TIMEOUT));

}

在看门狗诞生后,再来看看init函数:

public void init(Context context, BatteryService battery,

PowerManagerService power, AlarmManagerService alarm,

ActivityManagerService activity) {

mResolver = context.getContentResolver();

mBattery = battery;

mPower = power;

mAlarm = alarm;

mActivity = activity;

/*注册一个广播接受者,这个接受者只接收ACTION标记为REBOOT的广播,并且这个接受者拥有重启系统的权限*/

context.registerReceiver(new RebootRequestReceiver(),

new IntentFilter(Intent.ACTION_REBOOT),

android.Manifest.permission.REBOOT, null);

}

1.2 watchdog跑起来

Systemserver调用watchdog的start函数,这将导致watchdog的run函数在另一个线程中被执行:

@Override

public void run() {

boolean waitedHalf = false;

while (true) {//外层while循环

final ArrayList<HandlerChecker> blockedCheckers;

final String subject;

final boolean allowRestart;

synchronized (this) {

long timeout = CHECK_INTERVAL; // 检查的间隔时间,=2500

//对每一个checker进行轮询,scheduleCheckLocked会调用handler的postAtFrontOfQueue(Runnable r)函数,这个函数发送的消息会在另一个线程中得到处理,该线程触发 monitor去检查server的状态(是否死锁).

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

HandlerChecker hc = mHandlerCheckers.get(i);

hc.scheduleCheckLocked();

}

long start = SystemClock.uptimeMillis(); //检查的开始时间

//在CHECK_INTERVAL时间内循环等待检查结果

while (timeout > 0) {

try {

wait(timeout); //等待检查的结果

} catch (InterruptedException e) {

Log.wtf(TAG, e);

}

timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);

}

/* waitState共有四种状态:COMPLETED = 0;WAITING = 1;WAITED_HALF = 2;

OVERDUE = 3; evaluateCheckerCompletionLocked函数的作用是:遍历所有的checkers,返回这些checkers中最大的State值——值越大就说明该checker检查到的状态越严重*/

final int waitState = evaluateCheckerCompletionLocked();

if (waitState == COMPLETED) {

// 表明经过所有检查后,发现一切service正常,本轮检查后,系统不处于waitedHalf状态

waitedHalf = false;

continue;

} else if (waitState == WAITING) {

// 有某些checker还在它们的间隔时间内等待检查结果,那么就直接返回,重新检查,waitedHalf状态值不改变

continue;

} else if (waitState == WAITED_HALF) {//已经有某些个checker处于WAITED_HALF状态了

if (!waitedHalf) { //如果当前总的检查状态并不处于waitedHalf状态,那么就pull一个栈跟踪到ActivityManagerService中,并将waitedHalf状态置为真,然后继续检查;否则直接继续检查

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

pids.add(Process.myPid());

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

NATIVE_STACKS_OF_INTEREST);

waitedHalf = true;

}

continue;

}

//说明watchdog已经处于OVERDUE状态了,就可能需要重启了

blockedCheckers = getBlockedCheckersLocked();

subject = describeCheckersLocked(blockedCheckers);

allowRestart = mAllowRestart;

}

/*代码运行到这里就说明watchdog已经处于OVERDUE状态了(即系统极可能已经挂起了~),那么我们首先收集SS进程中所有线程的栈跟踪信息,然后杀掉SS进程,这样就会导致系统重启了~*/

EventLog.writeEvent(EventLogTags.WATCHDOG, subject);

……..

if (controller != null) { //如果第一次检查的时候发现controller可以处理这种情况,那么就不需要重启,重新检查即可

Slog.i(TAG, "Reporting stuck state to activity controller");

try {

Binder.setDumpDisabled("Service dumps disabled due to hung system process.");

// 1 = keep waiting, -1 = kill system

int res = controller.systemNotResponding(subject);

if (res >= 0) {

Slog.i(TAG, "Activity controller requested to coninue to wait");

waitedHalf = false;

continue;

}

} catch (RemoteException e) {

}

}

// 已经检查过两次了,还是有问题,那么说明这次是真的有问题了,所以SS就自杀

if (Debug.isDebuggerConnected()) {

Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process");

}

……..

Process.killProcess(Process.myPid());

System.exit(10);

}

//检查发现没有问题了,就将waitedHalf值置为假

waitedHalf = false;

}

}

总结:watchdog隔一段时间就会向另一个线程的消息队列头发送一条消息,那个线程将检查各个service的状况。而watchdog会等待检查的结果,如果第二次还是没有正确的返回结果,那么就杀掉SS。

1.3 列队检查

这么多service,哪些是watchdog比较关注的呢?一共有3个:

①activityManagerService

②powerManagerService

③windowManagerService

要想支持watchdog的检查,就需要让这些service实现monitor接口,然后watchdog就会调用它们的monitor函数进行检查了。检查的地方是在watchdog::HandlerChecker类的run中:

public void run() {

// mMonitors是所有monitor的集合

final int size = mMonitors.size();

//检查各个service

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

synchronized (Watchdog.this) {

mCurrentMonitor = mMonitors.get(i);

}

mCurrentMonitor.monitor(); //调用该service的monitor函数

}

synchronized (Watchdog.this) {

//如果没有问题,就设置mCompleted为真

mCompleted = true;

mCurrentMonitor = null;

}

}

那么,service是如何判断自己的健康状况的呢?我们以ActivityManagerService为例,先看看它是如何把自己交给watchdog检查的:

①在其构造函数中有:

Watchdog.getInstance().addMonitor(this); //把自己加入到watchdog的检查队列中

②ActivityManagerService自己重载的monitoe函数:

public void monitor() { //执行死锁检查

synchronized (this) { }

}

原来,watchdog最怕系统服务死锁,对于这种情况也只能采取杀系统的方法了~

1.4总结

其实watchdog是周期性地调用需要进行状态检查的服务自己的monitor函数来判断该服务是否死锁。然后根据返回结果来判断系统是否需要重启。

android2.2 watchdog分析的更多相关文章

  1. OpenFaaS实战之五:大话watchdog

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  2. [深入理解Android卷一全文-第四章]深入理解zygote

    由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该由于纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的所有内容. ...

  3. android -- WatchDog看门狗分析

    在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞,而陷入死循环,程序的正常运行被打断,由单片机控制的系统无法继续工作,会造成整个系统的陷入停滞状态,发生 ...

  4. Watchdog问题实例分析

    1.日志获取 Watchdog相关的问题甚至需要以下所有的日志: logcat 通过adb logcat命令输出Android的一些当前运行日志,可以通过logcat的 -b 参数指定要输出的日志缓冲 ...

  5. Android2.2源码属性服务分析

    属性服务property service 大家都知道,在windows中有个注册表,里面存储的是一些键值对.注册表的作用就是:系统或者应用程序将自己的一些属性存储在注册表中,即使系统或应用程序重启,它 ...

  6. Android2.2源码init机制分析

    1 源码分析必备知识 1.1 linux内核链表 Linux内核链表的核心思想是:在用户自定义的结构A中声明list_head类型的成员p,这样每个结构类型为A的变量a中,都拥有同样的成员p,如下: ...

  7. android2.3 View视图框架源码分析之一:android是如何创建一个view的?

    View是所有控件的一个基类,无论是布局(Layout),还是控件(Widget)都是继承自View类.只不过layout是一个特殊的view,它里面创建一个view的数组可以包含其他的view而已. ...

  8. 《深入理解Android2》读书笔记(四)

    接上篇<深入理解Android2>读书笔记(三) ActivityManagerService(AMS) 1.AMS由ActivityManagerNative(AMN)类派生,并实现Wa ...

  9. [原] KVM虚拟机网络闪断分析

    背景 公司云平台的机器时常会发生网络闪断,通常在10s-100s之间. 异常情况 VM出现问题时,表现出来的情况是外部监控系统无法访问,猜测可能是由于系统假死,OVS链路问题等等.但是在出现网络问题的 ...

随机推荐

  1. Oracle 分区表的索引、分区索引

    对于分区表,可以建立不分区索引.也就是说表分区,但是索引不分区.以下着重介绍分区表的分区索引. 索引与表一样,也可以分区.索引分为两类:locally partition index(局部分区索引). ...

  2. 使用IP地址方法登录MySQL数据库Can't connect to MySQL server的原因。mysql -h 192.168.1.104 -P3306 -uroot -p 失败

    mysql -h 192.168.1.104 -P3306 -uroot -p 然后输入你安装时设置的MySQL密码 发现Can't connect to MySQL server 你的IP 解决方法 ...

  3. Angular2 Service获取json数据

    在Angular2框架下一般交互解析json是要用到Service的,其实除了Service还是很多的,今天先写个最简单的前后端数据交互 嗯~~ 首先我先在app包下直接创建Service 好了 这里 ...

  4. JAVA JDBC 连接 Oracle

    使用 Junit 测试类编写 public class JdbcTest { private Connection con = null;// 创建一个数据库连接 private PreparedSt ...

  5. 【Django】Django中datetime的处理(strftime/strptime)

    strftime<将date,datetime,timezone.now()类型处理转化为字符串类型> strftime()函数是用来格式化一个日期.日期时间和时间的函数,支持date.d ...

  6. 二十六、MySQL 临时表

    MySQL 临时表 MySQL 临时表在我们需要保存一些临时数据时是非常有用的.临时表只在当前连接可见,当关闭连接时,Mysql会自动删除表并释放所有空间. 临时表在MySQL 3.23版本中添加,如 ...

  7. DevOps - 日志分析 -ELK

    wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-co ...

  8. Python知识点入门笔记——Python文件操作、异常处理及random模块使用

    文件是存储在外部介质的数据集合,通常可以长久保存,前提是介质不易损坏 Python的绝对路径写法: E:\\编程学习资料\\爬取某社区高清无码大图.py E:/编程学习资料/爬取某社区高清无码大图.p ...

  9. js中正则表达式与Python中正则表达式的区别

    今天女票让我帮她写一个js中的正则,来提取电话号码,对于正则规则来说,js与python是基本没有区别的,重点的区别是在一些函数与方法中. python中的正则提取: import re str = ...

  10. Linux命令之---rm

    命令简介 rm命令为删除一个目录中的一个或多个文件或目录,它也可以将某个目录及其下的所有文件及子目录均删除.对于链接文件,只是删除了链接,原有文件均保持不变. rm是一个危险的命令,使用的时候要特别当 ...