signal —非同期イベントのハンドラーを設定します—Pythonドキュメント

提供:Dev Guides
< PythonPython/docs/3.9/library/signal
移動先:案内検索

signal —非同期イベントのハンドラーを設定します


このモジュールは、Pythonでシグナルハンドラーを使用するメカニズムを提供します。

一般的なルール

signal.signal()関数を使用すると、シグナルを受信したときに実行されるカスタムハンドラーを定義できます。 少数のデフォルトハンドラーがインストールされます。 SIGPIPE は無視され(したがって、パイプとソケットの書き込みエラーは通常のPython例外として報告されます)、 SIGINTKeyboardInterruptに変換されます。 親プロセスが変更していない場合の例外。

特定のシグナルのハンドラーは、一度設定されると、明示的にリセットされるまでインストールされたままになります(Pythonは、基になる実装に関係なくBSDスタイルのインターフェースをエミュレートします)。ただし、基になる SIGCHLD のハンドラーは例外です。実装。

Pythonシグナルハンドラーの実行

Pythonシグナルハンドラーは、低レベル(C)シグナルハンドラー内では実行されません。 代わりに、低レベルのシグナルハンドラーは、仮想マシンに対応するPythonシグナルハンドラーを後の時点(たとえば、次のバイトコード命令)で実行するように指示するフラグを設定します。 これは結果をもたらします:

  • Cコードの無効な操作によって引き起こされる SIGFPESIGSEGV のような同期エラーをキャッチすることはほとんど意味がありません。 PythonはシグナルハンドラーからCコードに戻ります。これにより、同じシグナルが再び発生する可能性があり、Pythonが明らかにハングします。 Python 3.3以降では、 faulthandler モジュールを使用して同期エラーを報告できます。
  • 純粋にCで実装された長時間実行の計算(大量のテキストでの正規表現マッチングなど)は、受信した信号に関係なく、任意の時間中断することなく実行できます。 計算が終了すると、Pythonシグナルハンドラーが呼び出されます。


シグナルとスレッド

Pythonシグナルハンドラーは、シグナルが別のスレッドで受信された場合でも、常にメインインタープリターのメインPythonスレッドで実行されます。 これは、シグナルをスレッド間通信の手段として使用できないことを意味します。 代わりに、 threading モジュールの同期プリミティブを使用できます。

さらに、メインインタープリターのメインスレッドのみが新しいシグナルハンドラーを設定できます。


モジュールの内容

バージョン3.5で変更:シグナル(SIG *)、ハンドラー( SIG_DFLSIG_IGN )、およびsigmask( SIG_BLOCKSIG_UNBLOCKSIG_SETMASK )に関連する以下の定数は、列挙型に変換されました。 getsignal()pthread_sigmask()sigpending()、および sigwait()関数は、人間が読める形式の列挙型[X139X ]。


signal モジュールで定義されている変数は次のとおりです。

signal.SIG_DFL
これは、2つの標準的な信号処理オプションの1つです。 シグナルのデフォルト機能を実行するだけです。 たとえば、ほとんどのシステムでは、SIGQUITのデフォルトのアクションはコアをダンプして終了することですが、 SIGCHLD のデフォルトのアクションは単にそれを無視することです。
signal.SIG_IGN
これは別の標準的なシグナルハンドラーであり、指定されたシグナルを単に無視します。
signal.SIGABRT
abort(3)からの信号を中止します。
signal.SIGALRM
alarm(2)からのタイマー信号。
signal.SIGBREAK
キーボードからの割り込み(CTRL + BREAK)。
signal.SIGBUS
バスエラー(メモリアクセス不良)。
signal.SIGCHLD
子プロセスが停止または終了しました。
signal.SIGCLD
SIGCHLD のエイリアス。
signal.SIGCONT
現在停止している場合は、プロセスを続行します
signal.SIGFPE

浮動小数点例外。 たとえば、ゼロによる除算。

も参照してください

ZeroDivisionError は、除算またはモジュロ演算の2番目の引数がゼロの場合に発生します。

