1. 程式人生 > >給應用程式加裝“看門狗”

給應用程式加裝“看門狗”

相信大多數的程式設計師或使用者,在Windows中見到類似於下面的親切而又溫馨的提示資訊,都不會感到陌生:

“XXX執行了非法操作,將被關閉。要終止程式,請單擊<確定>;要除錯程式,請單擊<取消>。”或者,“是否向Microsoft傳送錯誤報告?<傳送>,<不傳送>。”

如果這個程式執行在無人值守、需要保持連續工作狀態的場合,而其中的bug又一時難以排除,就需要採取應急措施,消除或減少程式出錯造成的影響。本文討論解決這個問題的辦法。

做過一定硬體開發的人都知道,惡劣的工作環境,帶有缺陷的硬體設計,不完善的演算法等內外因素,都可能造成程式“跑飛”,因此專門加裝一個“看門狗”,負責監視程式主體,必要時產生復位中斷,有效地避免裝置當機。

“看門狗”的思想,完全可以拿到高階語言程式設計中來用。基本做法是:設計一個簡單的監視程式做為主程序,將原來的工作程式作為子程序,由主程序啟動子程序並監視子程序的執行狀態。子程序在發生嚴重錯誤時不彈出本文開始時描述的對話方塊,而是悄悄退出。主程序發現子程序退出後,重新啟動子程序。如此反覆。

在具體實現上,下面以VC為例說明:

設定子程序為“靜默模式”

在系統初始化部分(CWinApp或main中的開頭),呼叫API函式SetErrorMode

SetErrorMode(SEM_NOGPFAULTERRORBOX);

保證程式在發生嚴重錯誤時不彈出對話方塊,無需人工干預,自行退出。

啟動子程序

在主程序中,建立子程序並執行。假定子程序的可執行檔案為work.exe,示意性程式碼如下

STARTUPINFO si;
PROCESS_INFORMATION pi;
 
ZeroMemory( &pi, sizeof(pi) );
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
 
// Start the child process
if (CreateProcess("work.exe", "", NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
    // success
    … …
}

CreateProcess有10個引數,看起來挺嚇人,其實並不複雜,很容易理解。最後一個引數會返回子程序的ID和控制代碼等資訊,後面就是對程序ID或控制代碼進行監視。

監視子程序

定時檢查子程序是否在正常執行。有好幾個API都可以用於對指定ID的程序進行監視,象GetProcessVersion,GetProcessTimes,GetProcessIoCounters等,其中GetProcessVersion最簡單,只有一個引數:

DWORD GetProcessVersion( DWORD ProcessId);

當子程序已經退出時,該函式返回0。

更為“專業”的函式是GetExitCodeProcess,它甚至能告訴我們子程序退出的原因:

BOOL GetExitCodeProcess(
    HANDLE hProcess,     // handle to the process
    LPDWORD lpExitCode   // termination status
);

更進一步的考慮

為增強系統的可靠性,給工作程式加裝“看門狗”,不失為一種可行的技術方案。但如果有兩套使用者介面,看起來就有點不那麼專業了。可將子程序設計為基於console的應用,不帶使用者介面,所有的資訊都通過主程序視窗輸出。主程序CreateProcess的第6個引數需加入CREATE_NO_WINDOW項,將子程序隱藏起來。這樣從使用者的角度看起來,就象只存在一個應用程式。

另一個問題是,如果使用者關閉主程序,如何同時關閉子程序?用TerminateProcess函式固然能結束子程序,但可能會造成記憶體洩漏等新問題。最好是主程序向子程序發出結束的訊息並進行同步,使子程序能夠從容地退出。

再擴充套件一下,一個主程序可以同時管理多個子程序。典型的例子是利用多塊網絡卡進行抓包、分析、處理的系統,將每一塊網絡卡應用與一個子程序繫結,而主程序負責監視所有的子程序的工作。

上面的討論涉及到程序間通訊(IPC)問題。解決的辦法有很多,象file mapping, mailslot, pipe, DDE, COM, RPC, clipboard, socket, WM_COPYDATA等都能達到目的,可根據個人喜好和具體情況採用。

[相關資源]

科腦工作室(Kernel Studio):www.kernelstudio.com