signal --- 設(shè)置異步事件處理程序?


該模塊提供了在 Python 中使用信號(hào)處理程序的機(jī)制。

一般規(guī)則?

signal.signal() 函數(shù)允許定義在接收到信號(hào)時(shí)執(zhí)行的自定義處理程序。少量的默認(rèn)處理程序已經(jīng)設(shè)置: SIGPIPE 被忽略(因此管道和套接字上的寫入錯(cuò)誤可以報(bào)告為普通的 Python 異常)以及如果父進(jìn)程沒(méi)有更改 SIGINT ,則其會(huì)被翻譯成 KeyboardInterrupt 異常。

一旦設(shè)置,特定信號(hào)的處理程序?qū)⒈3职惭b,直到它被顯式重置( Python 模擬 BSD 樣式接口而不管底層實(shí)現(xiàn)),但 SIGCHLD 的處理程序除外,它遵循底層實(shí)現(xiàn)。

執(zhí)行 Python 信號(hào)處理程序?

Python 信號(hào)處理程序不會(huì)在低級(jí)( C )信號(hào)處理程序中執(zhí)行。相反,低級(jí)信號(hào)處理程序設(shè)置一個(gè)標(biāo)志,告訴 virtual machine 稍后執(zhí)行相應(yīng)的 Python 信號(hào)處理程序(例如在下一個(gè) bytecode 指令)。這會(huì)導(dǎo)致:

  • 捕獲同步錯(cuò)誤是沒(méi)有意義的,例如 SIGFPESIGSEGV ,它們是由 C 代碼中的無(wú)效操作引起的。Python 將從信號(hào)處理程序返回到 C 代碼,這可能會(huì)再次引發(fā)相同的信號(hào),導(dǎo)致 Python 顯然的掛起。 從Python 3.3開始,你可以使用 faulthandler 模塊來(lái)報(bào)告同步錯(cuò)誤。

  • 純 C 中實(shí)現(xiàn)的長(zhǎng)時(shí)間運(yùn)行的計(jì)算(例如在大量文本上的正則表達(dá)式匹配)可以在任意時(shí)間內(nèi)不間斷地運(yùn)行,而不管接收到任何信號(hào)。計(jì)算完成后將調(diào)用 Python 信號(hào)處理程序。

  • If the handler raises an exception, it will be raised "out of thin air" in the main thread. See the note below for a discussion.

信號(hào)與線程?

Python 信號(hào)處理程序總是會(huì)在主 Python 主解釋器的主線程中執(zhí)行,即使信號(hào)是在另一個(gè)線程中接收的。 這意味著信號(hào)不能被用作線程間通信的手段。 你可以改用 threading 模塊中的同步原語(yǔ)。

此外,只有主解釋器的主線程才被允許設(shè)置新的信號(hào)處理程序。

模塊內(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.

Availability: Unix. See the man page sigprocmask(3) and pthread_sigmask(3) for further information.

3.5 新版功能.

signal 模塊中定義的變量是:

signal.SIG_DFL?

這是兩種標(biāo)準(zhǔn)信號(hào)處理選項(xiàng)之一;它只會(huì)執(zhí)行信號(hào)的默認(rèn)函數(shù)。 例如,在大多數(shù)系統(tǒng)上,對(duì)于 SIGQUIT 的默認(rèn)操作是轉(zhuǎn)儲(chǔ)核心并退出,而對(duì)于 SIGCHLD 的默認(rèn)操作是簡(jiǎn)單地忽略它。

signal.SIG_IGN?

這是另一個(gè)標(biāo)準(zhǔn)信號(hào)處理程序,它將簡(jiǎn)單地忽略給定的信號(hào)。

signal.SIGABRT?

來(lái)自 abort(3) 的中止信號(hào)。

signal.SIGALRM?

來(lái)自 alarm(2) 的計(jì)時(shí)器信號(hào)。

可用性: Unix。

signal.SIGBREAK?

來(lái)自鍵盤的中斷 (CTRL + BREAK)。

可用性: Windows。

signal.SIGBUS?

總線錯(cuò)誤 (非法的內(nèi)存訪問(wèn))。

可用性: Unix。

signal.SIGCHLD?

子進(jìn)程被停止或終結(jié)。

可用性: Unix。

signal.SIGCLD?

SIGCHLD 的別名。

signal.SIGCONT?

如果進(jìn)程當(dāng)前已停止則繼續(xù)執(zhí)行它

可用性: Unix。

signal.SIGFPE?

浮點(diǎn)異常。 例如除以零。

參見(jiàn)

當(dāng)除法或求余運(yùn)算的第二個(gè)參數(shù)為零時(shí)會(huì)引發(fā) ZeroDivisionError 。

signal.SIGHUP?

在控制終端上檢測(cè)到掛起或控制進(jìn)程的終止。

可用性: Unix。

signal.SIGILL?

非法指令。

signal.SIGINT?

來(lái)自鍵盤的中斷 (CTRL + C)。

默認(rèn)的動(dòng)作是引發(fā) KeyboardInterrupt。

signal.SIGKILL?

終止信號(hào)。

它不能被捕獲、阻塞或忽略。

可用性: Unix。

signal.SIGPIPE?

損壞的管道:寫入到?jīng)]有讀取器的管道。