signal.SIGHUP
制御端末でのハングアップまたは制御プロセスの停止が検出されました。
signal.SIGILL
違法な指示。
signal.SIGINT

キーボードからの割り込み(CTRL + C)。

デフォルトのアクションは、 KeyboardInterrupt を上げることです。

signal.SIGKILL

信号を殺します。

キャッチ、ブロック、または無視することはできません。

signal.SIGPIPE

壊れたパイプ:リーダーなしでパイプに書き込みます。

デフォルトのアクションは、信号を無視することです。

signal.SIGSEGV
セグメンテーション違反:無効なメモリ参照。
signal.SIGTERM
終了信号。
signal.SIGUSR1
ユーザー定義の信号1。
signal.SIGUSR2
ユーザー定義の信号2。
signal.SIGWINCH
ウィンドウサイズ変更信号。
SIG*
すべての信号番号はシンボリックに定義されています。 たとえば、ハングアップ信号は signal.SIGHUP として定義されます。 変数名は、<signal.h>にあるように、Cプログラムで使用される名前と同じです。 'signal()'のUnixマニュアルページには、既存のシグナルがリストされています(一部のシステムでは、これは signal(2)であり、他のシステムでは、リストは signal(7)にあります。 )。 すべてのシステムが同じ信号名のセットを定義しているわけではないことに注意してください。 システムによって定義された名前のみがこのモジュールによって定義されます。
signal.CTRL_C_EVENT

Ctrl + C キーストロークイベントに対応する信号。 この信号は、 os.kill()でのみ使用できます。

バージョン3.2の新機能。

signal.CTRL_BREAK_EVENT

Ctrl + Break キーストロークイベントに対応する信号。 この信号は、 os.kill()でのみ使用できます。

バージョン3.2の新機能。

signal.NSIG
最高信号番号の番号より1つ多い。
signal.ITIMER_REAL
インターバルタイマーをリアルタイムでデクリメントし、満了時に SIGALRM を配信します。
signal.ITIMER_VIRTUAL
プロセスの実行時にのみインターバルタイマーをデクリメントし、満了時にSIGVTALRMを配信します。
signal.ITIMER_PROF
プロセスの実行時とシステムがプロセスに代わって実行しているときの両方で、インターバルタイマーをデクリメントします。 ITIMER_VIRTUALと組み合わせて、このタイマーは通常、ユーザーおよびカーネル空間でアプリケーションが費やした時間をプロファイリングするために使用されます。 SIGPROFは有効期限が切れると配信されます。
signal.SIG_BLOCK

how パラメーターから pthread_sigmask()への可能な値は、信号がブロックされることを示します。

バージョン3.3の新機能。

signal.SIG_UNBLOCK

how パラメーターから pthread_sigmask()への可能な値は、信号のブロックが解除されることを示します。

バージョン3.3の新機能。

signal.SIG_SETMASK

how パラメーターの pthread_sigmask()への可能な値は、シグナルマスクが置き換えられることを示します。

バージョン3.3の新機能。

signal モジュールは、1つの例外を定義します。

exception signal.ItimerError

基になる setitimer()または getitimer()実装からのエラーを通知するために発生します。 無効なインターバルタイマーまたは負の時間が setitimer()に渡された場合、このエラーが発生する可能性があります。 このエラーは OSError のサブタイプです。

バージョン3.3の新機能:このエラーは、以前は IOError のサブタイプでしたが、現在は OSError のエイリアスになっています。

signal モジュールは、次の機能を定義します。

signal.alarm(time)
time がゼロ以外の場合、この関数は SIGALRM シグナルを time 秒でプロセスに送信するように要求します。 以前にスケジュールされたアラームはキャンセルされます(一度にスケジュールできるアラームは1つだけです)。 戻り値は、以前に設定されたアラームが配信されるまでの秒数です。 time がゼロの場合、アラームはスケジュールされず、スケジュールされたアラームはキャンセルされます。 戻り値がゼロの場合、現在アラームはスケジュールされていません。
signal.getsignal(signalnum)
シグナル signalnum の現在のシグナルハンドラーを返します。 戻り値は、呼び出し可能なPythonオブジェクト、または特別な値 signal.SIG_IGNsignal.SIG_DFL 、または None のいずれかです。 ここで、 signal.SIG_IGN は信号が以前に無視されたことを意味し、 signal.SIG_DFL は信号を処理するデフォルトの方法が以前に使用されていたことを意味し、Noneは以前のシグナルハンドラーがPythonからインストールされていないこと。
signal.strsignal(signalnum)

