這篇文章主要介紹了Python中signal包的使用,主要在Linux系統(tǒng)下對(duì)進(jìn)程信號(hào)進(jìn)行相關(guān)操作,需要的朋友可以參考下
在liunx系統(tǒng)中要想每隔一分鐘執(zhí)行一個(gè)命令,最普遍的方法就是crontab了,如果不想使用crontab,經(jīng)同事指點(diǎn)在程序中可以用定時(shí)器實(shí)現(xiàn)這種功能,于是就開(kāi)始摸索了,發(fā)現(xiàn)需要一些信號(hào)的知識(shí)...
查看你的linux支持哪些信號(hào):kill -l 即可
- root@server:~# 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
root@server:~#
信號(hào):進(jìn)程之間通訊的方式,是一種軟件中斷。一個(gè)進(jìn)程一旦接收到信號(hào)就會(huì)打斷原來(lái)的程序執(zhí)行流程來(lái)處理信號(hào)。操作系統(tǒng)規(guī)定了進(jìn)程收到信號(hào)以后的默認(rèn)行為,但是,我們可以通過(guò)綁定信號(hào)處理函數(shù)來(lái)修改進(jìn)程收到信號(hào)以后的行為,有兩個(gè)信號(hào)是不可更改的SIGTOP和SIGKILL。
發(fā)送信號(hào)一般有兩種原因:
1(被動(dòng)式) 內(nèi)核檢測(cè)到一個(gè)系統(tǒng)事件.例如子進(jìn)程退出會(huì)像父進(jìn)程發(fā)送SIGCHLD信號(hào).鍵盤(pán)按下control+c會(huì)發(fā)送SIGINT信號(hào)
2(主動(dòng)式) 通過(guò)系統(tǒng)調(diào)用kill來(lái)向指定進(jìn)程發(fā)送信號(hào)
在C語(yǔ)言中有個(gè)setitimer函數(shù),函數(shù)setitimer可以提供三種定時(shí)器,它們相互獨(dú)立,任意一個(gè)定時(shí)完成都將發(fā)送定時(shí)信號(hào)到進(jìn)程,并且自動(dòng)重新計(jì)時(shí)。參數(shù)which確定了定時(shí)器的類型:
ITIMER_REAL 定時(shí)真實(shí)時(shí)間,與alarm類型相同。 SIGALRM
ITIMER_VIRT 定時(shí)進(jìn)程在用戶態(tài)下的實(shí)際執(zhí)行時(shí)間。 SIGVTALRM
ITIMER_PROF 定時(shí)進(jìn)程在用戶態(tài)和核心態(tài)下的實(shí)際執(zhí)行時(shí)間。 SIGPROF
這三種定時(shí)器定時(shí)完成時(shí)給進(jìn)程發(fā)送的信號(hào)各不相同,其中ITIMER_REAL類定時(shí)器發(fā)送SIGALRM信號(hào),ITIMER_VIRT類定時(shí)器發(fā)送SIGVTALRM信號(hào),ITIMER_REAL類定時(shí)器發(fā)送SIGPROF信號(hào)。
函數(shù)alarm本質(zhì)上設(shè)置的是低精確、非重載的ITIMER_REAL類定時(shí)器,它只能精確到秒,并且每次設(shè)置只能產(chǎn)生一次定時(shí)。函數(shù)setitimer設(shè)置的定時(shí)器則不同,它們不但可以計(jì)時(shí)到微妙(理論上),還能自動(dòng)循環(huán)定時(shí)。在一個(gè)Unix進(jìn)程中,不能同時(shí)使用alarm和ITIMER_REAL類定時(shí)器。
SIGINT 終止進(jìn)程 中斷進(jìn)程 (control+c)
SIGTERM 終止進(jìn)程 軟件終止信號(hào)
SIGKILL 終止進(jìn)程 殺死進(jìn)程
SIGALRM 鬧鐘信號(hào)
前期的知識(shí)也準(zhǔn)備的差不多了,該向python的signal進(jìn)軍了。
定義信號(hào)名
signal包定義了各個(gè)信號(hào)名及其對(duì)應(yīng)的整數(shù),比如
- import signal
- print signal.SIGALRM
- print signal.SIGCONT
Python所用的信號(hào)名和Linux一致。你可以通過(guò)
- $man 7 signal
查詢
預(yù)設(shè)信號(hào)處理函數(shù)
signal包的核心是使用signal.signal()函數(shù)來(lái)預(yù)設(shè)(register)信號(hào)處理函數(shù),如下所示:
- singnal.signal(signalnum, handler)
signalnum為某個(gè)信號(hào),handler為該信號(hào)的處理函數(shù)。我們?cè)谛盘?hào)基礎(chǔ)里提到,進(jìn)程可以無(wú)視信號(hào),可以采取默認(rèn)操作,還可以自定義操作。當(dāng)handler為signal.SIG_IGN時(shí),信號(hào)被無(wú)視(ignore)。當(dāng)handler為singal.SIG_DFL,進(jìn)程采取默認(rèn)操作(default)。當(dāng)handler為一個(gè)函數(shù)名時(shí),進(jìn)程采取函數(shù)中定義的操作。
- import signal
- # Define signal handler function
- def myHandler(signum, frame):
- print('I received: ', signum)
- # register signal.SIGTSTP's handler
- signal.signal(signal.SIGTSTP, myHandler)
- signal.pause()
- print('End of Signal Demo')
在主程序中,我們首先使用signal.signal()函數(shù)來(lái)預(yù)設(shè)信號(hào)處理函數(shù)。然后我們執(zhí)行signal.pause()來(lái)讓該進(jìn)程暫停以等待信號(hào),以等待信號(hào)。當(dāng)信號(hào)SIGUSR1被傳遞給該進(jìn)程時(shí),進(jìn)程從暫停中恢復(fù),并根據(jù)預(yù)設(shè),執(zhí)行SIGTSTP的信號(hào)處理函數(shù)myHandler()。myHandler的兩個(gè)參數(shù)一個(gè)用來(lái)識(shí)別信號(hào)(signum),另一個(gè)用來(lái)獲得信號(hào)發(fā)生時(shí),進(jìn)程棧的狀況(stack frame)。這兩個(gè)參數(shù)都是由signal.singnal()函數(shù)來(lái)傳遞的。
上面的程序可以保存在一個(gè)文件中(比如test.py)。我們使用如下方法運(yùn)行:
- $python test.py
以便讓進(jìn)程運(yùn)行。當(dāng)程序運(yùn)行到signal.pause()的時(shí)候,進(jìn)程暫停并等待信號(hào)。此時(shí),通過(guò)按下CTRL+Z向該進(jìn)程發(fā)送SIGTSTP信號(hào)。我們可以看到,進(jìn)程執(zhí)行了myHandle()函數(shù), 隨后返回主程序,繼續(xù)執(zhí)行。(當(dāng)然,也可以用$ps查詢process ID, 再使用$kill來(lái)發(fā)出信號(hào)。)
(進(jìn)程并不一定要使用signal.pause()暫停以等待信號(hào),它也可以在進(jìn)行工作中接受信號(hào),比如將上面的signal.pause()改為一個(gè)需要長(zhǎng)時(shí)間工作的循環(huán)。)
我們可以根據(jù)自己的需要更改myHandler()中的操作,以針對(duì)不同的信號(hào)實(shí)現(xiàn)個(gè)性化的處理。
定時(shí)發(fā)出SIGALRM信號(hào)
一個(gè)有用的函數(shù)是signal.alarm(),它被用于在一定時(shí)間之后,向進(jìn)程自身發(fā)送SIGALRM信號(hào):
- import signal
- # Define signal handler function
- def myHandler(signum, frame):
- print("Now, it's the time")
- exit()
- # register signal.SIGALRM's handler
- signal.signal(signal.SIGALRM, myHandler)
- signal.alarm(5)
- while True:
- print('not yet')
我們這里用了一個(gè)無(wú)限循環(huán)以便讓進(jìn)程持續(xù)運(yùn)行。在signal.alarm()執(zhí)行5秒之后,進(jìn)程將向自己發(fā)出SIGALRM信號(hào),隨后,信號(hào)處理函數(shù)myHandler開(kāi)始執(zhí)行。
發(fā)送信號(hào)
signal包的核心是設(shè)置信號(hào)處理函數(shù)。除了signal.alarm()向自身發(fā)送信號(hào)之外,并沒(méi)有其他發(fā)送信號(hào)的功能。但在os包中,有類似于linux的kill命令的函數(shù),分別為
- os.kill(pid, sid)
- os.killpg(pgid, sid)
分別向進(jìn)程和進(jìn)程組(見(jiàn)Linux進(jìn)程關(guān)系)發(fā)送信號(hào)。sid為信號(hào)所對(duì)應(yīng)的整數(shù)或者singal.SIG*。
實(shí)際上signal, pause,kill和alarm都是Linux應(yīng)用編程中常見(jiàn)的C庫(kù)函數(shù),在這里,我們只不過(guò)是用Python語(yǔ)言來(lái)實(shí)現(xiàn)了一下。實(shí)際上,Python 的解釋器是使用C語(yǔ)言來(lái)編寫(xiě)的,所以有此相似性也并不意外。此外,在Python 3.4中,signal包被增強(qiáng),信號(hào)阻塞等功能被加入到該包中。我們暫時(shí)不深入到該包中。
新聞熱點(diǎn)
疑難解答
圖片精選