[TOC]
-
- system_server进程存在哪些问题?
-
- 思考如何解决问题
-
- 如何设计Watchdog机制?
-
- 日志收集
-
- 总结
1. system_server进程存在哪些问题?
在整个Android系统中最为重要的一个进程就是system_server进程,对于应用程序来说system_server进程提供应用进程所需的一切,system_server进程的正常工作是整个手机系统正常运行的保障,这样用户才能够顺利使用手机。
- 问题一:
在我们开发者的眼中,一个复杂功能的进程中往往包含了各个线程,并且将复杂功能拆分成各个模块,使用各个线程运行各个功能模块的逻辑,将各个功能联动起来。system_server进程的功能非常复杂,使用的线程也非常多。各个功能被抽象成一个个服务,例如和应用程序界面显示相关的WMS(WindowManagerService)服务,和应用组件相关的AMS(ActivityManagerService)服务等,这些服务在system_server进程中以一个个对象实例的形式存在(单例)。从设计的角度来讲,这些服务也是分等级的,有些服务稍显得没那么重要,有些服务例如和窗口显示相关的WMS服务、和应用组件相关的AMS服务等都是比较重要的。system_server进程作为其他进程的数据处理中心,基本所有进程都访问system_server中的各个服务,如果两个应用进程同时访问system_server中的同一个服务,因为服务本身只有一个不可能让所有进程都同时访问到,那么只能排队访问,一个一个访问。那么system_server如何让他们轮着访问呢?答案就是使用锁-对象锁,每个服务都有自己对象锁,使用锁的形式来保证顺序访问。
举例:当进程A来访问system_server中的AMS服务时候那么就先持有AMS实例的对象锁。访问完则释放给其他进程使用,这样又有了新的问题:万一AMS锁被某个流程长期的占用一直不释放怎么办,出现长时间持锁,另外一个情况就是多线程的使用,当各模块服务之间交互的时候就可能出现死锁异常,所以目前可能会出现的两个问题就是:代码流程耗时、逻辑出现死锁。两个问题引起线程长时间持锁的异常,造成system_server卡死,其他进程就无法根本无法使用访问到对应服务,这样系统不就长时间卡死了吗?那么我们要如何解决这个问题呢?
- 问题二:
其次,这些服务逻辑功能的实现离不开各个线程的运行,在system_server中线程也是分作用和功能的,有负责整个system_server进程初始化工作的主线程:main线程,显示相关的android.display线程,动画相关的android.anim线程,用来处理各个客户端应用进程的逻辑的31个binder线程等等。由于线程的分工不一样,他们的重要性就不一样,像main、ui、display 等这些关键的线程处理事务时候是不能出现长时间卡死的,不能长时间只处理一个事务。这样在用户体验上就是极度的顿卡,那么我们如何解决这样的问题呢?
1.1 需要解决的问题
针对上面的描述目前我们要解决的问题:
-
各个关键服务(
AMS、WMS、IMS等)不能被某一逻辑长时间占用,即关键服务的对象锁不能被某些线程长时间的持有(死锁,逻辑异常等) -
各个关键线程(
main、i/o、ui、display等)不能出现耗时,即关键线程的消息要及时处理完
2. 思考如何解决问题
所以我们要怎么解决这两个问题呢?
答案: 程序员优化代码逻辑。额。。。但情况确实就是这样的,但是起码要让程序员知道哪些逻辑是异常的,所以从
system_server的角度来讲我们要对上面问题场景进行监测,当监测到问题时我们将所需日志完整的输出出来,交给程序员解决。
这也就引出了我们今天的主题:system_server进程中的watchdog监测机制。
(请注意它是个监测机制,基本上所有的稳定性问题都有对应的监测机制,要分析稳定性问题就要先熟悉对应的监测机制)
在system_server中我们采取的策略是:我们使用一个新的线程(watchdog线程)来监测上面两个问题,当问题发生时我们将日志保留下来,提供给程序员分析解决异常点(当然这里使用一个线程来进行监测也会有问题,例如watchdog线程卡死了怎么办,难道再使用一个线程来监控吗?这又可以引出另外一个专题,暂时先不多展开)。
当然这是从我们程序员的角度来看,我们只需要查看日志并解决问题即可。但是从用户体验的角度上来看,出现问题时手机将会长时间顿卡,当问题包含死锁等情况时候,手机可能会一直卡死。在使用可拆卸电池手机的年代,我们用户可以将电池拆下来,直接断电重启可以解决问题。而今电池和手机是封装在一块的,出现这种问题就只能等待手机电量耗尽手机重启才能恢复,这对于用户来说是体验很糟糕的,所以我们要在watchdog机制中制定一个标准,通过手机卡住的时间来衡量问题的严重性,卡住的时间超过用户能忍受的时间直接触发手机重启,让用户能够恢复手机的使用。
所以目前watchdog机制要解决的问题进一步细化:
-
检测关键服务的对象锁是否被某线程长时间占用(死锁、逻辑耗时阻塞),异常情况输出日志
-
检测关键线程运行逻辑是否阻塞,异常情况输出日志
-
根据异常的时间长短,划分异常的严重性,长时间异常则重启手机,短时间异常则尽可能输出日志让程序员能够知道问题的原因
针对这上面提出的三个点我们需要怎么样设计代码
3. 如何设计Watchdog机制?
因为我们无法真正从代码设计者的角度去剖析这个代码的由来(因为代码不是我们设计的,但是我们可以通过读代码来反推作者可能这样设计的原因,我查阅了历史watchdog的源码,发现现有Android 15的Watchdog框架延续的是android-4.4.4_r1的watchdog版本,历经10多个版本框架原理基本没有改变,和android-4.4.4_r1以前的watchdog框架基本截然不同,说明android-4.4.4_r1的watchdog代码很健壮)
3.1 system_server的功能和线程情况
在此之前我们需要了解一下system_server的功能以及线程的情况,才能更好的结合问题点来设计解决问题的方案。
(dump出system_server中启动所有binder服务:adb shell dumpsys system_server_dumper)
不完全统计可能有100+个服务运行在system_server进程之上,我们要监控比较重要的服务对象:
考虑的点(猜测):和用户进程交互比较频繁的服务,功能比较复杂比较多的服务,功能越多越复杂,线程交叉使用得多越容易死锁,例如如下这些服务:
-
StorageManagerService
-
MediaProjectionManagerService
-
MediaRouterService
-
InputManagerService
-
PowerManagerService
-
WindowManagerService
-
ActivityManagerService
各个服务模块逻辑的实现离不开system_server的各个线程,主要包含下面的一些线程(不完全功能按照来进行划分,粗略统计线程数量300+)
-
橘黄色框图:各个服务模块的线程,线程名和模块相关
-
绿色框图:重要的系统线程,按照线程处理的事务类型来进行划分,说白了就是公共线程,其他模块要是要处理的事务符合对应线程处理的事务类型,他就可以使用该线程来处理自己的事务。
-
橙色框图:Binder线程,用于和其他进程交互
-
蓝色框图:基础线程,每个Android应用进程都基本配备的线程
-
紫灰色框图:起辅助作用普通线程,线程池线程,和Hal层服务交互的HWBinder线程
整个system_server进程从功能和线程的角度来看就上图的描述那样。
简单介绍如下线程
- 基础线程(apk进程也是如此)
1.1 基础线程:main: 主线程初始化工作,
1.2 基础线程:守护线程:
1.2.1 ReferenceQueueDaemon 垃圾回收相关:清理Java引用对象,
1.2.2 FinalizerDaemon 垃圾回收相关:清理重写finalize()的对象,
1.2.3 FinalizerWatchdogDaemon:垃圾回收相关:监测 FinalizerDaemon线程,
1.2.4 Signal Catcher:信号处理函数,堆栈dump工作
1.2.5 HeapTaskDaemon:垃圾回收相关:堆内存
1.2.6 Profile Saver Android 混合编译相关
1.2.7 Jit thread pool worker thread:JIT即时编译相关
- Binder线程:和Binder驱动紧密相连,是其他进程和system_server进程交互的重要桥梁
2.1 binder:Pid_N(1-20,A-F,1A-1F) = 20+6+6=32个,包含1个binder主线程和31个binder普通线程
2.2 HwBinder
- 普通线程:
3.1 pool-X-thread-Y: 线程池产生的线程,可复用
2.2 Thread-N:普通线程,不可复用
-
各服务功能线程:线程名和功能相关
-
系统公共线程:
| Name | priority | 作用 |
|---|---|---|
| android.fg | THREAD_PRIORITY_DEFAULT = 0 | 前台公共线程,该线程设计的特别之处就是当处理的消息有超时的情况就会打印出来,它要求处理的消息要快,我们在system log能看到:Slow delivery XX等等log就是这里设置的 |
| android.ui | THREAD_PRIORITY_FOREGROUND = -2 | 处理系统进程UI相关的事务,它是一个fg线程(有消息超时会打印日志),和上面的fg线程不同的是它属于一个进程的Group中(THREAD_GROUP_TOP_APP = 5),因为显示UI要进行额外的加速处理和CPU调度策略相关 |
| android.io | THREAD_PRIORITY_DEFAULT = 0 | 文件读写相关的事务,无耗时监控 |
| android.display | THREAD_PRIORITY_DISPLAY +1= -4 +1 =-3 | 处理系统显示事务相关(WindowManager,DisplayManager,InputManager使用),拥有较高线程优先级,无耗时监控 |
| android.anim | THREAD_PRIORITY_DISPLAY = -4 | 处理系统动画相关的事务(窗口动画,启动动画等),线程优先级 ,无耗时监控 |
| android.anim.lf | THREAD_PRIORITY_DISPLAY = -4 | 处理系统动画相关的事务和Vsync相关,线程优先级 |
| android.bg | THREAD_PRIORITY_BACKGROUND = 10 | 后台线程,公共使用,有耗时监控 |
| android.perm | THREAD_PRIORITY_DEFAULT = 0 | 权限处理相关公共线程,有耗时监控 |
| android.usagestats | THREAD_PRIORITY_DEFAULT = 0 | usagestatus数据处理相关 ,有耗时监控 |
watchdog监控的线程:处理逻辑较多,和用户体验挂钩,窗口显示,动画,UI显示.
3.2 如何进行监控?
-
监测重要线程:只需要通过
watchdog监控线程给对应线程发送一条消息,看对应线程在对应时间能不能处理完就行,不能执行完成,则可能线程出现阻塞 -
监测重要对象:只需要通过
watchdog监控线程去使用一次对应对象,即获取一下对象锁能不能获取得到,不能获取到,则对象被某一线程长时间占用,可能线程出现阻塞.目前watchdog设置的时间是30s去检测一次(为什么是30s,这个我暂时无从考证,可能是由于一分钟=60s=2个30s)
第一点较容易实现,watchdog线程向各个线程发送一条消息,在规定时间内是否及时响应。
第二点稍微难一点,我们的实现方式是:各个重要服务对象都实现一个持锁的方法即可,方法主动使用synchronized (自身服务对象锁)获取一次锁,规定时间内是否能及时获取到。
目前我们可以根据问题得到下图,watchdog线程给每个重要的线程发消息
对于第二点,需要都调用各个重要服务的该方法来完成一次检查,这里和第一点有相似之处的实现逻辑:都是需要去检查是否出现耗时,所以第二点逻辑实现上我们可以抽象一下(Java中面向对象):使用单独一个线程去做获取各个重要服务对象锁这件事情。
那么目前得到下图,我们新增加了一个线程:watchdog:monitor,watchdog线程的事情就变成:给包括watchdog.monitor在内的重要线程发送一条消息,如果消息不能定时处理完成则异常,否则正常。
以上就是watchdog框架的树干部分,从现在我们就可以去梳理代码,将细枝末节的树叶补充完整。(我会先提出问题再回答这种形式来逐个将细节呈现出来)
3.3 代码实现一:问题的抽象
对于上述的图片中我们大概框架已经显现出来了
问题:对于我们watchdog线程只需要做的事情就是隔一段时间就发个消息去监测一下各个重要线程(新增加的watchdog.monitor/main/android.fg/android.ui等)是否阻塞就行,那么我们使用什么数据结构来保存这些重要线程呢?或者我们是否重新将这些线程和其他因素考虑在一起封装成一个新的对象呢?
考虑因素:
-
对于
watchdog线程发消息去check各个线程这个事情,了解Android线程模型的的都知道,其实本质就是获取对应线程的handler调用mHandler.postAtFrontOfQueue发送消息即可,即通过Handler去check各个线程是否阻塞-->HandlerChecker -
(main)Handler.postAtFrontOfQueue -
...
-
(android.fg)Handler.postAtFrontOfQueue -
假如我们现在有一个模块线程想要被watchdog线程监测,但是我只想watchdog来检测我的时候,由于我模块功能的特殊性检测时候只要我线程10s内不阻塞就行,因此需要提供一个客制化超时时间的方法--->AndTimeout
private final ArrayList<HandlerCheckerAndTimeout> mHandlerCheckers = new ArrayList<>()
所以HandlerCheckerAndTimeout实际上就是要保存各个检测线程的handler,以及特别定制化的超时时间timeout。所以我们进一步看HandlerCheckerAndTimeout的成员有mHandler以及客制化超时时间mCustomTimeoutMillis
/** Holds a checker and its timeout. */
static final class HandlerCheckerAndTimeout {
private final HandlerChecker mHandler;
private final Optional<Long> mCustomTimeoutMillis;
private HandlerCheckerAndTimeout(HandlerChecker checker, Optional<Long> timeoutMillis) {
this.mHandler = checker;
this.mCustomTimeoutMillis = timeoutMillis;
}
...
//其他方法容易理解类似get/set这样的方法故省略
}
我们使用一个ArrayList 来保存各个检测的对象,所以在watchdog初始化时候会将mHandlerCheckers填满,将各个检测对象add到对应成员中,这里大家可以看一下watchdog的构造函数即可。
-
运行
watchdog线程 -
将新的
watchdog.monitor线程运行起来并加入监测列表中 -
将其他重要监测线程加入到监测列表中
private Watchdog() {
mThread = new Thread(this::run, "watchdog");
ServiceThread t = new ServiceThread("watchdog.monitor",
android.os.Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/);
t.start();
mMonitorChecker = new HandlerChecker(new Handler(t.getLooper()), "monitor thread", mLock);
mHandlerCheckers.add(withDefaultTimeout(mMonitorChecker));
mHandlerCheckers.add(
withDefaultTimeout(
new HandlerChecker(FgThread.getHandler(), "foreground thread", mLock)));
//...相似实现
// Initialize monitor for Binder threads.
addMonitor(new BinderThreadMonitor());//后面在提
}
回到图四,我们虽然抽象出了HandlerCheckerAndTimeout(handler和客制化timeout),但是我们想一下,我们watchdog.monitor线程检查工作和其他线程(例如main线程)检查工作完全不一样,其他线程只需要调用Handler.postAtFrontOfQueue即可,watchdog.monitor线程 需要逐个便利各个服务对象实例的方法去进行持锁检查。为了功能的实现我们当然分开去实现,但是为了代码统一,减少额外的代码,我们能否将将两个check事项抽象为一个新的类?
可以,我们将两个事项进一步抽象HandlerChecker,并且在HandlerChecker中将两个check事项区分开来就行。
我们将watchdog.monitor线程需要check的一个个重要服务称为一个个monitor,对应每个重要服务都实现对应的方法来完成自身模块是否异常的逻辑(对于monitor()方法的实现,大多数模块直接获取当前模块的同步锁对象即可,一些特殊的模块可以根据模块自定义实现例如:StorageManagerService 服务则直接调用Vold进程的相关方法来判断自身模块的阻塞情况)
(我们可以在源码搜Watchdog.Monitor关键字就可以知道watchdog.monitor线程一共监测这多少个重要服务他们分别是什么)
//抽象接口继承此接口即可实现自己服务的耗时逻辑监控
public interface Monitor {
void monitor();
}
所以HandlerChecker中我们要使用一个数据结构来保存watchdog.monitor的监测对象
public static class HandlerChecker implements Runnable {
private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();//保存watchdog.monitor线程的监测对象:各个重要服务对象的实例
//watchdog线程触发的接口
public void scheduleCheckLocked(long handlerCheckerTimeoutMillis) {
...
mHandler.postAtFrontOfQueue(this);
}
//message具体检查事务逻辑细节:
//也是watchdog.monitor和其他main,android.ui等HandlerCheckers区别的关键
//* HandlerCheckers:watchdog.monitor 循环遍历各个monitor调用对应的monitor方法持锁排查
//* HandlerCheckers:main/android.ui/android.fg等,写一个标志位代表我的线程已经按时执行完成了
@Override
public void run() {
//下面这个循环主要是watchdog.monitor线程执行调用,只有HandlerCheckers:watchdog.monitor有monitor成员
final int size = mMonitors.size();
for (int i = 0 ; i < size ; i++) {
synchronized (mLock) {
mCurrentMonitor = mMonitors.get(i);
}
mCurrentMonitor.monitor();
}
// 所有的HandlerCheckers都写一个标志位,代表我的线程已经顺利完成检查工作
synchronized (mLock) {
mCompleted = true;
mCurrentMonitor = null;
}
}
}
代码中我们能看到使用list来保持monitor对象。以及发送的message的实现(run())
所以目前我们watchdog框架中的HandlerCheckers梳理框架图如下图:
在mHandlerCheckers中保存了众多的HandlerChecker,仔细分类可以分为:Monitor Checker和Hander Checker
-
Monitor Checker,用于检查是Monitor对象可能发生的”卡死“,
AMS,PKMS,WMS等核心的系统服务都是Monitor对象,通过watchdog.monitor线程来检查。 -
Handler Checker, 纯粹意义上的HandlerChecker,主要检测各个线程消息处理是否阻塞
补充:Binder线程是其他进程和system_server进程交互的通道,一共32个线程,当这32个线程出现阻塞,或者都繁忙无法继续处理来自其他进程的交互,这个时候会导致异常,系统也会出现卡死。我们需要对这种异常进行监控,所以添加一个monitor:BinderThreadMonitor交给watchdog.monitor线程来监控,所以我们在玫红色框图中看懂一个灰色的monitor成员。所以对于Monitor Checker进一步区分分为:普通服务监控monitor和binder线程监控monitor
根本上这两类HandlerChecker就是对应着文章开头提出的两个问题1和问题2。(这两个概念比较重要,对于SWT问题的分析特别重要)
3.4 代码实现二:异常的衡量
上面的check项,我们都已经抽象完成。那么我们的一个HandlerChecker出现卡死究竟卡死多久才算异常呢?有没有一个衡量标准?
阻塞卡死都归结到HandlerChecker是否正常,我们给每个HandlerChecker都设置一个状态值:state,代表HandlerChecker的阻塞程度
默认的标准是这样的:
| state 状态 | timeout | 作用 |
|---|---|---|
| COMPLETED | timeout =0 | 当HandlerChecker正常情况下不阻塞,线程能够瞬间的完成message的事务,状态良好 |
| WAITING | 0<timeout <30s | 当HandlerChecker出现阻塞,线程能够30s内完成message的事务,稍微耗时可能能恢复 |
| WAITED_HALF | 30s<timeout <60s | 当HandlerChecker出现阻塞,线程能够30s-60s内完成message的事务,耗时以及过半了,可能阻塞恢复不了了 |
| OVERDUE | 60s<timeout | 当HandlerChecker出现阻塞,线程能够60s都完成不了message的事务,耗时超过1分钟,完了,用户现在可能极度不满 |
判断系统卡死的事情基本解决完毕,那么我们需要什么时机输出日志呢?以及需要输出那些日志呢?
watchdog线程使用一个死循环,每隔30s去check一次各个HandlerChecker,获取各个HandlerChecker的状态是否正常.
-
当存在一个
HandlerChecker的state是(COMPLETED,WAITING),卡0-30s内我们认为可以接受,等下一个30s再去检查一次 -
当存在一个
HandlerChecker的state是(WAITING_HALF),卡30s-60s系统可能存在问题了,需要dump一些log,再等下一个30s去检查一次 -
当存在一个
HandlerChecker的state是(OVERDUE),卡大于60s,系统已经出现问题,用户以及无法使用手机超过1分钟了,可能死锁了等严重阻塞问题,我们需要dump所有必要的日志,重启手机,让用户能恢复使用
伪代码:
run() {
while(true) {
//step1:调用每个HandlerChecker,检查每个HandlerChecker的状态
//step2:休眠30s
//step3:根据HandlerChecker的状态,决定系统异常程度,以及dump log出来
//step4:收集日志部分
}
}
android-4.4.4_r1的watchdog的部分检查代码
@Override
public void run() {
while (true) {
//step1:调用每个HandlerChecker,检查每个HandlerChecker的状态
for (int i=0; i<mHandlerCheckers.size(); i++) {
HandlerChecker hc = mHandlerCheckers.get(i);
hc.scheduleCheckLocked();
}
//step2:休眠30s
long start = SystemClock.uptimeMillis();
while (timeout > 0) {
try {
wait(timeout);
} catch (InterruptedException e) {
Log.wtf(TAG, e);
}
timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);
}
//step3:根据HandlerChecker的状态,决定系统异常程度,以及dump log出来
final int waitState = evaluateCheckerCompletionLocked();
if (waitState == COMPLETED) {
// 情况良好,下次继续检查
waitedHalf = false;
continue;
} else if (waitState == WAITING) {
// 可能存在阻塞,时间可控,下次继续检查
continue;
} else if (waitState == WAITED_HALF) {
// 存在阻塞,时间超过30s,dump日志,下次继续检查
if (!waitedHalf) {
ArrayList<Integer> pids = new ArrayList<Integer>();
pids.add(Process.myPid());
ActivityManagerService.dumpStackTraces(true, pids, null, null,
NATIVE_STACKS_OF_INTEREST);
waitedHalf = true;
}
continue;
}
//存在阻塞,时间超过60s,系统存在问题,用户体验糟糕,尽可能收集日志,重启收集
// something is overdue!
blockedCheckers = getBlockedCheckersLocked();
subject = describeCheckersLocked(blockedCheckers);
allowRestart = mAllowRestart;
}
//step4:收集日志部分
}
}
- 执行所有的
Checker的监控方法scheduleCheckLocked()- 是否完成检测,完成则将mMonitorQueue队列中的monitor全部加入mMonitor中;
- 对于HandlerChecker来说,如果目标线程空闲,则不用进一步检测,则设置mCompleted = true,返回;
- 当上次check还没有完成, 则直接返回;
- Watchdog线程向各个线程发送消息,看消息能否按时完成。
- 等待30s后, 再调用
evaluateCheckerCompletionLocked来评估各HandlerChecker状态; - 根据
waitState状态来执行不同的操作:- 当COMPLETED或WAITING,则正常,继续检查;
- 当WAITED_HALF(超过30s)且为首次, 则输出system_server和3个Native进程的traces;
- 当OVERDUE, 则输出更多信息,并重启系统。
综合上之前的信息,我们目前可以得到下面这个框架图
4. 日志收集
我们需要收集哪些日志呢?
因为我们无法得知和HandlerChecker卡住的具体原因,我们只能dump和system_server有交集的各个进程,特别涉及和system_server模块服务有相关交集的进程。
例如
-
StorageManagerService--vold(volume daemon)进程
-
WindowManagerService--SurfaceFlinger 进程
-
等等以及
Hal的服务进程
首先第一步system_server自身进程的各个线程堆栈信息要dump出来。
其次和system_server 交互的其他关系紧密的进程的堆栈也需要dump出来,这里直接给出的进程有:
| Name | 说明 |
|---|---|
| system_server | 自身进程 |
| com.android.phone | 由于phone进程作为一个应用层级的Binder服务,依托system_server将接口中转给其他进程使用,(遇到过phone进程system_server形成死锁的案例 ) |
| com.google.android.providers.media.module | 同上,也有遇到过类似案例 |
| media相关进程 | |
| surfaceflinger | |
| netd | |
| ...30+ | 这里主要涉及在开发过程中遇到问题,不断添加完善的过程 |
日志输出:
- 当阻塞超过30s后进行一次日志输出:用户态进程堆栈,日志包含上面的用户态各个进程的各个现场现场的堆栈信息(目录
data/anr/anr_日期.txt) - 当阻塞超过60s后进行最后一次日志输出用户态进程堆栈+kernel异常信息,这个时候系统是真的卡死了,很可能是由于
kernel卡死导致(内核线程死锁,阻塞,文件系统异常,内存异常,驱动模块异常等),所以我们要输出一下kernel中状态异常的进程信息- 使用 /proc/sysrq-trigger 节点
echo l > /proc/sysrq-trigger: 显示所有活动cpu的堆栈信息echo w > /proc/sysrq-trigger: 将进入uninterrupted 状态的任务信息 dump 出来echo c > /proc/sysrq-trigger: 使系统崩溃,如果配置了crashdump,崩溃后会生成dump 文件
关于dump出来的日志是一个比较复杂的内容,等后面在进一步解释。
5. 总结
-
本文通过文字结合代码结合图片形式来讲述Watchdog机制的由来,讲的内容是粒度较大的,可能还存在部分错误,但是总统是个不断完善的过程。
-
文中比较重要的就是代码上对于两个问题的抽象是比较重要的,对于这两个概念的理解是后面分析SWT问题的基础,读者务必了解清楚。
-
文章的不足:代码部分往往很难使用语言来描述到极度详细,需要一定的代码阅读能力。
-
文章的用途:主要是用于记录自己学习watchdog机制的记录,一方面通过比较口语化的形式记录,方便后续查阅。
-
后期待办:完善问题分析例子