「割り込み」、「セグメンテーション違反」など、信号 signalnum のシステム記述を返します。 信号が認識されない場合は、 None を返します。

バージョン3.8の新機能。

signal.valid_signals()

このプラットフォームで有効な信号番号のセットを返します。 一部の信号がシステムによって内部使用のために予約されている場合、これはrange(1, NSIG)よりも小さくなる可能性があります。

バージョン3.8の新機能。

signal.pause()

シグナルが受信されるまでプロセスをスリープ状態にします。 次に、適切なハンドラーが呼び出されます。 何も返しません。

sigwait()sigwaitinfo()sigtimedwait()、および sigpending()も参照してください。

signal.raise_signal(signum)

呼び出しプロセスにシグナルを送信します。 何も返しません。

バージョン3.8の新機能。

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

ファイル記述子 pidfd によって参照されるプロセスにシグナル sig を送信します。 Pythonは現在、 siginfo パラメーターをサポートしていません。 Noneである必要があります。 flags 引数は、将来の拡張のために提供されています。 現在、フラグ値は定義されていません。

詳細については、 pidfd_send_signal(2)のマニュアルページを参照してください。

バージョン3.9の新機能。

signal.pthread_kill(thread_id, signalnum)

シグナル signalnum を、呼び出し元と同じプロセス内の別のスレッドであるスレッド thread_id に送信します。 ターゲットスレッドは、任意のコード(Pythonかどうか)を実行できます。 ただし、ターゲットスレッドがPythonインタープリターを実行している場合、Pythonシグナルハンドラーはメインインタープリターのメインスレッドによって実行されます。 したがって、特定のPythonスレッドにシグナルを送信する唯一のポイントは、実行中のシステムコールを InterruptedError で強制的に失敗させることです。

threading.get_ident()または threading.Thread オブジェクトの ident 属性を使用して、 thread_id の適切な値を取得します。

signalnum が0の場合、シグナルは送信されませんが、エラーチェックは引き続き実行されます。 これを使用して、ターゲットスレッドがまだ実行されているかどうかを確認できます。

os.kill()も参照してください。

バージョン3.3の新機能。

signal.pthread_sigmask(how, mask)

呼び出し元のスレッドのシグナルマスクをフェッチおよび/または変更します。 シグナルマスクは、発信者に対して現在配信がブロックされているシグナルのセットです。 古い信号マスクを信号のセットとして返します。

呼び出しの動作は、次のように how の値に依存します。

  • SIG_BLOCK :ブロックされた信号のセットは、現在のセットと mask 引数の和集合です。

  • SIG_UNBLOCKマスクの信号は、現在ブロックされている信号のセットから削除されます。 ブロックされていない信号のブロックを解除しようとしてもかまいません。

  • SIG_SETMASK :ブロックされた信号のセットは mask 引数に設定されます。

マスクは信号番号のセットです(例: { signal.SIGINTsignal.SIGTERM })。 すべての信号を含む完全なマスクには、 valid_signals()を使用します。

たとえば、signal.pthread_sigmask(signal.SIG_BLOCK, [])は、呼び出し元のスレッドのシグナルマスクを読み取ります。

SIGKILL およびSIGSTOPはブロックできません。

pause()sigpending()、および sigwait()も参照してください。

バージョン3.3の新機能。

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

で指定されたインターバルタイマー( signal.ITIMER_REALsignal.ITIMER_VIRTUALsignal.ITIMER_PROF のいずれか)をで指定しての後に起動するように設定します。 X168X]秒(フロートが受け入れられ、 alarm()とは異なります)、その後は間隔秒ごと(間隔がゼロ以外の場合)。 で指定されたインターバルタイマーは、をゼロに設定することでクリアできます。

