.NET 筆記 - 處理程式終止事件 (kill SIGTERM/SIGINT)
本文內容以 .NET 6 或更新版本為目標平台。我不用 .NET Framework ,不保證適用。
我們有很多種方法可以結束一個執行中的程式。例如一個在指令列執行的程式,可以按下 Ctrl + C 打斷它。又或者是「工作管理員」這類的工具,也會提供「結束工作」按鈕讓使用者終止指定的程式。 Linux 用戶熟悉的 kill 指令也是一個用外部工具終止程式的方法。
在 Unix/Linux/BSD/macOS 系統,上述操作都統一在 signal 訊號機制。例如 Ctrl + C 實際上觸發 SIGINT 訊號; kill 默認發出 SIGTERM 訊號;強制終止程式觸發 SIGKILL 訊號。具體列表請見 GNU libc manual - Termination Signals。在 Windows 系統上其實也有類似的方法,不過本文重點是 .NET 平台。
當 .NET Core 從 Windows 走向跨作業系統時,它也必須支持這些操作。但 .NET 沒有增加 signal 類別,而是將 SIGTERM, SIGINT, SIGQUIT 這些訊號設計成事件(event),打散在不同類別中。
- SIGINT/SIGQUIT => Console.CancelKeyPress
- SIGTERM => AppDomain.CurrentDomain.ProcessExit
SIGINT 和 CancelKeyPress
// 在終端機按下 Ctrl+C 就等於 kill -SIGINT
Console.CancelKeyPress += (sender, e) =>
{
System.Console.WriteLine("Cancel Key (SIGINT) received");
// 等程序跑完事件處理程序後,作業系統就會結束程序。
// 不需要程序自行呼叫exit。
};
這個事件屬於 Console 類別,所以 winforms 型態的視窗應用程式不會觸發這個事件。
如果你的 .NET 程式作為 Windows 服務執行,那麼從工作管理員終止服務時,也會觸發這個事件。參考我用 nssm 執行 Python 程式的經驗:使Python程式作為Windows服務。
SIGTERM 和 ProcessExit
AppDomain.CurrentDomain.ProcessExit += (s, ev) =>
{
System.Console.WriteLine("SIGTERM received");
};
注意, ProcessExit 事件在程序正常終止的情形下也會觸發。例如 Main() 結束或呼叫 Environment.Exit() 。
winfroms 型態的視窗應用程式會觸發這個事件。
本文範例完整源碼在 sigterm-handler。
二、三十年前,我們在寫 Unix/Linux 程式時,必須要處理 SIGTERM/SIGINT 這些訊號,以保證我們可以在終止程序之前釋放程序使用的資源。現在的作業系統在資源回收這部份已經不太需要程式設計者關心了。不過我們還是有用到這些事件的場合,例如在程式終止時記錄終止時間,或是發出提示訊息給系統管理者。