默認(rèn)的動(dòng)作是忽略此信號(hào)。

可用性: Unix。

signal.SIGSEGV?

段錯(cuò)誤:無(wú)效的內(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é)信號(hào)。

signal.SIGUSR1?

用戶自定義信號(hào) 1。

可用性: Unix。

signal.SIGUSR2?

用戶自定義信號(hào) 2。

可用性: Unix。

signal.SIGWINCH?

窗口調(diào)整大小信號(hào)。

可用性: Unix。

SIG*

所有信號(hào)編號(hào)都是符號(hào)化定義的。 例如,掛起信號(hào)被定義為 signal.SIGHUP;變量的名稱與 C 程序中使用的名稱相同,具體見(jiàn) <signal.h>。 'signal()' 的 Unix 手冊(cè)頁(yè)面列出了現(xiàn)有的信號(hào) (在某些系統(tǒng)上這是 signal(2),在其他系統(tǒng)中此列表則是在 signal(7) 中)。 請(qǐng)注意并非所有系統(tǒng)都會(huì)定義相同的信號(hào)名稱集;只有系統(tǒng)所定義的名稱才會(huì)由此模塊來(lái)定義。

signal.CTRL_C_EVENT?

對(duì)應(yīng)于 Ctrl+C 擊鍵事件的信號(hào)。此信號(hào)只能用于 os.kill()

可用性: Windows。

3.2 新版功能.

signal.CTRL_BREAK_EVENT?

對(duì)應(yīng)于 Ctrl+Break 擊鍵事件的信號(hào)。此信號(hào)只能用于 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è)可能的值,表明信號(hào)將會(huì)被阻塞。

3.3 新版功能.

signal.SIG_UNBLOCK?

pthread_sigmask()how 形參的是個(gè)可能的值,表明信號(hào)將被解除阻塞。

3.3 新版功能.

signal.SIG_SETMASK?

pthread_sigmask()how 形參的一個(gè)可能的值,表明信號(hào)掩碼將要被替換。

3.3 新版功能.

signal 模塊定義了一個(gè)異常:

exception signal.ItimerError?

作為來(lái)自下層 setitimer()getitimer() 實(shí)現(xiàn)錯(cuò)誤的信號(hào)被引發(fā)。 如果將無(wú)效的定時(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 信號(hào)在 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ì)被取消。 如果返回值為零,則目前沒(méi)有任何警報(bào)被排入計(jì)劃。

可用性: Unix。 更多信息請(qǐng)參見(jiàn)手冊(cè)頁(yè)面 alarm(2)。

signal.getsignal(signalnum)?

返回當(dāng)前用于信號(hào) signalnum 的信號(hào)處理程序。 返回值可以是一個(gè) Python 可調(diào)用對(duì)象,或是特殊值 signal.SIG_IGN, signal.SIG_DFLNone 之一。 在這里,signal.SIG_IGN 表示信號(hào)在之前被忽略,signal.SIG_DFL 表示之前在使用默認(rèn)的信號(hào)處理方式,而 None 表示之前的信號(hào)處理程序未由 Python 安裝。

signal.strsignal(signalnum)?