インターバルタイマーが作動すると、信号がプロセスに送信されます。 送信される信号は、使用されているタイマーによって異なります。 signal.ITIMER_REALSIGALRM を配信し、 signal.ITIMER_VIRTUALSIGVTALRMを送信し、 signal.ITIMER_PROFSIGPROF

古い値はタプルとして返されます:(遅延、間隔)。

無効なインターバルタイマーを通過させようとすると、 ItimerError が発生します。

signal.getitimer(which)
which で指定されたインターバルタイマーの現在値を返します。
signal.set_wakeup_fd(fd, *, warn_on_full_buffer=True)

ウェイクアップファイル記述子を fd に設定します。 信号を受信すると、信号番号が1バイトとしてfdに書き込まれます。 これをライブラリで使用して、ポーリングまたは選択呼び出しをウェイクアップし、信号を完全に処理できるようにすることができます。

古いウェイクアップfdが返されます(またはファイル記述子ウェイクアップが有効になっていない場合は-1)。 fd が-1の場合、ファイル記述子のウェイクアップは無効になります。 -1でない場合、 fd は非ブロッキングである必要があります。 ポーリングを呼び出す前、または再度選択する前に、 fd からバイトを削除するのはライブラリの責任です。

スレッドが有効になっている場合、この関数はメインインタープリターのメインスレッドからのみ呼び出すことができます。 他のスレッドから呼び出そうとすると、 ValueError 例外が発生します。

この関数を使用する一般的な方法は2つあります。 どちらのアプローチでも、信号が到着したときにfdを使用してウェイクアップしますが、どの信号が到着したかを判断する方法が異なります。

最初のアプローチでは、fdのバッファからデータを読み取り、バイト値から信号番号を取得します。 これは単純ですが、まれに問題が発生する可能性があります。通常、fdのバッファスペースの量は限られており、到着する信号が多すぎると、バッファがいっぱいになり、一部の信号が失われる可能性があります。 このアプローチを使用する場合は、warn_on_full_buffer=Trueを設定する必要があります。これにより、少なくとも信号が失われたときにstderrに警告が出力されます。

2番目のアプローチでは、ウェイクアップにwakeup fd only を使用し、実際のバイト値を無視します。 この場合、私たちが気にするのは、fdのバッファが空か空でないかだけです。 バッファがいっぱいになっても、問題はまったくありません。 このアプローチを使用する場合は、warn_on_full_buffer=Falseを設定して、ユーザーが誤った警告メッセージで混乱しないようにする必要があります。

バージョン3.5で変更: Windowsでは、この関数はソケットハンドルもサポートするようになりました。

バージョン3.7で変更: warn_on_full_bufferパラメーターが追加されました。

signal.siginterrupt(signalnum, flag)

システムコールの再起動動作の変更:フラグFalse の場合、信号 signalnum によって中断されるとシステムコールが再開されます。それ以外の場合、システムコールは中断されます。 何も返しません。

signal()を使用してシグナルハンドラーをインストールすると、指定されたシグナルの真のフラグ値を使用してsiginterrupt()を暗黙的に呼び出すことにより、再起動動作が割り込み可能にリセットされることに注意してください。

signal.signal(signalnum, handler)

シグナル signalnum のハンドラーを関数 handler に設定します。 handler は、2つの引数(以下を参照)をとる呼び出し可能なPythonオブジェクト、または特別な値 signal.SIG_IGN または signal.SIG_DFL のいずれかです。 以前のシグナルハンドラーが返されます(上記の getsignal()の説明を参照してください)。 (詳細については、Unixのマニュアルページ signal(2)を参照してください。)

スレッドが有効になっている場合、この関数はメインインタープリターのメインスレッドからのみ呼び出すことができます。 他のスレッドから呼び出そうとすると、 ValueError 例外が発生します。

