Python】簡單聊進程間異步通信之signal模塊
Linux
信號是Unix系統中常見的一種進程間通信方式(IPC),例如我們經常操作的
kill -9 pid
這裏的-9
對應的就是 SIGKILL 信號, 9就是這個信號的編號,SIGKILL是它的名稱。 由於不同版本的 *nux 的實現會有差異,具體請參照系統API,我這裏是OSX,可以使用man signal
查看所有信號的定義。這裏簡單的學習下Python標準庫中信號處理的模塊 signal模塊.
信號是啥?
由於signal是系統編程的接口,那麼咱們來看看他的概念。
信號(signal)是Linux進程間通信的一種機制,全稱爲軟中斷信號,也被稱爲軟中斷。信號本質上是在軟件層次上對硬件中斷機制的一種模擬。
再來看看常用的場景
與其他進程間通信方式(例如管道、共享內存等)相比,信號所能傳遞的信息比較粗糙,只是一個整數。但正是由於傳遞的信息量少,信號也便於管理和使用,可以用於系統管理相關的任務,例如通知進程終結、中止或者恢復等。每種信號用一個整型常量宏表示,以SIG開頭,比如SIGCHLD、SIGINT等,它們在系統頭文件中定義。
更具體的介紹和詳細的機制原理,請參考 《Unix環境高級編程》等書籍。
自己的理解就是:可以給一個正在運行的進程發送不同的信號,然後進程就能立即收到這個通知,並且做出響應的行爲。
常用的幾個信號
編號 | 名稱 | 作用 |
---|---|---|
1 | SIGHUP | 終端掛起或者終止進程。默認動作爲終止進程 |
2 | SIGINT |
鍵盤中斷 <ctrl+c> 經常會用到。默認動作爲終止進程 |
3 | SIGQUIT |
鍵盤退出鍵被按下。一般用來響應 <ctrl+d> 。 默認動作終止進程 |
9 | SIGKILL | 強制退出。 shell中經常使用 |
14 | SIGALRM | 定時器超時,默認爲終止進程 |
15 | SIGTERM | 程序結束信號,程序一般會清理完狀態在退出,我們一般說的優雅的退出 |
signal模塊
在文檔的開頭,講述了Python signal對於系統的封裝和一些使用常識, 使用之前應當認真閱讀一下。
常用的API
signal.signal(signalnum, handler)
針對不同的信號需要定義對應的處理函數,當運行中的程序接受到對應的信號時候,會調用對應的handler。 handler函數應當有2個參數,一個是 signalnum, 另一是stack frame
(None 或者是 frame對象)
例如寫一個小程序,來處理 ctrl+c
事件和 SIGHUP
,也就是1和2信號。
#coding:utf-8
#orangleliu py2.7
#recv_signal.py
import signal
import time
import sys
import os
def handle_int(sig, frame):
print "get signal: %s, I will quit"%sig
sys.exit(0)
def handle_hup(sig, frame):
print "get signal: %s"%sig
if __name__ == "__main__":
signal.signal(2, handle_int)
signal.signal(1, handle_hup)
print "My pid is %s"%os.getpid()
while True:
time.sleep(3)
我們來測試下,首先啓動程序(根據打印的pid),在另外的窗口輸入 kill -1 21838
和 kill
-HUP 21838
, 最後使用 ctrl+c
關閉程序。 程序的輸出如下:
# python recv_signal.py
My pid is 21838
get signal: 1
get signal: 1
^Cget signal: 2, I will quit
signal.getsignal(signalnum)
根據signalnum返回信號對應的handler,可能是一個可以調用的Python對象,或者是signal.SIG_IGN
(表示被忽略),signal.SIG_DFL
(默認行爲已經被使用) 或None
(Python的handler還沒被定義)。
獲取signal中定義的信號num和名稱,還有它的handler是什麼
#coding:utf-8
#orangleliu py2.7
#getsignal_handler.py
import signal
def handle_hup(sig, frame):
print "get signal: %s"%sig
signal.signal(1, handle_hup)
if __name__ == "__main__":
ign = signal.SIG_IGN
dfl = signal.SIG_DFL
print "SIG_IGN", ign
print "SIG_DFL", dfl
print "*"*40
for name in dir(signal):
if name[:3] == "SIG" and name[3] != "_":
signum = getattr(signal, name)
gsig = signal.getsignal(signum)
print name, signum, gsig
運行的結果:可以看到大部分信號都是都有默認的行爲。
SIG_IGN 1
SIG_DFL 0
****************************************
SIGABRT 6 0
SIGALRM 14 0
SIGBUS 10 0
SIGCHLD 20 0
SIGCONT 19 0
SIGEMT 7 0
SIGFPE 8 0
SIGHUP 1 <function handle_hup at 0x109371c80>
SIGILL 4 0
SIGINFO 29 0
SIGINT 2 <built-in function default_int_handler>
SIGIO 23 0
SIGIOT 6 0
SIGKILL 9 None
SIGPIPE 13 1
SIGPROF 27 0
SIGQUIT 3 0
SIGSEGV 11 0
SIGSTOP 17 None
SIGSYS 12 0
SIGTERM 15 0
SIGTRAP 5 0
SIGTSTP 18 0
SIGTTIN 21 0
SIGTTOU 22 0
SIGURG 16 0
SIGUSR1 30 0
SIGUSR2 31 0
SIGVTALRM 26 0
SIGWINCH 28 0
SIGXCPU 24 0
SIGXFSZ 25 1
- 多線程使用信號