新聞中心
signal —- 設(shè)置異步事件處理程序
該模塊提供了在 python 中使用信號處理程序的機(jī)制。

創(chuàng)新互聯(lián)建站2013年至今,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢想脫穎而出為使命,1280元弋陽做網(wǎng)站,已為上家服務(wù),為弋陽各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:028-86922220
一般規(guī)則
signal.signal() 函數(shù)允許定義在接收到信號時(shí)執(zhí)行的自定義處理程序。少量的默認(rèn)處理程序已經(jīng)設(shè)置: SIGPIPE 被忽略(因此管道和套接字上的寫入錯(cuò)誤可以報(bào)告為普通的 Python 異常)以及如果父進(jìn)程沒有更改 SIGINT ,則其會(huì)被翻譯成 KeyboardInterrupt 異常。
一旦設(shè)置,特定信號的處理程序?qū)⒈3职惭b,直到它被顯式重置( Python 模擬 BSD 樣式接口而不管底層實(shí)現(xiàn)),但 SIGCHLD 的處理程序除外,它遵循底層實(shí)現(xiàn)。
On WebAssembly platforms wasm32-emscripten and wasm32-wasi, signals are emulated and therefore behave differently. Several functions and signals are not available on these platforms.
執(zhí)行 Python 信號處理程序
Python 信號處理程序不會(huì)在低級( C )信號處理程序中執(zhí)行。相反,低級信號處理程序設(shè)置一個(gè)標(biāo)志,告訴 virtual machine 稍后執(zhí)行相應(yīng)的 Python 信號處理程序(例如在下一個(gè) bytecode 指令)。這會(huì)導(dǎo)致:
-
捕獲同步錯(cuò)誤是沒有意義的,例如 SIGFPE 或 SIGSEGV ,它們是由 C 代碼中的無效操作引起的。Python 將從信號處理程序返回到 C 代碼,這可能會(huì)再次引發(fā)相同的信號,導(dǎo)致 Python 顯然的掛起。 從Python 3.3開始,你可以使用 faulthandler 模塊來報(bào)告同步錯(cuò)誤。
-
純 C 中實(shí)現(xiàn)的長時(shí)間運(yùn)行的計(jì)算(例如在大量文本上的正則表達(dá)式匹配)可以在任意時(shí)間內(nèi)不間斷地運(yùn)行,而不管接收到任何信號。計(jì)算完成后將調(diào)用 Python 信號處理程序。
-
如果處理句柄引發(fā)了異常,它將在主線程中“憑空”被引發(fā)。 請參閱 下面的注釋 討論相關(guān)細(xì)節(jié)。
信號與線程
Python 信號處理程序總是會(huì)在主 Python 主解釋器的主線程中執(zhí)行,即使信號是在另一個(gè)線程中接收的。 這意味著信號不能被用作線程間通信的手段。 你可以改用 threading 模塊中的同步原語。
此外,只有主解釋器的主線程才被允許設(shè)置新的信號處理程序。
模塊內(nèi)容
在 3.5 版更改: signal (SIG*), handler (SIG_DFL, SIG_IGN) and sigmask (SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK) related constants listed below were turned into enums (Signals, Handlers and Sigmasks respectively). getsignal(), pthread_sigmask(), sigpending() and sigwait() functions return human-readable enums as Signals objects.
The signal module defines three enums:
class signal.Signals
enum.IntEnum collection of SIG* constants and the CTRL_* constants.
3.5 新版功能.
class signal.Handlers
enum.IntEnum collection the constants SIG_DFL and SIG_IGN.
3.5 新版功能.
class signal.Sigmasks
enum.IntEnum collection the constants SIG_BLOCK, SIG_UNBLOCK and SIG_SETMASK.
可用性: Unix。
3.5 新版功能.
在 signal 模塊中定義的變量是:
signal.SIG_DFL
這是兩種標(biāo)準(zhǔn)信號處理選項(xiàng)之一;它只會(huì)執(zhí)行信號的默認(rèn)函數(shù)。 例如,在大多數(shù)系統(tǒng)上,對于 SIGQUIT 的默認(rèn)操作是轉(zhuǎn)儲核心并退出,而對于 SIGCHLD 的默認(rèn)操作是簡單地忽略它。
signal.SIG_IGN
這是另一個(gè)標(biāo)準(zhǔn)信號處理程序,它將簡單地忽略給定的信號。
signal.SIGABRT
來自 abort(3)) 的中止信號。
signal.SIGALRM
來自 alarm(2)) 的計(jì)時(shí)器信號。
可用性: Unix。
signal.SIGBREAK
來自鍵盤的中斷 (CTRL + BREAK)。
可用性: Windows。
signal.SIGBUS
總線錯(cuò)誤 (非法的內(nèi)存訪問)。
可用性: Unix。
signal.SIGCHLD
子進(jìn)程被停止或終結(jié)。
可用性: Unix。
signal.SIGCLD
SIGCHLD 的別名。
signal.SIGCONT
如果進(jìn)程當(dāng)前已停止則繼續(xù)執(zhí)行它
可用性: Unix。
signal.SIGFPE
浮點(diǎn)異常。 例如除以零。
參見
當(dāng)除法或求余運(yùn)算的第二個(gè)參數(shù)為零時(shí)會(huì)引發(fā) ZeroDivisionError 。
signal.SIGHUP
在控制終端上檢測到掛起或控制進(jìn)程的終止。
可用性: Unix。
signal.SIGILL
非法指令。
signal.SIGINT
來自鍵盤的中斷 (CTRL + C)。
默認(rèn)的動(dòng)作是引發(fā) KeyboardInterrupt。
signal.SIGKILL
終止信號。
它不能被捕獲、阻塞或忽略。
可用性: Unix。
signal.SIGPIPE
損壞的管道:寫入到?jīng)]有讀取器的管道。
默認(rèn)的動(dòng)作是忽略此信號。
可用性: Unix。
signal.SIGSEGV
段錯(cuò)誤:無效的內(nèi)存引用。
signal.SIGSTKFLT
Stack fault on coprocessor. The Linux kernel does not raise this signal: it can only be raised in user space.
Availability: Linux.
On architectures where the signal is available. See the man page signal(7)) for further information.
3.11 新版功能.
signal.SIGTERM
終結(jié)信號。
signal.SIGUSR1
用戶自定義信號 1。
可用性: Unix。
signal.SIGUSR2
用戶自定義信號 2。
可用性: Unix。
signal.SIGWINCH
窗口調(diào)整大小信號。
可用性: Unix。
SIG*
所有信號編號都是符號化定義的。 例如,掛起信號被定義為 signal.SIGHUP;變量的名稱與 C 程序中使用的名稱相同,具體見 。 ‘signal()‘ 的 Unix 手冊頁面列出了現(xiàn)有的信號 (在某些系統(tǒng)上這是 signal(2)),在其他系統(tǒng)中此列表則是在 signal(7)) 中)。 請注意并非所有系統(tǒng)都會(huì)定義相同的信號名稱集;只有系統(tǒng)所定義的名稱才會(huì)由此模塊來定義。
signal.CTRL_C_EVENT
對應(yīng)于 Ctrl+C 擊鍵事件的信號。此信號只能用于 os.kill() 。
可用性: Windows。
3.2 新版功能.
signal.CTRL_BREAK_EVENT
對應(yīng)于 Ctrl+Break 擊鍵事件的信號。此信號只能用于 os.kill() 。
可用性: Windows。
3.2 新版功能.
signal.NSIG
One more than the number of the highest signal number. Use valid_signals() to get valid signal numbers.
signal.ITIMER_REAL
實(shí)時(shí)遞減間隔計(jì)時(shí)器,并在到期時(shí)發(fā)送 SIGALRM 。
signal.ITIMER_VIRTUAL
僅在進(jìn)程執(zhí)行時(shí)遞減間隔計(jì)時(shí)器,并在到期時(shí)發(fā)送 SIGVTALRM 。
signal.ITIMER_PROF
當(dāng)進(jìn)程執(zhí)行時(shí)以及當(dāng)系統(tǒng)替進(jìn)程執(zhí)行時(shí)都會(huì)減小間隔計(jì)時(shí)器。 這個(gè)計(jì)時(shí)器與 ITIMER_VIRTUAL 相配結(jié),通常被用于分析應(yīng)用程序在用戶和內(nèi)核空間中花費(fèi)的時(shí)間。 SIGPROF 會(huì)在超期時(shí)被發(fā)送。
signal.SIG_BLOCK
pthread_sigmask() 的 how 形參的一個(gè)可能的值,表明信號將會(huì)被阻塞。
3.3 新版功能.
signal.SIG_UNBLOCK
pthread_sigmask() 的 how 形參的是個(gè)可能的值,表明信號將被解除阻塞。
3.3 新版功能.
signal.SIG_SETMASK
pthread_sigmask() 的 how 形參的一個(gè)可能的值,表明信號掩碼將要被替換。
3.3 新版功能.
signal 模塊定義了一個(gè)異常:
exception signal.ItimerError
作為來自下層 setitimer() 或 getitimer() 實(shí)現(xiàn)錯(cuò)誤的信號被引發(fā)。 如果將無效的定時(shí)器或負(fù)的時(shí)間值傳給 setitimer() 就導(dǎo)致這個(gè)錯(cuò)誤。 此錯(cuò)誤是 OSError 的子類型。
3.3 新版功能: 此錯(cuò)誤是 IOError 的子類型,現(xiàn)在則是 OSError 的別名。
signal 模塊定義了以下函數(shù):
signal.alarm(time)
如果 time 值非零,則此函數(shù)將要求將一個(gè) SIGALRM 信號在 time 秒內(nèi)發(fā)往進(jìn)程。 任何在之前排入計(jì)劃的警報(bào)都會(huì)被取消(在任何時(shí)刻都只能有一個(gè)警報(bào)被排入計(jì)劃)。 后續(xù)的返回值將是任何之前設(shè)置的警報(bào)被傳入之前的秒數(shù)。 如果 time 值為零,則不會(huì)將任何警報(bào)排入計(jì)劃,并且任何已排入計(jì)劃的警報(bào)都會(huì)被取消。 如果返回值為零,則目前沒有任何警報(bào)被排入計(jì)劃。
可用性: Unix。
signal.getsignal(signalnum)
返回當(dāng)前用于信號 signalnum 的信號處理程序。 返回值可以是一個(gè) Python 可調(diào)用對象,或是特殊值 signal.SIG_IGN, signal.SIG_DFL 或 None 之一。 在這里,signal.SIG_IGN 表示信號在之前被忽略,signal.SIG_DFL 表示之前在使用默認(rèn)的信號處理方式,而 None 表示之前的信號處理程序未由 Python 安裝。
signal.strsignal(signalnum)
返回信號 signalnum 的系統(tǒng)描述,例如 “Interrupt”, “Segmentation fault” 等等。 如果信號無法被識別則返回 None。
3.8 新版功能.
signal.valid_signals()
返回本平臺上的有效信號編號集。 這可能會(huì)少于 range(1, NSIG),如果某些信號被系統(tǒng)保留作為內(nèi)部使用的話。
3.8 新版功能.
signal.pause()
使進(jìn)程休眠直至接收到一個(gè)信號;然后將會(huì)調(diào)用適當(dāng)?shù)奶幚沓绦颉?返回空值。
可用性: Unix。
另請參閱 sigwait(), sigwaitinfo(), sigtimedwait() 和 sigpending()。
signal.raise_signal(signum)
向調(diào)用方進(jìn)程發(fā)送一個(gè)信號。 返回空值。
3.8 新版功能.
signal.pidfd_send_signal(pidfd, sig, siginfo=None, flags=0)
發(fā)送信號 sig 到文件描述符 pidfd 所指向的進(jìn)程。 Python 目前不支持 siginfo 形參;它必須為 None。 提供 flags 參數(shù)是為了將來擴(kuò)展;當(dāng)前未定義旗標(biāo)值。
更多信息請參閱 pidfd_send_signal(2)) 手冊頁面。
Availability: Linux >= 5.1
3.9 新版功能.
signal.pthread_kill(thread_id, signalnum)
將信號 signalnum 發(fā)送至與調(diào)用者在同一進(jìn)程中另一線程 thread_id。 目標(biāo)線程可被用于執(zhí)行任何代碼(Python或其它)。 但是,如果目標(biāo)線程是在執(zhí)行 Python 解釋器,則 Python 信號處理程序?qū)?由主解釋器的主線程來執(zhí)行。 因此,將信號發(fā)送給特定 Python 線程的唯一作用在于強(qiáng)制讓一個(gè)正在運(yùn)行的系統(tǒng)調(diào)用失敗并拋出 InterruptedError。
使用 threading.get_ident() 或 threading.Thread 對象的 ident 屬性為 thread_id 獲取合適的值。
如果 signalnum 為 0,則不會(huì)發(fā)送信號,但仍然會(huì)執(zhí)行錯(cuò)誤檢測;這可被用來檢測目標(biāo)線程是否仍在運(yùn)行。
引發(fā)一個(gè) 審計(jì)事件 signal.pthread_kill,附帶參數(shù) thread_id, signalnum。
可用性: Unix。
另請參閱 os.kill()。
3.3 新版功能.
signal.pthread_sigmask(how, mask)
獲取和/或修改調(diào)用方線程的信號掩碼。 信號掩碼是一組傳送過程目前為調(diào)用者而阻塞的信號集。 返回舊的信號掩碼作為一組信號。
該調(diào)用的行為取決于 how 的值,具體見下。
-
SIG_BLOCK: 被阻塞信號集是當(dāng)前集與 mask 參數(shù)的并集。
-
SIG_UNBLOCK: mask 中的信號會(huì)從當(dāng)前已阻塞信號集中被移除。 允許嘗試取消對一個(gè)非阻塞信號的阻塞。
-
SIG_SETMASK: 已阻塞信號集會(huì)被設(shè)為 mask 參數(shù)的值。
mask 是一個(gè)信號編號集合 (例如 {signal.SIGINT, signal.SIGTERM})。 請使用 valid_signals() 表示包含所有信號的完全掩碼。
例如,signal.pthread_sigmask(signal.SIG_BLOCK, []) 會(huì)讀取調(diào)用方線程的信號掩碼。
SIGKILL 和 SIGSTOP 不能被阻塞。
可用性: Unix。
另請參閱 pause(), sigpending() 和 sigwait()。
3.3 新版功能.
signal.setitimer(which, seconds, interval=0.0)
設(shè)置由 which 指明的給定間隔計(jì)時(shí)器 (signal.ITIMER_REAL, signal.ITIMER_VIRTUAL 或 signal.ITIMER_PROF 之一) 在 seconds 秒 (接受浮點(diǎn)數(shù)值,為與 alarm() 之差) 之后開始并在每 interval 秒間隔時(shí) (如果 interval 不為零) 啟動(dòng)。 由 which 指明的間隔計(jì)時(shí)器可通過將 seconds 設(shè)為零來清空。
當(dāng)一個(gè)間隔計(jì)時(shí)器啟動(dòng)時(shí),會(huì)有信號發(fā)送至進(jìn)程。 所發(fā)送的具體信號取決于所使用的計(jì)時(shí)器;signal.ITIMER_REAL 將發(fā)送 SIGALRM, signal.ITIMER_VIRTUAL 將發(fā)送 SIGVTALRM, 而 signal.ITIMER_PROF 將發(fā)送 SIGPROF.
原有的值會(huì)以元組: (delay, interval) 的形式被返回。
嘗試傳入無效的計(jì)時(shí)器將導(dǎo)致 ItimerError。
可用性: Unix。
signal.getitimer(which)
返回由 which 指明的給定間隔計(jì)時(shí)器當(dāng)前的值。
可用性: Unix。
signal.set_wakeup_fd(fd, **, warn_on_full_buffer=True*)
將喚醒文件描述符設(shè)為 fd。 當(dāng)接收到信號時(shí),會(huì)將信號編號以單個(gè)字節(jié)的形式寫入 fd。 這可被其它庫用來喚醒一次 poll 或 select 調(diào)用,以允許該信號被完全地處理。
原有的喚醒 fd 會(huì)被返回(或者如果未啟用文件描述符喚醒則返回 -1)。 如果 fd 為 -1,文件描述符喚醒會(huì)被禁用。 如果不為 -1,則 fd 必須為非阻塞型。 需要由庫來負(fù)責(zé)在重新調(diào)用 poll 或 select 之前從 fd 移除任何字節(jié)數(shù)據(jù)。
當(dāng)啟用線程用時(shí),此函數(shù)只能從 主解釋器的主線程 被調(diào)用;嘗試從另一線程調(diào)用它將導(dǎo)致 ValueError 異常被引發(fā)。
使用此函數(shù)有兩種通常的方式。 在兩種方式下,當(dāng)有信號到達(dá)時(shí)你都是用 fd 來喚醒,但之后它們在確定達(dá)到的一個(gè)或多個(gè)信號 which 時(shí)存在差異。
在第一種方式下,我們從 fd 的緩沖區(qū)讀取數(shù)據(jù),這些字節(jié)值會(huì)給你信號編號。 這種方式很簡單,但在少數(shù)情況下會(huì)發(fā)生問題:通常 fd 將有緩沖區(qū)空間大小限制,如果信號到達(dá)得太多且太快,緩沖區(qū)可能會(huì)爆滿,有些信號可能丟失。 如果你使用此方式,則你應(yīng)當(dāng)設(shè)置 warn_on_full_buffer=True,當(dāng)信號丟失時(shí)這至少能將警告消息打印到 stderr。
在第二種方式下,我們 只會(huì) 將喚醒 fd 用于喚醒,而忽略實(shí)際的字節(jié)值。 在此情況下,我們所關(guān)心的只有 fd 的緩沖區(qū)為空還是不為空;爆滿的緩沖區(qū)完全不會(huì)導(dǎo)致問題。 如果你使用此方式,則你應(yīng)當(dāng)設(shè)置 warn_on_full_buffer=False,這樣你的用戶就不會(huì)被虛假的警告消息所迷惑。
在 3.5 版更改: 在 Windows 上,此函數(shù)現(xiàn)在也支持套接字處理。
在 3.7 版更改: 添加了 warn_on_full_buffer 形參。
signal.siginterrupt(signalnum, flag)
更改系統(tǒng)調(diào)用重啟行為:如果 flag 為 False,系統(tǒng)調(diào)用將在被信號 signalnum 中斷時(shí)重啟,否則系統(tǒng)調(diào)用將被中斷。 返回空值。
可用性: Unix。
請注意用 signal() 安裝信號處理程序?qū)⒅貑⑿袨橹刂脼榭赏ㄟ^顯式調(diào)用 siginterrupt() 并為給定信號的 flag 設(shè)置真值來實(shí)現(xiàn)中斷。
signal.signal(signalnum, handler)
將信號 signalnum 的處理程序設(shè)為函數(shù) handler。 handler 可以為接受兩個(gè)參數(shù)(見下)的 Python 可調(diào)用對象,或者為特殊值 signal.SIG_IGN 或 signal.SIG_DFL 之一。 之前的信號處理程序?qū)⒈环祷兀▍⒁娚衔?getsignal() 的描述)。 (更多信息請參閱 Unix 手冊頁面 signal(2))。)
當(dāng)啟用線程用時(shí),此函數(shù)只能從 主解釋器的主線程 被調(diào)用;嘗試從另一線程調(diào)用它將導(dǎo)致 ValueError 異常被引發(fā)。
handler 將附帶兩個(gè)參數(shù)調(diào)用:信號編號和當(dāng)前堆棧幀 (None 或一個(gè)幀對象;有關(guān)幀對象的描述請參閱 類型層級結(jié)構(gòu)描述 或者參閱 inspect 模塊中的屬性描述)。
在 Windows 上,signal() 調(diào)用只能附帶 SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM 或 SIGBREAK。 任何其他值都將引發(fā) ValueError。 請注意不是所有系統(tǒng)都定義了同樣的信號名稱集合;如果一個(gè)信號名稱未被定義為 SIG* 模塊層級常量則將引發(fā) AttributeError。
signal.sigpending()
檢查正在等待傳送給調(diào)用方線程的信號集合(即在阻塞期間被引發(fā)的信號)。 返回正在等待的信號集合。
可用性: Unix。
另請參閱 pause(), pthread_sigmask() 和 sigwait()。
3.3 新版功能.
signal.sigwait(sigset)
掛起調(diào)用方線程的執(zhí)行直到信號集合 sigset 中指定的信號之一被傳送。 此函數(shù)會(huì)接受該信號(將其從等待信號列表中移除),并返回信號編號。
可用性: Unix。
另請參閱 pause(), pthread_sigmask(), sigpending(), sigwaitinfo() 和 sigtimedwait()。
3.3 新版功能.
signal.sigwaitinfo(sigset)
掛起調(diào)用方線程的執(zhí)行直到信號集合 sigset 中指定的信號之一被傳送。 此函數(shù)會(huì)接受該信號并將其從等待信號列表中移除。 如果 sigset 中的信號之一已經(jīng)在等待調(diào)用方線程,此函數(shù)將立即返回并附帶有關(guān)該信號的信息。 被傳送信號的信號處理程序不會(huì)被調(diào)用。 如果該函數(shù)被某個(gè)不在 sigset 中的信號中斷則會(huì)引發(fā) InterruptedError。
返回值是一個(gè)代表 siginfo_t 結(jié)構(gòu)體所包含數(shù)據(jù)的對象,具體為: si_signo, si_code, si_errno, si_pid, si_uid, si_status, si_band。
可用性: Unix。
另請參閱 pause(), sigwait() 和 sigtimedwait()。
3.3 新版功能.
在 3.5 版更改: 當(dāng)被某個(gè) 不在 sigset 中的信號中斷時(shí)本函數(shù)將進(jìn)行重試并且信號處理程序不會(huì)引發(fā)異常(請參閱 PEP 475 了解其理由)。
signal.sigtimedwait(sigset, timeout)
類似于 sigwaitinfo(),但會(huì)接受一個(gè)額外的 timeout 參數(shù)來指定超時(shí)限制。 如果將 timeout 指定為 0,則會(huì)執(zhí)行輪詢。 如果發(fā)生超時(shí)則返回 None。
可用性: Unix。
另請參閱 pause(), sigwait() 和 sigwaitinfo()。
3.3 新版功能.
在 3.5 版更改: 現(xiàn)在當(dāng)此函數(shù)被某個(gè)不在 sigset 中的信號中斷時(shí)將以計(jì)算出的 timeout 進(jìn)行重試并且信號處理程序不會(huì)引發(fā)異常(請參閱 PEP 475 了解其理由)。
Examples
這是一個(gè)最小示例程序。 它使用 alarm() 函數(shù)來限制等待打開一個(gè)文件所花費(fèi)的時(shí)間;這在文件為無法開啟的串行設(shè)備時(shí)會(huì)很有用處,此情況通常會(huì)導(dǎo)致 os.open() 無限期地掛起。 解決辦法是在打開文件之前設(shè)置 5 秒鐘的 alarm;如果操作耗時(shí)過長,將會(huì)發(fā)送 alarm 信號,并且處理程序會(huì)引發(fā)一個(gè)異常。
import signal, osdef handler(signum, frame):signame = signal.Signals(signum).nameprint(f'Signal handler called with signal {signame} ({signum})')raise OSError("Couldn't open device!")# Set the signal handler and a 5-second alarmsignal.signal(signal.SIGALRM, handler)signal.alarm(5)# This open() may hang indefinitelyfd = os.open('/dev/ttyS0', os.O_RDWR)signal.alarm(0) # Disable the alarm
對于 SIGPIPE 的說明
將你的程序用管道輸出到工具例如 head(1)) 將會(huì)導(dǎo)致 SIGPIPE 信號在其標(biāo)準(zhǔn)輸出的接收方提前關(guān)閉時(shí)被發(fā)送到你的進(jìn)程。 這將引發(fā)一個(gè)異常例如 BrokenPipeError: [Errno 32] Broken pipe。 要處理這種情況,請對你的入口點(diǎn)進(jìn)行包裝以捕獲此異常,如下所示:
import osimport sysdef main():try:# simulate large output (your code replaces this loop)for x in range(10000):print("y")# flush output here to force SIGPIPE to be triggered# while inside this try block.sys.stdout.flush()except BrokenPipeError:# Python flushes standard streams on exit; redirect remaining output# to devnull to avoid another BrokenPipeError at shutdowndevnull = os.open(os.devnull, os.O_WRONLY)os.dup2(devnull, sys.stdout.fileno())sys.exit(1) # Python exits with error code 1 on EPIPEif __name__ == '__main__':main()
請不要將 SIGPIPE 的處置方式設(shè)為 SIG_DFL 以避免 BrokenPipeError。 這樣做還會(huì)在你的程序所寫入的任何套接字連接中斷時(shí)導(dǎo)致你的程序異常退出。
有關(guān)信號處理句柄和異常的注釋
如果一個(gè)信號處理句柄引發(fā)了異常,該異常將被傳播到主線程并可能在任何 bytecode 指令之后被引發(fā),在執(zhí)行期間的任何時(shí)候都可能出現(xiàn) KeyboardInterrupt。 大多數(shù) Python 代碼,包括標(biāo)準(zhǔn)庫的代碼都不能對此進(jìn)行健壯性處理,因此 KeyboardInterrupt (或由信號處理句柄所導(dǎo)致的任何其他異常) 可能會(huì)在極少數(shù)情況下使程序處于非預(yù)期的狀態(tài)。
為了展示這個(gè)問題,請考慮以下代碼:
class SpamContext:def __init__(self):self.lock = threading.Lock()def __enter__(self):# If KeyboardInterrupt occurs here, everything is fineself.lock.acquire()# If KeyboardInterrupt occurs here, __exit__ will not be called...# KeyboardInterrupt could occur just before the function returnsdef __exit__(self, exc_type, exc_val, exc_tb):...self.lock.release()
對于許多程序,特別是那些在遇到 KeyboardInterrupt 只需直接退出的程序來說,這不是個(gè)問題,但是高復(fù)雜度或要求高可靠性的應(yīng)用程序則應(yīng)當(dāng)避免由于信號處理句柄引發(fā)異常。 他們還應(yīng)當(dāng)避免將捕獲 KeyboardInterrupt 作為程序關(guān)閉的優(yōu)雅方式。 相反地,他們應(yīng)當(dāng)安裝自己的 SIGINT 處理句柄。 下面是一個(gè)避免了 KeyboardInterrupt 的 HTTP 服務(wù)器示例:
import signalimport socketfrom selectors import DefaultSelector, EVENT_READfrom http.server import HTTPServer, SimpleHTTPRequestHandlerinterrupt_read, interrupt_write = socket.socketpair()def handler(signum, frame):print('Signal handler called with signal', signum)interrupt_write.send(b'\0')signal.signal(signal.SIGINT, handler)def serve_forever(httpd):sel = DefaultSelector()sel.register(interrupt_read, EVENT_READ)sel.register(httpd, EVENT_READ)while True:for key, _ in sel.select():if key.fileobj == interrupt_read:interrupt_read.recv(1)returnif key.fileobj == httpd:httpd.handle_request()print("Serving on port 8000")httpd = HTTPServer(('', 8000), SimpleHTTPRequestHandler)serve_forever(httpd)print("Shutdown...")
文章標(biāo)題:創(chuàng)新互聯(lián)Python教程:signal—-設(shè)置異步事件處理程序
當(dāng)前鏈接:http://www.dlmjj.cn/article/dpeeecc.html


咨詢
建站咨詢
