Python - 訊號處理



Python 中的訊號處理允許您為管理非同步事件定義自定義處理程式,例如來自鍵盤的中斷或終止請求、警報,甚至系統訊號。您可以透過定義自定義處理程式來控制程式如何響應各種訊號。Python 中的signal模組提供了設定和管理訊號處理程式的機制。

訊號處理程式是一個在接收到特定訊號時執行的函式。signal.signal()函式允許為訊號定義自定義處理程式。signal模組提供了一種方法來定義自定義處理程式,這些處理程式將在接收到特定訊號時執行。Python 中已經安裝了一些預設處理程式,它們是:

  • SIGPIPE 被忽略。
  • SIGINT 被轉換為 KeyboardInterrupt 異常。

常用訊號

即使訊號是在另一個執行緒中接收到的,Python 訊號處理程式也會在主直譯器的 Python 主執行緒中執行。訊號不能用於執行緒間通訊。

以下是某些常用訊號及其預設操作的列表:

  • SIGINT - 來自鍵盤的中斷 (Ctrl+C),它會引發 KeyboardInterrupt。
  • SIGTERM - 終止訊號。
  • SIGALRM - 來自 alarm() 的定時器訊號。
  • SIGCHLD - 子程序停止或終止。
  • SIGUSR1SIGUSR2 - 使用者定義的訊號。

設定訊號處理程式

要設定訊號處理程式,我們可以使用signal.signal()函式。它允許您為訊號定義自定義處理程式。處理程式保持安裝狀態,直到顯式重置,除了SIGCHLD

示例

以下是如何使用signal.signal()函式和SIGINT處理程式設定訊號處理程式的示例。

import signal
import time

def handle_signal(signum, frame):
   print(f"Signal {signum} received")

# Setting the handler for SIGINT
signal.signal(signal.SIGINT, handle_signal)

print("Press Ctrl+C to trigger SIGINT")
while True:
   time.sleep(1)

輸出

執行上述程式後,您將獲得以下結果:

Press Ctrl+C to trigger SIGINT
Signal 2 received
Signal 2 received
Signal 2 received
Signal 2 received

Windows 上的訊號處理

在 Windows 上,signal.signal()函式只能處理有限的訊號集。如果您嘗試使用 Windows 不支援的訊號,則會引發ValueError。如果訊號名稱未定義為 SIG* 模組級常量,則會引發AttributeError

Windows 上支援的訊號如下:

  • SIGABRT
  • SIGFPE
  • SIGILL
  • SIGINT
  • SIGSEGV
  • SIGTERM
  • SIGBREAK

處理定時器和警報

定時器和警報可用於在特定時間後安排訊號傳遞。

示例

讓我們觀察以下處理警報的示例。

import signal
import time

def handler(signum, stack):
   print('Alarm: ', time.ctime())

signal.signal(signal.SIGALRM, handler)
signal.alarm(2)
time.sleep(5)
for i in range(5):
   signal.alarm(2)
   time.sleep(5)
   print("interrupted #%d" % i)

輸出

執行上述程式後,您將獲得以下結果:

Alarm:  Wed Jul 17 17:30:11 2024
Alarm:  Wed Jul 17 17:30:16 2024
interrupted #0
Alarm:  Wed Jul 17 17:30:21 2024
interrupted #1
Alarm:  Wed Jul 17 17:30:26 2024
interrupted #2
Alarm:  Wed Jul 17 17:30:31 2024
interrupted #3
Alarm:  Wed Jul 17 17:30:36 2024
interrupted #4

從數字獲取訊號名稱

在 Python 中,沒有直接的方法可以從數字獲取訊號名稱。您可以使用signal模組獲取其所有屬性,過濾掉以SIG開頭的屬性,並將它們儲存在字典中。

示例

此示例建立一個字典,其中鍵是訊號編號,值是相應的訊號名稱。這對於動態地從其數值解析訊號名稱很有用。

import signal

sig_items = reversed(sorted(signal.__dict__.items()))
final = dict((k, v) for v, k in sig_items if v.startswith('SIG') and not v.startswith('SIG_'))
print(final)

輸出

執行上述程式後,您將獲得以下結果:

{<Signals.SIGXFSZ: 25>: 'SIGXFSZ', <Signals.SIGXCPU: 24>: 'SIGXCPU', <Signals.SIGWINCH: 28>: 'SIGWINCH', <Signals.SIGVTALRM: 26>: 'SIGVTALRM', <Signals.SIGUSR2: 12>: 'SIGUSR2', <Signals.SIGUSR1: 10>: 'SIGUSR1', <Signals.SIGURG: 23>: 'SIGURG', <Signals.SIGTTOU: 22>: 'SIGTTOU', <Signals.SIGTTIN: 21>: 'SIGTTIN', <Signals.SIGTSTP: 20>: 'SIGTSTP', <Signals.SIGTRAP: 5>: 'SIGTRAP', <Signals.SIGTERM: 15>: 'SIGTERM', <Signals.SIGSYS: 31>: 'SIGSYS', <Signals.SIGSTOP: 19>: 'SIGSTOP', <Signals.SIGSEGV: 11>: 'SIGSEGV', <Signals.SIGRTMIN: 34>: 'SIGRTMIN', <Signals.SIGRTMAX: 64>: 'SIGRTMAX', <Signals.SIGQUIT: 3>: 'SIGQUIT', <Signals.SIGPWR: 30>: 'SIGPWR', <Signals.SIGPROF: 27>: 'SIGPROF', <Signals.SIGIO: 29>: 'SIGIO', <Signals.SIGPIPE: 13>: 'SIGPIPE', <Signals.SIGKILL: 9>: 'SIGKILL', <Signals.SIGABRT: 6>: 'SIGABRT', <Signals.SIGINT: 2>: 'SIGINT', <Signals.SIGILL: 4>: 'SIGILL', <Signals.SIGHUP: 1>: 'SIGHUP', <Signals.SIGFPE: 8>: 'SIGFPE', <Signals.SIGCONT: 18>: 'SIGCONT', <Signals.SIGCHLD: 17>: 'SIGCHLD', <Signals.SIGBUS: 7>: 'SIGBUS', <Signals.SIGALRM: 14>: 'SIGALRM'}
廣告