(本文系統源碼基於Andoroid 10.0.0-r16)
Watchdog
的中文叫「看門狗」,最先引入Watchdog
是在單片機系統中,因爲單片機的工做環境容易受到外界磁場的干擾,致使程序「跑飛」,形成整個系統沒法正常工做,所以,引入了一個「看門狗」,對單片機的運行狀態進行實時監測,針對運行故障作一些保護處理,譬如讓系統重啓。這種Watchdog屬於硬件層面,必須有硬件電路的支持。java
Linux
也引入了Watchdog
,在Linux
內核下,當Watchdog啓動後,便設定了一個定時器,若是在超時時間內沒有對/dev/Watchdog
進行寫操做,則會致使系統重啓,經過定時器實現的Watchdog屬於軟件層面。android
Android設計了一個軟件層面Watchdog,用於保護一些重要的系統服務,當出現故障時,一般會讓Android系統重啓。因爲這種機制的存在,就常常會出現一些system_server進程被Watchdog殺掉而發生系統重啓的問題。 原理就是每到達必定的時間,獲取正在監控的鎖(Monitor.monitor()
),可否得到鎖從而判斷是否產生了死鎖。git
首先咱們能夠看到WatchDog是繼承於Thread類,而實際上WatchDog也是單線程運行的。github
public class Watchdog extends Thread {}
複製代碼
接下來咱們能夠看下Watchdog的構造函數: frameworks/base/services/core/java/com/android/server/Watchdog.java數組
private Watchdog() {
// 設置線程名字爲watchdog
super("watchdog");
// Initialize handler checkers for each common thread we want to check.
// 請注意,咱們目前沒有檢查後臺線程,由於它可能會持有較長時間運行的操做,
// 而不能保證那裏的操做的及時性。
// FgThread monitor
mMonitorChecker = new HandlerChecker(FgThread.getHandler(),
"foreground thread", DEFAULT_TIMEOUT);
mHandlerCheckers.add(mMonitorChecker);
// main thread monitor
mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),
"main thread", DEFAULT_TIMEOUT));
// UiThread monitor
mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),
"ui thread", DEFAULT_TIMEOUT));
// IoThread monitor
mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),
"i/o thread", DEFAULT_TIMEOUT));
// DisplayThread monitor
mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),
"display thread", DEFAULT_TIMEOUT));
// AnimationThread monitor
mHandlerCheckers.add(new HandlerChecker(AnimationThread.getHandler(),
"animation thread", DEFAULT_TIMEOUT));
// SurfaceAnimationThread monitor
mHandlerCheckers.add(new HandlerChecker(SurfaceAnimationThread.getHandler(),
"surface animation thread", DEFAULT_TIMEOUT));
// BinderThreadMonitor 主要用於檢測binder線程是否達到鏈接上限16個
// (正常的是16,這裏是SystemServer的進程,因此是31) >= 31個則阻塞線程等待mThreadCountDecrement喚醒
addMonitor(new BinderThreadMonitor());
// 用於對fd數量進行檢測 只會在userdebug或者eng版本開啓
// 值是廠商設置的 達到這個值 - 12 就會在/data/anr/ dump fd的信息
mOpenFdMonitor = OpenFdMonitor.create();
assert DB ||
DEFAULT_TIMEOUT > ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;
}
複製代碼
能夠看出咱們在建立出Watchdog的時候先把一些重要的線程加到Watchdog的mHandlerCheckers
裏面,當Watchdog執行run()
方法的時候就會依次對mHandlerCheckers
裏面的HandlerChecker對象進行輪詢,查看是否有超時的線程。有的話通常都是重啓來解決。markdown
那咱們在哪裏調用這個設置爲private的構造函數呢?就是在本類的getInstance()
中多線程
public static Watchdog getInstance() {
if (sWatchdog == null) {
sWatchdog = new Watchdog();
}
return sWatchdog;
}
複製代碼
咱們如今再看深一層,那究竟誰調用了getInstance()
方法呢?咱們動用ctrl
+ 鼠標點一下就彈出一系列的調用者。查看一番以後,能夠觀察到是SystemServer調用了咱們Watchdog的getInstance()
方法進行初始化。 frameworks/base/services/java/com/android/server/SystemServer.java併發
private void startBootstrapServices() {
final Watchdog watchdog = Watchdog.getInstance();
watchdog.start();
// ...
}
複製代碼
在調用了初始化的getInstance()
以後就立刻調用了watchdog的start()
方法,其實也就是父類Thread的start()
方法,這時候咱們的「看門狗」就跑起來了(執行了run()
方法),並且能夠看到watchdog是在startBootstrapServices()
中的第一句就進行初始化了,SystemServer中初始化服務的順序是這樣的:app
startBootstrapServices();
startCoreServices();
startOtherServices();
這說明了watchdog在衆多的服務中,優先級仍是很高的,那有沒有同窗會想到是爲何呢?答案就是不少服務的超時結束機制都是依賴watchdog
提供的,因此watchdog
必需要比其餘服務早初始化。ide
咱們再來看一下WatchDog的init()
方法,由於依賴於ActivityManagerService,因此咱們將在ActivityManagerService初始化以後再執行這個方法。
public void init(Context context, ActivityManagerService activity) {
mActivity = activity;
context.registerReceiver(new RebootRequestReceiver(),
new IntentFilter(Intent.ACTION_REBOOT),
android.Manifest.permission.REBOOT, null);
}
複製代碼
能夠看到這裏經過context註冊了一個廣播接收器,而這個RebootRequestReceiver
用來接收重啓的廣播來進行手機重啓的,是Watchdog中定義的一個內部類,RebootRequestReceiver
的代碼以下:
final class RebootRequestReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context c, Intent intent) {
if (intent.getIntExtra("nowait", 0) != 0) {
rebootSystem("Received ACTION_REBOOT broadcast");
return;
}
Slog.w(TAG, "Unsupported ACTION_REBOOT broadcast: " + intent);
}
}
void rebootSystem(String reason) {
Slog.i(TAG, "Rebooting system because: " + reason);
IPowerManager pms = (IPowerManager)ServiceManager.getService(Context.POWER_SERVICE);
try {
// 這裏是手機重啓而不是系統重啓
pms.reboot(false, reason, false);
} catch (RemoteException ex) {
}
}
複製代碼
咱們再來看看在Watchdog中相對比較重要的HandlerChecker內部類是長什麼樣子的。
/** * Used for checking status of handle threads and scheduling monitor callbacks. */
public final class HandlerChecker implements Runnable {
// 須要檢測的線程對應的handler
private final Handler mHandler;
// 進行日誌打印的時候知道是哪一個handler出現問題
private final String mName;
// 超時時間
private final long mWaitMax;
// 檢測的Monitor數組
private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
// 還未加入mMonitors中的Monitor
// 等待mCompleted爲true的時候才一次性加入mMonitors中
private final ArrayList<Monitor> mMonitorQueue = new ArrayList<Monitor>();
// 是否檢查完成的標誌位 空閒的話默認爲true
private boolean mCompleted;
// 目前正在檢查的Monitor
private Monitor mCurrentMonitor;
// 檢查開始的時間
private long mStartTime;
// mPauseCount > 0則說明這個HandlerChecker處於暫停狀態
private int mPauseCount;
HandlerChecker(Handler handler, String name, long waitMaxMillis) {
mHandler = handler;
mName = name;
mWaitMax = waitMaxMillis;
mCompleted = true;
}
// 註釋1
void addMonitorLocked(Monitor monitor) {
// We don't want to update mMonitors when the Handler is in the middle of checking
// all monitors. We will update mMonitors on the next schedule if it is safe
mMonitorQueue.add(monitor);
}
// 註釋2
public void scheduleCheckLocked() {
if (mCompleted) {
// Safe to update monitors in queue, Handler is not in the middle of work
mMonitors.addAll(mMonitorQueue);
mMonitorQueue.clear();
}
// 待檢測的mMonitors爲零且looper正在從隊列中輪詢任務
// 或者正在暫停的狀況下 不要進行一些數據的重置
if ((mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling())
|| (mPauseCount > 0)) {
mCompleted = true;
return;
}
if (!mCompleted) {
// we already have a check in flight, so no need
return;
}
mCompleted = false;
mCurrentMonitor = null;
mStartTime = SystemClock.uptimeMillis();
// 往handler前面插入任務(插隊)
mHandler.postAtFrontOfQueue(this);
}
// 是否超時
boolean isOverdueLocked() {
return (!mCompleted) && (SystemClock.uptimeMillis() > mStartTime + mWaitMax);
}
// 經過mStartTime、mWaitMax和mCompleted 進行狀態的判斷
// 前 mWaitMax/2 秒爲WAITING狀態,後mWaitMax/2爲WAITED_HALF狀態
// 大於mWaitMax爲超時狀態也就是OVERDUE狀態
public int getCompletionStateLocked() {
if (mCompleted) {
return COMPLETED;
} else {
long latency = SystemClock.uptimeMillis() - mStartTime;
if (latency < mWaitMax/2) {
return WAITING;
} else if (latency < mWaitMax) {
return WAITED_HALF;
}
}
return OVERDUE;
}
public Thread getThread() {
return mHandler.getLooper().getThread();
}
public String getName() {
return mName;
}
// 獲取Blocked狀態的描述
// 在哪一個名字的HandlerChecker中 或者 當前handler執行哪一個monitor
String describeBlockedStateLocked() {
if (mCurrentMonitor == null) {
return "Blocked in handler on " + mName + " (" + getThread().getName() + ")";
} else {
return "Blocked in monitor " + mCurrentMonitor.getClass().getName()
+ " on " + mName + " (" + getThread().getName() + ")";
}
}
@Override
public void run() {
// 一旦咱們到達這裏,咱們確保即便咱們調用#addMonitorLocked也不會改變mMonitors,
// 由於咱們首先將新的監視器添加到mMonitorQueue中,
// 並在下一次mCompleted爲真時將它們移到mMonitors中,此時咱們已經完成了這個方法的執行。
// 因此這裏用了兩個數組分別存儲Monitor
final int size = mMonitors.size();
for (int i = 0 ; i < size ; i++) {
synchronized (Watchdog.this) {
mCurrentMonitor = mMonitors.get(i);
}
mCurrentMonitor.monitor();
}
synchronized (Watchdog.this) {
mCompleted = true;
mCurrentMonitor = null;
}
}
// 暫停這個HandlerChecker
public void pauseLocked(String reason) {
mPauseCount++;
// Mark as completed, because there's a chance we called this after the watchog
// thread loop called Object#wait after 'WAITED_HALF'. In that case we want to ensure
// the next call to #getCompletionStateLocked for this checker returns 'COMPLETED'
mCompleted = true;
Slog.i(TAG, "Pausing HandlerChecker: " + mName + " for reason: "
+ reason + ". Pause count: " + mPauseCount);
}
// 繼續這個HandlerChecker
public void resumeLocked(String reason) {
if (mPauseCount > 0) {
mPauseCount--;
Slog.i(TAG, "Resuming HandlerChecker: " + mName + " for reason: "
+ reason + ". Pause count: " + mPauseCount);
} else {
Slog.wtf(TAG, "Already resumed HandlerChecker: " + mName);
}
}
}
複製代碼
從註釋1開始,能夠看到大部分方法後綴都加上了Locked,這是由於在Watchdog調用的時候都會加上synchronize鎖,保證不會發生多線程併發致使的問題。
而註釋2的scheduleCheckLocked()
方法在Watchdog中以間隔30s執行一次。
如下是實現了Monitor接口的類:
實現了Monitor的接口的類// 設置activityController 用處在後面Watchdog的run()方法會介紹到
public void setActivityController(IActivityController controller) {
synchronized (this) {
mController = controller;
}
}
// 設置watchdog超時後是否能重啓手機
public void setAllowRestart(boolean allowRestart) {
synchronized (this) {
mAllowRestart = allowRestart;
}
}
// 在FgThread中添加對monitor的監控
public void addMonitor(Monitor monitor) {
synchronized (this) {
mMonitorChecker.addMonitorLocked(monitor);
}
}
// 添加須要監控的handler 超時時間默認爲60s
public void addThread(Handler thread) {
addThread(thread, DEFAULT_TIMEOUT);
}
// 添加須要監控的handler 能夠自定義超時時間
public void addThread(Handler thread, long timeoutMillis) {
synchronized (this) {
final String name = thread.getLooper().getThread().getName();
mHandlerCheckers.add(new HandlerChecker(thread, name, timeoutMillis));
}
}
// 暫停當前正在運行的線程的監視操做。在執行可能錯誤觸發watchdog的長時間運行的操做以前很是有用。
// 每一個調用都須要一個匹配的{@link #resumeWatchingCurrentThread}調用。
public void pauseWatchingCurrentThread(String reason) {
synchronized (this) {
for (HandlerChecker hc : mHandlerCheckers) {
if (Thread.currentThread().equals(hc.getThread())) {
hc.pauseLocked(reason);
}
}
}
}
// 恢復watchdog的檢測狀態
public void resumeWatchingCurrentThread(String reason) {
synchronized (this) {
for (HandlerChecker hc : mHandlerCheckers) {
if (Thread.currentThread().equals(hc.getThread())) {
hc.resumeLocked(reason);
}
}
}
}
// 計算HandlerChecker的狀態 只要一個handler中有其中一個HandlerChecker超時了
// 那Watchdog就超時了
private int evaluateCheckerCompletionLocked() {
int state = COMPLETED;
for (int i=0; i<mHandlerCheckers.size(); i++) {
HandlerChecker hc = mHandlerCheckers.get(i);
state = Math.max(state, hc.getCompletionStateLocked());
}
return state;
}
// 獲取哪一個HandlerChecker超時,並加入數組返回
private ArrayList<HandlerChecker> getBlockedCheckersLocked() {
ArrayList<HandlerChecker> checkers = new ArrayList<HandlerChecker>();
for (int i=0; i<mHandlerCheckers.size(); i++) {
HandlerChecker hc = mHandlerCheckers.get(i);
if (hc.isOverdueLocked()) {
checkers.add(hc);
}
}
return checkers;
}
// 獲取每一個超時HandlerChecker的超時緣由,並組成String返回
private String describeCheckersLocked(List<HandlerChecker> checkers) {
StringBuilder builder = new StringBuilder(128);
for (int i=0; i<checkers.size(); i++) {
if (builder.length() > 0) {
builder.append(", ");
}
builder.append(checkers.get(i).describeBlockedStateLocked());
}
return builder.toString();
}
複製代碼
接下來咱們就看一下Watchdog的核心代碼,run()
方法
@Override
public void run() {
// 是否在等待的前半段時間 true則爲等待的後半段時間
boolean waitedHalf = false;
// 死循環
while (true) {
final List<HandlerChecker> blockedCheckers;
// 超時緣由 用於日誌打印
final String subject;
// 是否容許system_server重啓 默認爲true
// 能夠經過watchdog.setAllowRestart()從新設置值
final boolean allowRestart;
// 調試進程鏈接數 有的話會賦值爲2
int debuggerWasConnected = 0;
synchronized (this) {
// CHECK_INTERVAL爲30s
long timeout = CHECK_INTERVAL;
// 逐一執行每一個HandlerChecker的scheduleCheckLocked()方法
for (int i=0; i<mHandlerCheckers.size(); i++) {
HandlerChecker hc = mHandlerCheckers.get(i);
hc.scheduleCheckLocked();
}
if (debuggerWasConnected > 0) {
debuggerWasConnected--;
}
// 這裏用uptimeMillis() 是由於只會在設備喚醒的時候計算超時,
// 設備休眠計算時間會致使錯誤重啓
long start = SystemClock.uptimeMillis();
while (timeout > 0) {
if (Debug.isDebuggerConnected()) {
debuggerWasConnected = 2;
}
try {
// 等待30s
wait(timeout);
// Note: mHandlerCheckers and mMonitorChecker may have changed after waiting
} catch (InterruptedException e) {
Log.wtf(TAG, e);
}
if (Debug.isDebuggerConnected()) {
debuggerWasConnected = 2;
}
// 有可能wait到一半的時候發生了InterruptedException 致使時間沒有走完
// 只要沒有消耗完timeout的值 就繼續等待
timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);
}
// fd是否達到了限制的數量
boolean fdLimitTriggered = false;
if (mOpenFdMonitor != null) {
fdLimitTriggered = mOpenFdMonitor.monitor();
}
// fd數量限制沒有達到
if (!fdLimitTriggered) {
final int waitState = evaluateCheckerCompletionLocked();
// 都完成了的話 繼續下個循環 本次循環結束
if (waitState == COMPLETED) {
waitedHalf = false;
continue;
} else if (waitState == WAITING) {
// 在等待的前半段時間 繼續下個循環 本次循環結束
continue;
} else if (waitState == WAITED_HALF) {
if (!waitedHalf) {
Slog.i(TAG, "WAITED_HALF");
// 咱們等了一半的死鎖探測時間。
// 獲取堆棧跟蹤並等待另外一半。
ArrayList<Integer> pids = new ArrayList<Integer>();
pids.add(Process.myPid());
ActivityManagerService.dumpStackTraces(pids, null, null,
getInterestingNativePids());
waitedHalf = true;
}
continue;
}
// 到這裏的時候已經有handler超時了
// 獲取超時的HandlerChecker
blockedCheckers = getBlockedCheckersLocked();
// 獲取超時的HandlerChecker的信息
subject = describeCheckersLocked(blockedCheckers);
} else {
// fd數量限制達到了
blockedCheckers = Collections.emptyList();
subject = "Open FD high water mark reached";
}
allowRestart = mAllowRestart;
}
// 若是咱們到這裏,這意味着系統極可能掛起。
// 首先從系統進程的全部線程收集堆棧跟蹤。而後殺死這個進程,
// 這樣系統纔會從新啓動。
EventLog.writeEvent(EventLogTags.WATCHDOG, subject);
ArrayList<Integer> pids = new ArrayList<>();
pids.add(Process.myPid());
if (mPhonePid > 0) pids.add(mPhonePid);
// 打印java線程和native線程堆棧
final File stack = ActivityManagerService.dumpStackTraces(
pids, null, null, getInterestingNativePids());
// 掛起5s確保堆棧能寫入到文件中
SystemClock.sleep(5000);
// 讓kernel dump所有的blocked線程 和 cpu信息
doSysRq('w');
doSysRq('l');
// 嘗試把error加到dropbox下,但假設ActivityManager本身會死鎖
// 當這種狀況發生時, 致使如下語句死鎖,watchdog做爲一個總體將會失效
Thread dropboxThread = new Thread("watchdogWriteToDropbox") {
public void run() {
// 若是其中一條被觀察的線程在Watchdog init()方法執行前被掛起
// 咱們則沒有一個有效的AMS,因此不能把log打印存儲到dropbox路徑下
if (mActivity != null) {
mActivity.addErrorToDropBox(
"watchdog", null, "system_server", null, null, null,
subject, null, stack, null);
}
StatsLog.write(StatsLog.SYSTEM_SERVER_WATCHDOG_OCCURRED, subject);
}
};
dropboxThread.start();
try {
// 等待2s 讓dropboxThread返回
dropboxThread.join(2000);
} catch (InterruptedException ignored) {}
IActivityController controller;
synchronized (this) {
controller = mController;
}
// 若是ActivityController不爲null
if (controller != null) {
Slog.i(TAG, "Reporting stuck state to activity controller");
try {
// 因爲掛起system process而禁用dump 防止controller在報告錯誤的時候被掛起
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) {
}
}
// 只有在沒有debugger鏈接的狀況下才會殺死進程。
if (Debug.isDebuggerConnected()) {
debuggerWasConnected = 2;
}
if (debuggerWasConnected >= 2) {
Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process");
} else if (debuggerWasConnected > 0) {
Slog.w(TAG, "Debugger was connected: Watchdog is *not* killing the system process");
} else if (!allowRestart) {
Slog.w(TAG, "Restart not allowed: Watchdog is *not* killing the system process");
} else {
Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + subject);
WatchdogDiagnostics.diagnoseCheckers(blockedCheckers);
Slog.w(TAG, "*** GOODBYE!");
// 結束進程 watchdog存在於system_server進程之下
// 由於watchdog就是在system_server初始化的
Process.killProcess(Process.myPid());
System.exit(10);
}
waitedHalf = false;
}
}
複製代碼
文章到這裏就完了,基本上只是對參考文章進行代碼的上更新(Android Q),結構大體同樣。僅做爲學習記錄用途。