os/signal學習筆記

xjlgxlgx發表於2020-02-21

因為在專案中中關於註冊訊號處理器的,所以寫篇學習筆記來加深一下理解

首先基本概念為

訊號是事件發生時對程式的通知機制。有時也稱之為軟體中斷。訊號與硬體中斷的相似之處在於打斷了程式執行的正常流程,大多數情況下,無法預測訊號到達的精確時間。

因為一個具有合適許可權的程式可以向另一個程式傳送訊號,這可以稱為程式間的一種同步技術。當然,程式也可以向自身傳送訊號。然而,發往程式的諸多訊號,通常都是源於核心。引發核心為程式產生訊號的各類事件如下。

  • 硬體發生異常,即硬體檢測到一個錯誤條件並通知核心,隨即再由核心傳送相應訊號給相關程式。比如執行一條異常的機器語言指令(除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 都會獨立接收到該訊號的一個拷貝。

目前顯目上大概學習到這麼多,有新的理解後面再更新