標籤:sigabrt 不同的 sighup socket 不同 用途 main receive blank
先簡單說一下Signal是啥.(如果想直接使用可以不看)
Signal翻譯過來中文就是訊號- -
當然, 本身他就是Linux系統編程中非常重要的概念, 訊號機制是進程之間傳遞訊息的一種機制,
其全稱為非強制中斷訊號
作用是通知進程發生了非同步事件。進程之間可以調用系統來傳遞訊號, 本身核心也可以發送訊號給進程, 告訴該進程發生了某個事件.
注意,訊號只是用來通知某進程發生了什麼事件,並不給該進程傳遞任何資料。
接收訊號的進程對不同的訊號有三種處理方法
- 指定處理函數
- 忽略
- 根據系統預設值處理, 大部分訊號的預設處理是終止進程
然後就是一大段類型了..
Linux系統有兩大類訊號
- POSIX標準的規則訊號(regular signal 1-31編號)
- 即時訊號(real-time signal 32-63)
規則訊號
訊號編號 |
名稱 |
預設動作 |
說明 |
1 |
SIGHUP |
終止 |
終止控制終端或進程 |
2 |
SIGINT |
終止 |
由鍵盤引起的終端(Ctrl-c) |
3 |
SIGQUIT |
dump |
控制終端發送給進程的訊號, 鍵盤產生的退出(Ctrl-\), |
4 |
GIGILL |
dusmp |
非法指令引起 |
5 |
SIGTRAP |
dump |
debug中斷 |
6 |
SIGABRT/SIGIOT |
dump |
異常中止 |
7 |
SIGBUS/SIGEMT |
dump |
匯流排異常/EMT指令 |
8 |
SIGFPE |
dump |
浮點運算溢出 |
9 |
SIGKILL |
終止 |
強制殺死進程(大招, 進程不可捕獲) |
10 |
SIGUSR1 |
終止 |
使用者訊號, 進程可自訂用途 |
11 |
SIGSEGV |
dump |
非法記憶體位址引起 |
12 |
SIGUSR2 |
終止 |
使用者訊號, 進程可自訂用途 |
13 |
SIGPIPE |
終止 |
向某個沒有讀取的管道中寫入資料 |
14 |
SIGALRM |
終止 |
時鐘中斷(鬧鐘) |
15 |
SIGTERM |
終止 |
進程終止(進程可捕獲) |
16 |
SIGSTKFLT |
終止 |
副處理器棧錯誤 |
17 |
SIGCHLD |
忽略 |
子進程退出或中斷 |
18 |
SIGCONT |
繼續 |
如進程停止狀態則開始運行 |
19 |
SIGSTOP |
停止 |
停止進程運行 |
20 |
SIGSTP |
停止 |
鍵盤產生的停止 |
21 |
SIGTTIN |
停止 |
後台進程請求輸入 |
22 |
SIGTTOU |
停止 |
後台進程請求輸出 |
23 |
SIGURG |
忽略 |
socket發送緊急情況 |
24 |
SIGXCPU |
dump |
CPU時間限制被打破 |
25 |
SIGXFSZ |
dump |
檔案大小限制被打破 |
26 |
SIGVTALRM |
終止 |
虛擬定時時鐘 |
27 |
SIGPROF |
終止 |
profile timer clock |
28 |
SIGWINCH |
忽略 |
視窗尺寸調整 |
29 |
SIGIO/SIGPOLL |
終止 |
I/O可用 |
30 |
SIGPWR |
終止 |
電源異常 |
31 |
SIGSYS/SYSUNUSED |
dump |
系統調用異常 |
注意: 由於不同系統中同一個數值對應的訊號類型不一樣, 所以最好使用訊號名稱.
訊號的數值越小, 優先順序越高.
OK, 現在來說說Python中的處理
先列幾個常用的訊號:
編號 |
訊號名稱 |
說明 |
2 |
SIGINT |
當按下鍵盤(Ctrl-c)按鍵組合時進程就會收到這個訊號 |
15 |
SIGTERM |
當使用者輸入 kill sigterm pid. 對應的進程就會收到這個訊號. 這個訊號進程是可以捕獲並指定函數處理, 例如做一下程式清理等工作. 甚至忽視這個訊號 |
9 |
SIGKILL |
強制殺死進程, 這個訊號進程無法忽視, 直接在系統層面把進程殺掉. 所以在Python中他的不能監聽的 |
14 |
SIGALRM |
鬧鐘訊號 |
去碼
先來一個例子
#!/usr/bin/env python# -*- coding: utf-8 -*-""" 監聽了SIGINT訊號, 當程式在啟動並執行時候同步選取鍵盤 Ctrl+c 就會輸出 收到訊號 2 <frame object at 0x00000000021DD048> handler方法的兩個參數分別是 訊號編號, 程式幀"""import sysreload(sys)sys.setdefaultencoding("utf-8")import timeimport osimport signalreceive_times = 0def handler(signalnum, handler): global receive_times print u"收到訊號", signalnum, frame, receive_times receive_times += 1 if receive_times > 3: exit(0) # 自己走def main(): signal.signal(signal.SIGINT, handler) # Ctrl-c # time.sleep(10) # SIGINT 訊號同樣能喚醒 time.sleep, 所以這裡程式就會結束 while True: # 改成 while 效果會好點 passif __name__ == ‘__main__‘: main()
再看看SIGTERM
的效果
#!/usr/bin/env python# -*- coding: utf-8 -*-""" 當我們運行該程式時因為 while True 所以會持續的運行. 這裡監聽的是 SIGTERM 訊號, 所以當我們在終端輸入 kill pid (linux kill 預設是發送SIGTERM)時, 程式就會輸出: 收到訊號 15 <frame object at 0x7ff695738050> 0 當超過3次時就強制把自己殺死. 所以 SIGTERM 很適合用來做一些清理的工作"""import sysreload(sys)sys.setdefaultencoding("utf-8")import timeimport osimport signalreceive_times = 0def handler(signalnum, frame): global receive_times print u"收到訊號", signalnum, frame, receive_times receive_times += 1 if receive_times > 3: exit(0) # 自己走def main(): print "pid:", os.getpid() signal.signal(signal.SIGTERM, handler) while True: passif __name__ == ‘__main__‘: main()
剛才我們說過SIGKILL不能被監聽.
signal.signal(signal.SIGKILL, handler) # 這裡系統會直接跑錯 AttributeError: ‘module‘ object has no attribute ‘SIGKILL‘
最後來一個實際運用的例子
在python2.x的版本, 線程有個bug, 在join的時候不能接收訊號
詳解見:https://bugs.python.org/issue1167930
所以如果我們運行以下代碼
#!/usr/bin/env python# -*- coding: utf-8 -*-""" 這裡雖然我們監聽了 SIGINT 訊號, 但是當我們按下Ctrl-c時程式並沒有任何輸出. 還是要等線程運行完成程式才退出."""import sysreload(sys)sys.setdefaultencoding("utf-8")import timeimport osimport signalimport threadingreceive_times = 0def handler(signalnum, frame): global receive_times print u"收到訊號", signalnum, frame, receive_times receive_times += 1 if receive_times > 3: # os.kill(os.getpid(), signal.SIGTERM) # 我瘋起來連自己都殺 exit(0)def run(): print "thread %s run:"%(threading.currentThread().getName()) time.sleep(10) print "thread %s done"%(threading.currentThread().getName())def main(): print "pid:", os.getpid() signal.signal(signal.SIGINT, handler) thread_list = [] for i in range(5): thread = threading.Thread(target = run) thread_list.append(thread) for thread in thread_list: thread.start() for thread in thread_list: thread.join() print "all thread done"if __name__ == ‘__main__‘: main()
然後我們來改一下
#!/usr/bin/env python# -*- coding: utf-8 -*-""" 在這裡我們放棄了線程的join() 方法, 然後用 while True 的方式來代替, 然後在主進程判斷線程的存活狀態. 這樣既能持續的運行線程又能根據需求來隨時中斷"""import sysreload(sys)sys.setdefaultencoding("utf-8")import timeimport osimport signalimport threadingis_run_thread = Truedef handler(signalnum, frame): print u"收到訊號", signalnum, frame global is_run_thread is_run_thread = False # 停止運行線程def run(): print "thread %s run:"%(threading.currentThread().getName()) while is_run_thread: # do something pass print "thread %s done"%(threading.currentThread().getName())def main(): print "pid:", os.getpid() signal.signal(signal.SIGINT, handler) thread_list = [] for i in range(5): thread = threading.Thread(target = run) thread_list.append(thread) for thread in thread_list: thread.start() # 注意這裡 while True: for thread in thread_list: if thread.isAlive(): break else: break # for thread in thread_list: # thread.join() print "all thread done"if __name__ == ‘__main__‘: main()
注意, 在wnidows系統中只能調用 SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, or SIGTERM
Python和Signal