因為在專案中中關於註冊訊號處理器的,所以寫篇學習筆記來加深一下理解
首先基本概念為
訊號是事件發生時對程式的通知機制。有時也稱之為軟體中斷。訊號與硬體中斷的相似之處在於打斷了程式執行的正常流程,大多數情況下,無法預測訊號到達的精確時間。
因為一個具有合適許可權的程式可以向另一個程式傳送訊號,這可以稱為程式間的一種同步技術。當然,程式也可以向自身傳送訊號。然而,發往程式的諸多訊號,通常都是源於核心。引發核心為程式產生訊號的各類事件如下。
- 硬體發生異常,即硬體檢測到一個錯誤條件並通知核心,隨即再由核心傳送相應訊號給相關程式。比如執行一條異常的機器語言指令(除0,引用無法訪問的記憶體區域)。
- 使用者鍵入了能夠產生訊號的終端特殊字元。如中斷字元(通常是 Control-C)、暫停字元(通常是 Control-Z)。
- 發生了軟體事件。如調整了終端視窗大小,定時器到期等。
針對每個訊號,都定義了一個唯一的(小)整數,從 1 開始順序展開。系統會用相應常量表示。Linux 中,1-31 為標準訊號;32-64 為實時訊號(通過 kill -l
可以檢視)。
訊號達到後,程式視具體訊號執行如下預設操作之一。
- 忽略訊號,也就是核心將訊號丟棄,訊號對程式不產生任何影響。
- 終止(殺死)程式。
- 產生 coredump 檔案,同時程式終止。
- 暫停(Stop)程式的執行。
- 恢復程式執行。
當然,對於有些訊號,程式是可以改變預設行為的,這也就是 os/signal
包的用途。
而我們最常用的就是Notify
改變訊號處理,可以改變訊號的預設行為;Ignore
可以忽略訊號;Reset
重置訊號為預設行為;Stop
則停止接收訊號,但並沒有重置為預設行為。
func Notify(c chan<- os.Signal, sig ...os.Signal)
類似於繫結訊號處理程式。將輸入訊號轉發到 chan c。如果沒有列出要傳遞的訊號,會將所有輸入訊號傳遞到c;否則只傳遞列出的輸入訊號。
channel c 快取如何決定?因為 signal
包不會為了向c傳送資訊而阻塞(就是說如果傳送時 c 阻塞了,signal包會直接放棄):呼叫者應該保證 c 有足夠的快取空間可以跟上期望的訊號頻率。對使用單一訊號用於通知的channel,快取為1就足夠了。
相關原始碼:
// src/os/signal/signal.go process 函式
for c, h := range handlers.m {
if h.want(n) {
// send but do not block for it
select {
case c <- sig:
default: // 保證不會阻塞,直接丟棄
}
}
}
複製程式碼
可以使用同一 channel 多次呼叫 Notify
:每一次都會擴充套件該 channel 接收的訊號集。唯一從訊號集去除訊號的方法是呼叫 Stop
。可以使用同一訊號和不同 channel 多次呼叫 Notify
:每一個 channel 都會獨立接收到該訊號的一個拷貝。
目前顯目上大概學習到這麼多,有新的理解後面再更新