返回信號(hào) signalnum 的系統(tǒng)描述,例如 "Interrupt", "Segmentation fault" 等等。 如果信號(hào)無(wú)法被識(shí)別則返回 None

3.8 新版功能.

signal.valid_signals()?

返回本平臺(tái)上的有效信號(hào)編號(hào)集。 這可能會(huì)少于 range(1, NSIG),如果某些信號(hào)被系統(tǒng)保留作為內(nèi)部使用的話。

3.8 新版功能.

signal.pause()?

使進(jìn)程休眠直至接收到一個(gè)信號(hào);然后將會(huì)調(diào)用適當(dāng)?shù)奶幚沓绦颉?返回空值。

可用性: Unix。 更多信息請(qǐng)參見(jiàn)手冊(cè)頁(yè)面 signal(2)

另請(qǐng)參閱 sigwait(), sigwaitinfo(), sigtimedwait()sigpending()。

signal.raise_signal(signum)?

向調(diào)用方進(jìn)程發(fā)送一個(gè)信號(hào)。 返回空值。

3.8 新版功能.

signal.pidfd_send_signal(pidfd, sig, siginfo=None, flags=0)?

發(fā)送信號(hào) sig 到文件描述符 pidfd 所指向的進(jìn)程。 Python 目前不支持 siginfo 形參;它必須為 None。 提供 flags 參數(shù)是為了將來(lái)擴(kuò)展;當(dāng)前未定義旗標(biāo)值。

更多信息請(qǐng)參閱 pidfd_send_signal(2) 手冊(cè)頁(yè)面。

可用性: Linux 5.1+

3.9 新版功能.

signal.pthread_kill(thread_id, signalnum)?

將信號(hào) signalnum 發(fā)送至與調(diào)用者在同一進(jìn)程中另一線程 thread_id。 目標(biāo)線程可被用于執(zhí)行任何代碼(Python或其它)。 但是,如果目標(biāo)線程是在執(zhí)行 Python 解釋器,則 Python 信號(hào)處理程序?qū)?由主解釋器的主線程來(lái)執(zhí)行。 因此,將信號(hào)發(fā)送給特定 Python 線程的唯一作用在于強(qiáng)制讓一個(gè)正在運(yùn)行的系統(tǒng)調(diào)用失敗并拋出 InterruptedError。

使用 threading.get_ident()threading.Thread 對(duì)象的 ident 屬性為 thread_id 獲取合適的值。

如果 signalnum 為 0,則不會(huì)發(fā)送信號(hào),但仍然會(huì)執(zhí)行錯(cuò)誤檢測(cè);這可被用來(lái)檢測(cè)目標(biāo)線程是否仍在運(yùn)行。

引發(fā)一個(gè) 審計(jì)事件 signal.pthread_kill,附帶參數(shù) thread_id, signalnum。

可用性: Unix。 更多信息請(qǐng)參見(jiàn)手冊(cè)頁(yè)面 pthread_kill(3)。

另請(qǐng)參閱 os.kill()。

3.3 新版功能.

signal.pthread_sigmask(how, mask)?

獲取和/或修改調(diào)用方線程的信號(hào)掩碼。 信號(hào)掩碼是一組傳送過(guò)程目前為調(diào)用者而阻塞的信號(hào)集。 返回舊的信號(hào)掩碼作為一組信號(hào)。

該調(diào)用的行為取決于 how 的值,具體見(jiàn)下。

  • SIG_BLOCK: 被阻塞信號(hào)集是當(dāng)前集與 mask 參數(shù)的并集。

  • SIG_UNBLOCK: mask 中的信號(hào)會(huì)從當(dāng)前已阻塞信號(hào)集中被移除。 允許嘗試取消對(duì)一個(gè)非阻塞信號(hào)的阻塞。

  • SIG_SETMASK: 已阻塞信號(hào)集會(huì)被設(shè)為 mask 參數(shù)的值。

mask 是一個(gè)信號(hào)編號(hào)集合 (例如 {signal.SIGINT, signal.SIGTERM})。 請(qǐng)使用 valid_signals() 表示包含所有信號(hào)的完全掩碼。

例如,signal.pthread_sigmask(signal.SIG_BLOCK, []) 會(huì)讀取調(diào)用方線程的信號(hào)掩碼。

