訊號的概念
訊號(signal)— 程式之間通訊的方式,是一種軟體中斷。一個程式一旦接收到訊號就會打斷原來的程式執行流程來處理訊號。
幾個常用訊號:
SIGINT 終止程式 中斷程式 (control+c)
SIGTERM 終止程式 軟體終止訊號
SIGKILL 終止程式 殺死程式
SIGALRM 鬧鐘訊號
程式結束訊號 SIGTERM和SIGKILL的區別
SIGTERM比較友好,程式能捕捉這個訊號,根據您的需要來關閉程式。在關閉程式之前,您可以結束開啟的記錄檔案和完成正在做的任務。在某些情況下,假如程式正在進行作業而且不能中斷,那麼程式可以忽略這個SIGTERM訊號。
對於SIGKILL訊號,程式是不能忽略的。這是一個 “我不管您在做什麼,立刻停止”的訊號。假如您傳送SIGKILL訊號給程式,Linux就將程式停止在那裡。
傳送訊號一般有兩種原因:
1(被動式) 核心檢測到一個系統事件.例如子程式退出會像父程式傳送SIGCHLD訊號.鍵盤按下control+c會傳送SIGINT訊號
2(主動式) 通過系統呼叫kill來向指定程式傳送訊號
linux作業系統提供的訊號
[100003@oss235 myppt]$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE
9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2
13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT
17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU
25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH
29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN
35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4
39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12
47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14
51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10
55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6
59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
Python提供的訊號
1 2 3 4 5 6 7 8 9 10 11 |
Python 2.4.3 (#1, Jun 11 2009, 14:09:58) [GCC 4.1.2 20080704 (Red Hat 4.1.2-44)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import signal >>> dir(signal) ['NSIG', 'SIGABRT', 'SIGALRM', 'SIGBUS', 'SIGCHLD', 'SIGCLD', 'SIGCONT', 'SIGFPE', 'SIGHUP', 'SIGILL', 'SIGINT', 'SIGIO', 'SIGIOT', 'SIGKILL', 'SIGPIPE', 'SIGPOLL', 'SIGPROF', 'SIGPWR', 'SIGQUIT', 'SIGRTMAX', 'SIGRTMIN', 'SIGSEGV', 'SIGSTOP', 'SIGSYS', 'SIGTERM', 'SIGTRAP', 'SIGTSTP', 'SIGTTIN', 'SIGTTOU', 'SIGURG', 'SIGUSR1', 'SIGUSR2', 'SIGVTALRM', 'SIGWINCH', 'SIGXCPU', 'SIGXFSZ', 'SIG_DFL', 'SIG_IGN', '__doc__', '__name__', 'alarm', 'default_int_handler', 'getsignal', 'pause', 'signal'] |
作業系統規定了程式收到訊號以後的預設行為
但是,我們可以通過繫結訊號處理函式來修改程式收到訊號以後的行為
有兩個訊號是不可更改的:SIGTOP和SIGKILL
繫結訊號處理函式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#coding=gbk import os import signal from time import sleep def onsignal_term(a,b): print '收到SIGTERM訊號' #這裡是繫結訊號處理函式,將SIGTERM繫結在函式onsignal_term上面 signal.signal(signal.SIGTERM,onsignal_term) def onsignal_usr1(a,b): print '收到SIGUSR1訊號' #這裡是繫結訊號處理函式,將SIGUSR1繫結在函式onsignal_term上面 signal.signal(signal.SIGUSR1,onsignal_usr1) while 1: print '我的程式id是',os.getpid() sleep(10) |
執行該程式。然後通過另外一個程式來傳送訊號。
傳送訊號
傳送訊號的程式碼如下:
1 2 3 4 5 6 7 |
import os import signal #傳送訊號,16175是前面那個繫結訊號處理函式的pid,需要自行修改 os.kill(16175,signal.SIGTERM) #傳送訊號,16175是前面那個繫結訊號處理函式的pid,需要自行修改 os.kill(16175,signal.SIGUSR1) |
SIGCHLD訊號
然後顯示一個子程式結束後自動向父程式傳送SIGCHLD訊號的例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
''' 子程式結束會向父程式傳送SIGCHLD訊號 ''' import os import signal from time import sleep def onsigchld(a,b): print '收到子程式結束訊號' signal.signal(signal.SIGCHLD,onsigchld) pid = os.fork() if pid == 0: print '我是子程式,pid是',os.getpid() sleep(2) else: print '我是父程式,pid是',os.getpid() os.wait() #等待子程式結束 |
使用訊號需要特別注意的地方:
如果一個程式收到一個SIGUSR1訊號,然後執行訊號繫結函式,第二個SIGUSR2訊號又來了,第一個訊號沒有被處理完畢的話,第二個訊號就會丟棄。
所以,儘量不要在多執行緒中使用訊號。
例子演示:
接收訊號的程式,你會發現如果有另外一端使用多執行緒向這個程式傳送訊號,會遺漏一些訊號。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#coding=gbk import os import signal from time import sleep import Queue QCOUNT = Queue.Queue() #初始化佇列 def onsigchld(a,b): '''收到訊號後向佇列中插入一個數字1''' print '收到SIGUSR1訊號' sleep(1) QCOUNT.put(1) #向佇列中寫入 signal.signal(signal.SIGUSR1,onsigchld) #繫結訊號處理函式 while 1: print '我的pid是',os.getpid() print '現在佇列中元素的個數是',QCOUNT.qsize() sleep(2) |
多執行緒發訊號端的程式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#coding=gbk ''' 使用多執行緒向另外一個程式傳送訊號 ''' import threading import os import signal def sendusr1(): print '傳送訊號' #這裡的程式id需要寫前一個程式實際執行的pid os.kill(17788, signal.SIGUSR1) WORKER = [] #開啟6個執行緒 for i in range(1, 7): threadinstance = threading.Thread(target = sendusr1) WORKER.append(threadinstance) for i in WORKER: i.start() for i in WORKER: i.join() print '主執行緒完成' |
需要的可以下載原始碼
內容補充:
Alarms 是一個特殊訊號型別,它可以讓程式要求系統經過一段時間對自己傳送通知。os 標準模組中指出,它可用於避免無限制阻塞 I/O 操作或其它系統呼叫。
像下面例子,原本程式睡眠 10 後才列印出 print ‘After :’, time.ctime(),但是由於 signal.alarm(2),所以 2 秒後就執行了列印。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import signal import time def receive_alarm(signum, stack): print 'Alarm :', time.ctime() # Call receive_alarm in 2 seconds signal.signal(signal.SIGALRM, receive_alarm) signal.alarm(2) print 'Before:', time.ctime() time.sleep(10) print 'After :', time.ctime() |
注意Signal只有主執行緒才能接收訊號,像下面例子,print ‘Done waiting’ 語句列印不出來,如果不呼叫 signal.alarm(2) ,程式將永遠阻塞。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
import signal import threading import os import time def signal_handler(num, stack): print 'Received signal %d in %s' % \ (num, threading.currentThread().name) signal.signal(signal.SIGUSR1, signal_handler) def wait_for_signal(): print 'Waiting for signal in', threading.currentThread().name signal.pause() print 'Done waiting' # Start a thread that will not receive the signal receiver = threading.Thread(target=wait_for_signal, name='receiver') receiver.start() time.sleep(0.1) def send_signal(): print 'Sending signal in', threading.currentThread().name os.kill(os.getpid(), signal.SIGUSR1) sender = threading.Thread(target=send_signal, name='sender') sender.start() sender.join() # Wait for the thread to see the signal (not going to happen!) print 'Waiting for', receiver.name signal.alarm(2) receiver.join() |
還有一點需要注意的是,雖然 alarms 類訊號可以在任何執行緒中呼叫,但是隻能在主執行緒中接收,像下面例子即使子執行緒 use_alarm 中呼叫 signal.alarm(1) ,但是不起作用 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import signal import time import threading def signal_handler(num, stack): print time.ctime(), 'Alarm in', threading.currentThread().name signal.signal(signal.SIGALRM, signal_handler) def use_alarm(): t_name = threading.currentThread().name print time.ctime(), 'Setting alarm in', t_name signal.alarm(1) print time.ctime(), 'Sleeping in', t_name time.sleep(3) print time.ctime(), 'Done with sleep in', t_name # Start a thread that will not receive the signal alarm_thread = threading.Thread(target=use_alarm, name='alarm_thread') alarm_thread.start() time.sleep(0.1) # Wait for the thread to see the signal (not going to happen!) print time.ctime(), 'Waiting for', alarm_thread.name alarm_thread.join() print time.ctime(), 'Exiting normally' |