ハンドラーは、信号番号と現在のスタックフレーム(Noneまたはフレームオブジェクト。フレームオブジェクトの説明については、タイプのの説明を参照)の2つの引数で呼び出されます。階層または inspect モジュールの属性の説明を参照してください)。

Windowsでは、 signal()は、 SIGABRTSIGFPESIGILLSIGINT 、[ X123X] SIGSEGV 、 SIGTERM 、または SIGBREAK 。 それ以外の場合は、 ValueError が発生します。 すべてのシステムが同じ信号名のセットを定義しているわけではないことに注意してください。 信号名がSIG*モジュールレベル定数として定義されていない場合、 AttributeError が発生します。

signal.sigpending()

呼び出しスレッドへの配信が保留されている一連のシグナル(つまり、ブロック中に発生したシグナル)を調べます。 保留中のシグナルのセットを返します。

pause()pthread_sigmask()、および sigwait()も参照してください。

バージョン3.3の新機能。

signal.sigwait(sigset)

シグナルセット sigset で指定されたシグナルの1つが配信されるまで、呼び出しスレッドの実行を一時停止します。 この関数はシグナルを受け入れ(シグナルの保留リストから削除し)、シグナル番号を返します。

pause()pthread_sigmask()sigpending()sigwaitinfo()sigtimedwait()も参照してください。 ]。

バージョン3.3の新機能。

signal.sigwaitinfo(sigset)

シグナルセット sigset で指定されたシグナルの1つが配信されるまで、呼び出しスレッドの実行を一時停止します。 この関数はシグナルを受け入れ、保留中のシグナルのリストからそれを削除します。 sigset のシグナルの1つが呼び出し元のスレッドに対してすでに保留中の場合、関数はそのシグナルに関する情報をすぐに返します。 配信された信号に対してシグナルハンドラーは呼び出されません。 この関数は、 sigset にない信号によって中断された場合、 InterruptedError を発生させます。

戻り値は、siginfo_t構造に含まれるデータを表すオブジェクトです。つまり、si_signosi_codesi_errnosi_pidsi_uidsi_statussi_band

pause()sigwait()、および sigtimedwait()も参照してください。

バージョン3.3の新機能。

バージョン3.5で変更: sigset にないシグナルによって中断され、シグナルハンドラーが例外を発生させない場合、関数が再試行されるようになりました( を参照)。理論的根拠についてはPEP475 )。

signal.sigtimedwait(sigset, timeout)

sigwaitinfo()と同様ですが、タイムアウトを指定する追加の timeout 引数を取ります。 timeout0として指定されている場合、ポーリングが実行されます。 タイムアウトが発生した場合、 None を返します。

pause()sigwait()、および sigwaitinfo()も参照してください。

バージョン3.3の新機能。

バージョン3.5で変更: sigset にないシグナルによって中断され、シグナルハンドラーが例外を発生させない場合、関数は再計算された timeout で再試行されるようになりました(理論的根拠については、 PEP 475 を参照してください)。


これが最小限のサンプルプログラムです。 alarm()関数を使用して、ファイルを開くのを待つ時間を制限します。 これは、ファイルがオンになっていない可能性のあるシリアルデバイス用である場合に役立ちます。これにより、通常、 os.open()が無期限にハングします。 解決策は、ファイルを開く前に5秒のアラームを設定することです。 操作に時間がかかりすぎると、アラーム信号が送信され、ハンドラーが例外を発生させます。

import signal, os

def handler(signum, frame):
    print('Signal handler called with signal', 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

SIGPIPEに関する注記

プログラムの出力を head(1)などのツールにパイプすると、標準出力のレシーバーが早く閉じると、 SIGPIPE シグナルがプロセスに送信されます。 これにより、BrokenPipeError: [Errno 32] Broken pipeのような例外が発生します。 このケースを処理するには、次のようにエントリポイントをラップして、この例外をキャッチします。

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()

BrokenPipeError を回避するために、 SIGPIPE の処理を SIG_DFL に設定しないでください。 これを行うと、プログラムがまだ書き込みを行っている間にソケット接続が中断された場合でも、プログラムが予期せず終了します。