python之signal操作
1 訊號的意義
在linux系統中訊號是與程序通訊的一種手段。假設沒有訊號,linux中的程序一旦執行起來將不再受控,這種局面對於程序的管理來說是一種災難。kill、ctrl+c等操作本質上就是我們向linux發出的訊號,程序接收到訊號後根據相應的策略做出反饋。
2 訊號的來源
A) 通過終端(組合鍵)產生訊號
最典型的ctrl+c終止當前程序
B) 硬體異常產生訊號
例如程序執行中記憶體的定址出現異常,就會發出訊號。
C) 顯式的呼叫linux命令產生訊號
最典型的kill命令,通過linux命令發出訊號干預程序執行
D) 軟體程式碼傳送訊號
Java本地方法、Python signal類包等,通過程式發出訊號。
3 有哪些訊號
執行命令$kill -l可以看到訊號列表
可以看到有62種訊號,32、33輪空;1-31號為不可靠訊號,是linux自帶的與程序通訊的訊號;34-64是可靠訊號,是為了彌補linux不可靠訊號太少而擴充套件的。不可靠訊號多次發出最終只響應1次;而可靠訊號每次都有響應。
執行命令$man 7 signal可以更詳細的檢視每一種訊號的意義:
例如我們前面說過的ctrl+c,對應的是2號sigint;kill對應的是9號sigkill;記憶體錯誤對應的11
4 程序會如何處理這些訊號
並不是每種訊號程序都要給出反饋的,程序對待訊號會有3種策略:
A) 忽略此訊號
好理解,就是假裝沒看見,不鳥它。
B) 執行預設動作
例如終止程序等
C) 提供一個訊號處理函式,要求核心執行該函式
通過某種形式在傳送訊號時同時給定處理訊號的函式,相當於回撥函式
5 python對訊號的支援
Python對訊號的操作在importsignal這個包裡,有了前面知識的積累,我們很好理解signal包呼叫的側重點:哪種訊號?如何處理?如何傳送?
A) 哪種訊號?
Linux
B) 如何處理?
Signal預設提供了Ignal和Default2種策略,同時支援開發人員自定義回撥函式
C) 如何傳送?
Signal對訊號的傳送做了簡易的封裝,例如alarm()函式發起sigalrm訊號
6 案例,做一個timeout的自定義annotation:
import signal
def timeout(seconds=10, error_message="connect to server timeout"):
def decorator(func):
def _handle_timeout(signum, frame):
raise TimeoutError(error_message)
def wrapper(*args, **kwargs):
signal.signal(signal.SIGALRM, _handle_timeout)
signal.alarm(seconds)
try:
result = func(*args, **kwargs)
finally:
signal.alarm(0)
return result
return wraps(func)(wrapper)
return decorator
先定製了一個回撥函式decorator,超時後直接拋異常;
然後通過signal.signal()方法決定了訊號種類和處理方式,第一個引數可以替換成62種訊號的其中一種,第二個引數還可以傳入SIG_DFL或SIG_IGN;
最後通過signal.alarm()函式發起訊號。
這樣我就自己開發了一個超時處理機制。
@timeout(1200)
def ready_host(**not_connected):
while True:
if not any(not_connected):
break
for h in not_connected.keys():
if check_port_is_open(h):
del not_connected[h]
return True
這是個批量驗證網路連線的函式,not_connected是個集合存放著需要做嘗試的所有遠端機器,我通過@timeout(1200)的方式設定等待20分鐘後如果測試還沒有全部通過就丟擲異常。