初步理解Python進(jìn)程的信號(hào)通訊
信號(hào)的概念
信號(hào)(signal)-- 進(jìn)程之間通訊的方式,是一種軟件中斷。一個(gè)進(jìn)程一旦接收到信號(hào)就會(huì)打斷原來的程序執(zhí)行流程來處理信號(hào)。
幾個(gè)常用信號(hào):
SIGINT 終止進(jìn)程 中斷進(jìn)程 (control+c)
SIGTERM 終止進(jìn)程 軟件終止信號(hào)
SIGKILL 終止進(jìn)程 殺死進(jìn)程
SIGALRM 鬧鐘信號(hào)
進(jìn)程結(jié)束信號(hào) SIGTERM和SIGKILL的區(qū)別
SIGTERM比較友好,進(jìn)程能捕捉這個(gè)信號(hào),根據(jù)您的需要來關(guān)閉程序。在關(guān)閉程序之前,您可以結(jié)束打開的記錄文件和完成正在做的任務(wù)。在某些情況下,假如進(jìn)程正在進(jìn)行作業(yè)而且不能中斷,那么進(jìn)程可以忽略這個(gè)SIGTERM信號(hào)。
對于SIGKILL信號(hào),進(jìn)程是不能忽略的。這是一個(gè) “我不管您在做什么,立刻停止”的信號(hào)。假如您發(fā)送SIGKILL信號(hào)給進(jìn)程,Linux就將進(jìn)程停止在那里。
發(fā)送信號(hào)一般有兩種原因:
1(被動(dòng)式) 內(nèi)核檢測到一個(gè)系統(tǒng)事件.例如子進(jìn)程退出會(huì)像父進(jìn)程發(fā)送SIGCHLD信號(hào).鍵盤按下control+c會(huì)發(fā)送SIGINT信號(hào)
2(主動(dòng)式) 通過系統(tǒng)調(diào)用kill來向指定進(jìn)程發(fā)送信號(hào)
linux操作系統(tǒng)提供的信號(hào)
[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提供的信號(hào)
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']
操作系統(tǒng)規(guī)定了進(jìn)程收到信號(hào)以后的默認(rèn)行為
但是,我們可以通過綁定信號(hào)處理函數(shù)來修改進(jìn)程收到信號(hào)以后的行為
有兩個(gè)信號(hào)是不可更改的SIGTOP和SIGKILL
綁定信號(hào)處理函數(shù)
import os import signal from time import sleep def onsignal_term(a,b): print '收到SIGTERM信號(hào)' #這里是綁定信號(hào)處理函數(shù),將SIGTERM綁定在函數(shù)onsignal_term上面 signal.signal(signal.SIGTERM,onsignal_term) def onsignal_usr1(a,b): print '收到SIGUSR1信號(hào)' #這里是綁定信號(hào)處理函數(shù),將SIGUSR1綁定在函數(shù)onsignal_term上面 signal.signal(signal.SIGUSR1,onsignal_usr1) while 1: print '我的進(jìn)程id是',os.getpid() sleep(10)
運(yùn)行該程序。然后通過另外一個(gè)進(jìn)程來發(fā)送信號(hào)。
發(fā)送信號(hào)
發(fā)送信號(hào)的代碼如下:
import os import signal #發(fā)送信號(hào),16175是前面那個(gè)綁定信號(hào)處理函數(shù)的pid,需要自行修改 os.kill(16175,signal.SIGTERM) #發(fā)送信號(hào),16175是前面那個(gè)綁定信號(hào)處理函數(shù)的pid,需要自行修改 os.kill(16175,signal.SIGUSR1)
SIGCHLD信號(hào)
然后顯示一個(gè)子進(jìn)程結(jié)束后自動(dòng)向父進(jìn)程發(fā)送SIGCHLD信號(hào)的例子。
''''''' 子進(jìn)程結(jié)束會(huì)向父進(jìn)程發(fā)送SIGCHLD信號(hào) ''' import os import signal from time import sleep def onsigchld(a,b): print '收到子進(jìn)程結(jié)束信號(hào)' signal.signal(signal.SIGCHLD,onsigchld) pid = os.fork() if pid == 0: print '我是子進(jìn)程,pid是',os.getpid() sleep(2) else: print '我是父進(jìn)程,pid是',os.getpid() os.wait() #等待子進(jìn)程結(jié)束
使用信號(hào)需要特別注意的地方:
如果一個(gè)進(jìn)程收到一個(gè)SIGUSR1信號(hào),然后執(zhí)行信號(hào)綁定函數(shù),第二個(gè)SIGUSR2信號(hào)又來了,第一個(gè)信號(hào)沒有被處理完畢的話,第二個(gè)信號(hào)就會(huì)丟棄。
所以,盡量不要在多線程中使用信號(hào)。
這個(gè)不妥,測試沒發(fā)現(xiàn)有信號(hào)丟失
例子演示:
接收信號(hào)的程序,你會(huì)發(fā)現(xiàn)如果有另外一端使用多線程向這個(gè)進(jìn)程發(fā)送信號(hào),會(huì)遺漏一些信號(hào)。
import os import signal from time import sleep import Queue QCOUNT = Queue.Queue() #初始化隊(duì)列 def onsigchld(a,b): '''''''收到信號(hào)后向隊(duì)列中插入一個(gè)數(shù)字1''' print '收到SIGUSR1信號(hào)' sleep(2) QCOUNT.put(1) #向隊(duì)列中寫入 def exithanddle(s,e): raise SystemExit('收到終止命令,退出程序') signal.signal(signal.SIGUSR1,onsigchld) #綁定信號(hào)處理函數(shù) signal.signal(signal.SIGINT,exithanddle) #當(dāng)按下Ctrl + C 終止進(jìn)程 while 1: print '我的pid是',os.getpid() print '現(xiàn)在隊(duì)列中元素的個(gè)數(shù)是',QCOUNT.qsize() sleep(2)
多線程發(fā)信號(hào)端的程序:
''''''' 使用多線程向另外一個(gè)進(jìn)程發(fā)送信號(hào) ''' import threading import os import signal def sendusr1(): print '發(fā)送信號(hào)' #這里的進(jìn)程id需要寫前一個(gè)程序?qū)嶋H運(yùn)行的pid os.kill(17788, signal.SIGUSR1) WORKER = [] #開啟6個(gè)線程 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 '主線程完成'
內(nèi)容補(bǔ)充:
Alarms 是一個(gè)特殊信號(hào)類型,它可以讓程序要求系統(tǒng)經(jīng)過一段時(shí)間對自己發(fā)送通知。os 標(biāo)準(zhǔn)模塊中指出,它可用于避免無限制阻塞 I/O 操作或其它系統(tǒng)調(diào)用。
像下面例子,原本程序睡眠 10 后才打印出 print 'After :', time.ctime(),但是由于 signal.alarm(2),所以 2 秒后就執(zhí)行了打印。
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只有主線程才能接收信號(hào),像下面例子,print 'Done waiting' 語句打印不出來,如果不調(diào)用 signal.alarm(2) ,程序?qū)⒂肋h(yuǎn)阻塞
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()
還有一點(diǎn)需要注意的是,雖然 alarms 類信號(hào)可以在任何線程中調(diào)用,但是只能在主線程中接收,像下面例子即使子線程 use_alarm 中調(diào)用 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'
相關(guān)文章
Python實(shí)現(xiàn)驗(yàn)證碼識(shí)別
這篇文章主要介紹了Python實(shí)現(xiàn)驗(yàn)證碼識(shí)別的方法,文中講解非常詳細(xì),代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-06-06Python入門教程(三十一)Python的Try和Except
這篇文章主要介紹了Python入門教程(三十一)Python的Try Except,當(dāng)我們調(diào)用Python并發(fā)生錯(cuò)誤或異常時(shí),通常會(huì)停止并生成錯(cuò)誤消息,2023-05-05
可以使用try語句處理這些異常,需要的朋友可以參考下使用Python合成圖片的實(shí)現(xiàn)代碼(圖片添加個(gè)性化文本,圖片上疊加其他圖片)
這篇文章主要介紹了使用Python合成圖片的實(shí)現(xiàn)代碼(圖片添加個(gè)性化文本,圖片上疊加其他圖片),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04使用Python腳本對GiteePages進(jìn)行一鍵部署的使用說明
剛好之前有了解過python的自動(dòng)化,就想著自動(dòng)化腳本,百度一搜還真有類似的文章。今天就給大家分享下使用Python腳本對GiteePages進(jìn)行一鍵部署的使用說明,感興趣的朋友一起看看吧2021-05-05對Python 3.2 迭代器的next函數(shù)實(shí)例講解
今天小編就為大家分享一篇對Python 3.2 迭代器的next函數(shù)實(shí)例講解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-10-10