SIGKILLSIGSTOP 不能被阻塞。

可用性: Unix。 更多信息請(qǐng)參見(jiàn)手冊(cè)頁(yè)面 sigprocmask(2)pthread_sigmask(3)。

另請(qǐng)參閱 pause(), sigpending()sigwait()

3.3 新版功能.

signal.setitimer(which, seconds, interval=0.0)?

設(shè)置由 which 指明的給定間隔計(jì)時(shí)器 (signal.ITIMER_REAL, signal.ITIMER_VIRTUALsignal.ITIMER_PROF 之一) 在 seconds 秒 (接受浮點(diǎn)數(shù)值,為與 alarm() 之差) 之后開始并在每 interval 秒間隔時(shí) (如果 interval 不為零) 啟動(dòng)。 由 which 指明的間隔計(jì)時(shí)器可通過(guò)將 seconds 設(shè)為零來(lái)清空。

當(dāng)一個(gè)間隔計(jì)時(shí)器啟動(dòng)時(shí),會(huì)有信號(hào)發(fā)送至進(jìn)程。 所發(fā)送的具體信號(hào)取決于所使用的計(jì)時(shí)器;signal.ITIMER_REAL 將發(fā)送 SIGALRM, signal.ITIMER_VIRTUAL 將發(fā)送 SIGVTALRM, 而 signal.ITIMER_PROF 將發(fā)送 SIGPROF.

原有的值會(huì)以元組: (delay, interval) 的形式被返回。

嘗試傳入無(wú)效的計(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)接收到信號(hào)時(shí),會(huì)將信號(hào)編號(hào)以單個(gè)字節(jié)的形式寫入 fd。 這可被其它庫(kù)用來(lái)喚醒一次 poll 或 select 調(diào)用,以允許該信號(hào)被完全地處理。

原有的喚醒 fd 會(huì)被返回(或者如果未啟用文件描述符喚醒則返回 -1)。 如果 fd 為 -1,文件描述符喚醒會(huì)被禁用。 如果不為 -1,則 fd 必須為非阻塞型。 需要由庫(kù)來(lái)負(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)有信號(hào)到達(dá)時(shí)你都是用 fd 來(lái)喚醒,但之后它們?cè)诖_定達(dá)到的一個(gè)或多個(gè)信號(hào) which 時(shí)存在差異。

在第一種方式下,我們從 fd 的緩沖區(qū)讀取數(shù)據(jù),這些字節(jié)值會(huì)給你信號(hào)編號(hào)。 這種方式很簡(jiǎn)單,但在少數(shù)情況下會(huì)發(fā)生問(wèn)題:通常 fd 將有緩沖區(qū)空間大小限制,如果信號(hào)到達(dá)得太多且太快,緩沖區(qū)可能會(huì)爆滿,有些信號(hào)可能丟失。 如果你使用此方式,則你應(yīng)當(dāng)設(shè)置 warn_on_full_buffer=True,當(dāng)信號(hào)丟失時(shí)這至少能將警告消息打印到 stderr。

在第二種方式下,我們 只會(huì) 將喚醒 fd 用于喚醒,而忽略實(shí)際的字節(jié)值。 在此情況下,我們所關(guān)心的只有 fd 的緩沖區(qū)為空還是不為空;爆滿的緩沖區(qū)完全不會(huì)導(dǎo)致問(wèn)題。 如果你使用此方式,則你應(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)用重啟行為:如果 flagFalse,系統(tǒng)調(diào)用將在被信號(hào) signalnum 中斷時(shí)重啟,否則系統(tǒng)調(diào)用將被中斷。 返回空值。

可用性: Unix。 更多信息請(qǐng)參見(jiàn)手冊(cè)頁(yè)面 siginterrupt(3)。

請(qǐng)注意用 signal() 安裝信號(hào)處理程序?qū)⒅貑⑿袨橹刂脼榭赏ㄟ^(guò)顯式調(diào)用 siginterrupt() 并為給定信號(hào)的 flag 設(shè)置真值來(lái)實(shí)現(xiàn)中斷。

signal.signal(signalnum, handler)?

