信號的概念 python
信號(signal)-- 進程之間通信的方式,是一種軟件中斷。一個進程一旦接收到信號就會打斷原來的程序執行流程來處理信號。 linux
幾個經常使用信號: 多線程
SIGINT 終止進程 中斷進程 (control+c) app
SIGTERM 終止進程 軟件終止信號 函數
SIGKILL 終止進程 殺死進程 測試
SIGALRM 鬧鐘信號 spa
SIGTERM比較友好,進程能捕捉這個信號,根據您的須要來關閉程序。在關閉程序以前,您能夠結束打開的記錄文件和完成正在作的任務。在某些狀況下,假如進程正在進行做業並且不能中斷,那麼進程能夠忽略這個SIGTERM信號。 操作系統
對於SIGKILL信號,進程是不能忽略的。這是一個 「我無論您在作什麼,馬上中止」的信號。假如您發送SIGKILL信號給進程,Linux就將進程中止在那裏。 線程
1(被動式) 內核檢測到一個系統事件.例如子進程退出會像父進程發送SIGCHLD信號.鍵盤按下control+c會發送SIGINT信號 code
2(主動式) 經過系統調用kill來向指定進程發送信號
[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提供的信號
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
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)
運行該程序。而後經過另一個進程來發送信號。
發送信號的代碼以下:
import os import signal #發送信號,16175是前面那個綁定信號處理函數的pid,須要自行修改 os.kill(16175,signal.SIGTERM) #發送信號,16175是前面那個綁定信號處理函數的pid,須要自行修改 os.kill(16175,signal.SIGUSR1)
而後顯示一個子進程結束後自動向父進程發送SIGCHLD信號的例子。
''''' 子進程結束會向父進程發送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信號又來了,第一個信號沒有被處理完畢的話,第二個信號就會丟棄。
因此,儘可能不要在多線程中使用信號。
這個不妥,測試沒發現有信號丟失
例子演示:
接收信號的程序,你會發現若是有另一端使用多線程向這個進程發送信號,會遺漏一些信號。
import os import signal from time import sleep import Queue QCOUNT = Queue.Queue() #初始化隊列 def onsigchld(a,b): '''''收到信號後向隊列中插入一個數字1''' print '收到SIGUSR1信號' sleep(2) QCOUNT.put(1) #向隊列中寫入 def exithanddle(s,e): raise SystemExit('收到終止命令,退出程序') signal.signal(signal.SIGUSR1,onsigchld) #綁定信號處理函數 signal.signal(signal.SIGINT,exithanddle) #當按下Ctrl + C 終止進程 while 1: print '個人pid是',os.getpid() print '如今隊列中元素的個數是',QCOUNT.qsize() sleep(2)
多線程發信號端的程序:
''''' 使用多線程向另一個進程發送信號 ''' 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 秒後就執行了打印。
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) ,程序將永遠阻塞
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) ,可是不起做用 :
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'