將信號(hào) signalnum 的處理程序設(shè)為函數(shù) handler。 handler 可以為接受兩個(gè)參數(shù)(見(jiàn)下)的 Python 可調(diào)用對(duì)象,或者為特殊值 signal.SIG_IGNsignal.SIG_DFL 之一。 之前的信號(hào)處理程序?qū)⒈环祷兀▍⒁?jiàn)上文 getsignal() 的描述)。 (更多信息請(qǐng)參閱 Unix 手冊(cè)頁(yè)面 signal(2)。)

當(dāng)啟用線程用時(shí),此函數(shù)只能從 主解釋器的主線程 被調(diào)用;嘗試從另一線程調(diào)用它將導(dǎo)致 ValueError 異常被引發(fā)。

handler 將附帶兩個(gè)參數(shù)調(diào)用:信號(hào)編號(hào)和當(dāng)前堆棧幀 (None 或一個(gè)幀對(duì)象;有關(guān)幀對(duì)象的描述請(qǐng)參閱 類型層級(jí)結(jié)構(gòu)描述 或者參閱 inspect 模塊中的屬性描述)。

在 Windows 上,signal() 調(diào)用只能附帶 SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERMSIGBREAK。 任何其他值都將引發(fā) ValueError。 請(qǐng)注意不是所有系統(tǒng)都定義了同樣的信號(hào)名稱集合;如果一個(gè)信號(hào)名稱未被定義為 SIG* 模塊層級(jí)常量則將引發(fā) AttributeError。

signal.sigpending()?

檢查正在等待傳送給調(diào)用方線程的信號(hào)集合(即在阻塞期間被引發(fā)的信號(hào))。 返回正在等待的信號(hào)集合。

可用性: Unix。 更多信息請(qǐng)參見(jiàn)手冊(cè)頁(yè)面 sigpending(2)

另請(qǐng)參閱 pause(), pthread_sigmask()sigwait()。

3.3 新版功能.

signal.sigwait(sigset)?

掛起調(diào)用方線程的執(zhí)行直到信號(hào)集合 sigset 中指定的信號(hào)之一被傳送。 此函數(shù)會(huì)接受該信號(hào)(將其從等待信號(hào)列表中移除),并返回信號(hào)編號(hào)。

可用性: Unix。 更多信息請(qǐng)參見(jiàn)手冊(cè)頁(yè)面 sigwait(3)。

另請(qǐng)參閱 pause(), pthread_sigmask(), sigpending(), sigwaitinfo()sigtimedwait()。

3.3 新版功能.

signal.sigwaitinfo(sigset)?

掛起調(diào)用方線程的執(zhí)行直到信號(hào)集合 sigset 中指定的信號(hào)之一被傳送。 此函數(shù)會(huì)接受該信號(hào)并將其從等待信號(hào)列表中移除。 如果 sigset 中的信號(hào)之一已經(jīng)在等待調(diào)用方線程,此函數(shù)將立即返回并附帶有關(guān)該信號(hào)的信息。 被傳送信號(hào)的信號(hào)處理程序不會(huì)被調(diào)用。 如果該函數(shù)被某個(gè)不在 sigset 中的信號(hào)中斷則會(huì)引發(fā) InterruptedError

返回值是一個(gè)代表 siginfo_t 結(jié)構(gòu)體所包含數(shù)據(jù)的對(duì)象,具體為: si_signo, si_code, si_errno, si_pid, si_uid, si_status, si_band。

可用性: Unix。 更多信息請(qǐng)參見(jiàn)手冊(cè)頁(yè)面 sigwaitinfo(2)。

另請(qǐng)參閱 pause(), sigwait()sigtimedwait()。

3.3 新版功能.

在 3.5 版更改: 當(dāng)被某個(gè) 不在 sigset 中的信號(hào)中斷時(shí)本函數(shù)將進(jìn)行重試并且信號(hào)處理程序不會(huì)引發(fā)異常(請(qǐng)參閱 PEP 475 了解其理由)。

signal.sigtimedwait(sigset, timeout)?

類似于 sigwaitinfo(),但會(huì)接受一個(gè)額外的 timeout 參數(shù)來(lái)指定超時(shí)限制。 如果將 timeout 指定為 0,則會(huì)執(zhí)行輪詢。 如果發(fā)生超時(shí)則返回 None。

可用性: Unix。 更多信息請(qǐng)參見(jiàn)手冊(cè)頁(yè)面 sigtimedwait(2)。

另請(qǐng)參閱 pause(), sigwait()sigwaitinfo()。

3.3 新版功能.

在 3.5 版更改: 現(xiàn)在當(dāng)此函數(shù)被某個(gè)不在 sigset 中的信號(hào)中斷時(shí)將以計(jì)算出的 timeout 進(jìn)行重試并且信號(hào)處理程序不會(huì)引發(fā)異常(請(qǐng)參閱 PEP 475 了解其理由)。

Examples?

這是一個(gè)最小示例程序。 它使用 alarm() 函數(shù)來(lái)限制等待打開一個(gè)文件所花費(fèi)的時(shí)間;這在文件為無(wú)法開啟的串行設(shè)備時(shí)會(huì)很有用處,此情況通常會(huì)導(dǎo)致 os.open() 無(wú)限期地掛起。 解決辦法是在打開文件之前設(shè)置 5 秒鐘的 alarm;如果操作耗時(shí)過(guò)長(zhǎng),將會(huì)發(fā)送 alarm 信號(hào),并且處理程序會(huì)引發(fā)一個(gè)異常。

import signal, os

def handler(signum, frame):
    signame = signal.Signals(signum).name
    print(f'Signal handler called with signal {signame} ({signum})')
    raise OSError("Couldn't open device!")

# Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)

# This open() may hang indefinitely
fd = os.open('/dev/ttyS0', os.O_RDWR)

signal.alarm(0)          # Disable the alarm

對(duì)于 SIGPIPE 的說(shuō)明?

將你的程序用管道輸出到工具例如 head(1) 將會(huì)導(dǎo)致 SIGPIPE 信號(hào)在其標(biāo)準(zhǔn)輸出的接收方提前關(guān)閉時(shí)被發(fā)送到你的進(jìn)程。 這將引發(fā)一個(gè)異常例如 BrokenPipeError: [Errno 32] Broken pipe。 要處理這種情況,請(qǐng)對(duì)你的入口點(diǎn)進(jìn)行包裝以捕獲此異常,如下所示:

import os
import sys

def 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 shutdown
        devnull = os.open(os.devnull, os.O_WRONLY)
        os.dup2(devnull, sys.stdout.fileno())
        sys.exit(1)  # Python exits with error code 1 on EPIPE

if __name__ == '__main__':
    main()

Do not set SIGPIPE's disposition to SIG_DFL in order to avoid BrokenPipeError. Doing that would cause your program to exit unexpectedly whenever any socket connection is interrupted while your program is still writing to it.

Note on Signal Handlers and Exceptions?

If a signal handler raises an exception, the exception will be propagated to the main thread and may be raised after any bytecode instruction. Most notably, a KeyboardInterrupt may appear at any point during execution. Most Python code, including the standard library, cannot be made robust against this, and so a KeyboardInterrupt (or any other exception resulting from a signal handler) may on rare occasions put the program in an unexpected state.

To illustrate this issue, consider the following code:

class SpamContext:
    def __init__(self):
        self.lock = threading.Lock()

    def __enter__(self):
        # If KeyboardInterrupt occurs here, everything is fine
        self.lock.acquire()
        # If KeyboardInterrupt occcurs here, __exit__ will not be called
        ...
        # KeyboardInterrupt could occur just before the function returns

    def __exit__(self, exc_type, exc_val, exc_tb):
        ...
        self.lock.release()

For many programs, especially those that merely want to exit on KeyboardInterrupt, this is not a problem, but applications that are complex or require high reliability should avoid raising exceptions from signal handlers. They should also avoid catching KeyboardInterrupt as a means of gracefully shutting down. Instead, they should install their own SIGINT handler. Below is an example of an HTTP server that avoids KeyboardInterrupt:

import signal
import socket
from selectors import DefaultSelector, EVENT_READ
from http.server import HTTPServer, SimpleHTTPRequestHandler

interrupt_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)
                return
            if key.fileobj == httpd:
                httpd.handle_request()

print("Serving on port 8000")
httpd = HTTPServer(('', 8000), SimpleHTTPRequestHandler)
serve_forever(httpd)
print("Shutdown...")