# `signal` --- 非同期イベントにハンドラーを設定する

ソースコード: Lib/signal.py

このモジュールでは Python でシグナルハンドラを使うための機構を提供します。

## 一般的なルール

`signal.signal()` 関数を使って、シグナルを受信した時に実行されるハンドラを定義することができます。 Python は標準でごく少数のシグナルハンドラをインストールしています: `SIGPIPE` は無視され (したがって、 pipe や socket に対する書き込みで生じたエラーは通常の Python 例外として報告されます)、 `SIGINT` は `KeyboardInterrupt` 例外に変換されます。親プロセスが変更していない場合は、これらはどれも上書きすることができます。

特定のシグナルに対するハンドラが一度設定されると、明示的にリセットしないかぎり設定されたままになります (Python は背後の実装系に関係なく BSD 形式のインターフェースをエミュレートします)。例外は `SIGCHLD` のハンドラで、この場合は背後の実装系の仕様に従います。

On WebAssembly platforms, signals are emulated and therefore behave differently. Several functions and signals are not available on these platforms.

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

Python のシグナルハンドラは、低水準 (C言語) のシグナルハンドラ内で実行されるわけではありません。代わりに、低水準のシグナルハンドラが virtual machine が対応する Python のシグナルハンドラを後から (例えば次の bytecode 命令時に) 実行するようにフラグを立てます:

- It makes little sense to catch synchronous errors like `SIGFPE` or `SIGSEGV` that are caused by an invalid operation in C code. Python will return from the signal handler to the C code, which is likely to raise the same signal again, causing Python to apparently hang. From Python 3.3 onwards, you can use the `faulthandler` module to report on synchronous errors.

- 完全にCで実装された長時間かかる計算 (大きいテキストに対する正規表現のマッチなど) は、どのシグナルを受信しても中断されないまま長時間実行され続ける可能性があります。Python のシグナルハンドラはその計算が終了してから呼び出されます。

- 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.

### シグナルとスレッド

Python のシグナルハンドラは、もしシグナルを受け取ったのが別のスレッドだったとしても、常にメインインタープリターの Python のメインスレッドで実行されます。このためシグナルをスレッド間通信に使うことはできません。代わりに `threading` モジュールが提供している同期プリミティブを利用できます。

また、メインインタープリターのメインスレッドだけが新しいシグナルハンドラを登録できます。

## モジュールの内容

バージョン 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.

  Added in version 3.5.

class signal.Handlers

  `enum.IntEnum` collection the constants `SIG_DFL` and `SIG_IGN`.

  Added in version 3.5.

class signal.Sigmasks

  `enum.IntEnum` collection the constants `SIG_BLOCK`, `SIG_UNBLOCK` and `SIG_SETMASK`.

  Availability: Unix.

  See the man page sigprocmask(2) and pthread_sigmask(3) for further information.

  Added in version 3.5.

以下に `signal` モジュールで定義されている変数を示します:

signal.SIG_DFL

  二つある標準シグナル処理オプションのうちの一つです; 単純にシグナルに対する標準の関数を実行します。例えば、ほとんどのシステムでは、 `SIGQUIT` に対する標準の動作はコアダンプと終了で、 `SIGCHLD` に対する標準の動作は単にシグナルの無視です。

signal.SIG_IGN

  もう一つの標準シグナル処理オプションで、受け取ったシグナルを単に無視します。

signal.SIGABRT

  Abort signal from abort(3).

signal.SIGALRM

  Timer signal from alarm(2).

  Availability: Unix.

signal.SIGBREAK

  Interrupt from keyboard (CTRL + BREAK).

  Availability: Windows.

signal.SIGBUS

  Bus error (bad memory access).

  Availability: Unix.

signal.SIGCHLD

  Child process stopped or terminated.

  Availability: Unix.

signal.SIGCLD

  `SIGCHLD` のエイリアスです。

  Availability: not macOS.

signal.SIGCONT

  Continue the process if it is currently stopped

  Availability: Unix.

signal.SIGFPE

  Floating-point exception. For example, division by zero.

  参考

  `ZeroDivisionError` is raised when the second argument of a division or modulo operation is zero.

signal.SIGHUP

  Hangup detected on controlling terminal or death of controlling process.

  Availability: Unix.

signal.SIGILL

  Illegal instruction.

signal.SIGINT

  Interrupt from keyboard (CTRL + C).

  Default action is to raise `KeyboardInterrupt`.

signal.SIGKILL

  Kill signal.

  It cannot be caught, blocked, or ignored.

  Availability: Unix.

signal.SIGPIPE

  Broken pipe: write to pipe with no readers.

  Default action is to ignore the signal.

  Availability: Unix.

signal.SIGSEGV

  Segmentation fault: invalid memory reference.

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.

  Added in version 3.11.

signal.SIGTERM

  Termination signal.

signal.SIGUSR1

  User-defined signal 1.

  Availability: Unix.

signal.SIGUSR2

  User-defined signal 2.

  Availability: Unix.

signal.SIGWINCH

  Window resize signal.

  Availability: Unix.

SIG*

  全てのシグナル番号はシンボル定義されています。例えば、ハングアップシグナルは `signal.SIGHUP` で定義されています; 変数名は C 言語のプログラムで使われているのと同じ名前で、 `<signal.h>` にあります。 '`signal()`' に関する Unix マニュアルページでは、システムで定義されているシグナルを列挙しています (あるシステムではリストは signal(2) に、別のシステムでは signal(7) に列挙されています)。全てのシステムで同じシグナル名のセットを定義しているわけではないので注意してください; このモジュールでは、システムで定義されているシグナル名だけを定義しています。

signal.CTRL_C_EVENT

  `CTRL`+`C` キーストロークに該当するシグナル。このシグナルは `os.kill()` でだけ利用できます。

  Availability: Windows.

  Added in version 3.2.

signal.CTRL_BREAK_EVENT

  `CTRL`+`BREAK` キーストロークに該当するシグナル。このシグナルは `os.kill()` でだけ利用できます。

  Availability: Windows.

  Added in version 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

  実時間でデクリメントするインターバルタイマーです。タイマーが発火したときに `SIGALRM` を送ります。

signal.ITIMER_VIRTUAL

  プロセスの実行時間だけデクリメントするインターバルタイマーです。タイマーが発火したときに `SIGVTALRM` を送ります。

signal.ITIMER_PROF

  プロセスの実行中と、システムがそのプロセスのために実行している時間だけデクリメントするインターバルタイマーです。ITIMER_VIRTUAL と組み合わせて、このタイマーはよくアプリケーションがユーザー空間とカーネル空間で消費した時間のプロファイリングに利用されます。タイマーが発火したときに `SIGPROF` を送ります。

signal.SIG_BLOCK

  `pthread_sigmask()` の how 引数に渡せる値で、シグナルがブロックされることを意味します。

  Added in version 3.3.

signal.SIG_UNBLOCK

  `pthread_sigmask()` の how 引数に渡せる値で、シグナルがブロック解除されることを意味します。

  Added in version 3.3.

signal.SIG_SETMASK

  `pthread_sigmask()` の how 引数に渡せる値で、シグナルが置換されることを意味します。

  Added in version 3.3.

`signal` モジュールは1つの例外を定義しています:

exception signal.ItimerError

  背後の `setitimer()` または `getitimer()` 実装からエラーを通知するために送出されます。無効なインタバルタイマーや負の時間が `setitimer()` に渡された場合、このエラーを予期してください。このエラーは `OSError` を継承しています。

  Added in version 3.3: このエラーは以前は `IOError` のサブタイプでしたが、 `OSError` のエイリアスになりました。

`signal` モジュールでは以下の関数を定義しています:

signal.alarm(time)

  time がゼロでない値の場合、この関数は time 秒後頃に `SIGALRM` をプロセスに送るように要求します。それ以前にスケジュールしたアラームはキャンセルされます (常に一つのアラームしかスケジュールできません)。この場合、戻り値は以前に設定されたアラームシグナルが通知されるまであと何秒だったかを示す値です。 time がゼロの場合、アラームは一切スケジュールされず、現在スケジュールされているアラームがキャンセルされます。戻り値がゼロの場合、現在アラームがスケジュールされていないことを示します。

  Availability: Unix.

  See the man page alarm(2) for further information.

signal.getsignal(signalnum)

  シグナル signalnum に対する現在のシグナルハンドラを返します。戻り値は呼び出し可能な Python オブジェクトか、 `signal.SIG_IGN`、 `signal.SIG_DFL`、および `None` といった特殊な値のいずれかです。ここで `signal.SIG_IGN` は以前そのシグナルが無視されていたことを示し、 `signal.SIG_DFL` は以前そのシグナルの標準の処理方法が使われていたことを示し、 `None` はシグナルハンドラがまだ Python によってインストールされていないことを示します。

signal.strsignal(signalnum)

  Returns the description of signal signalnum, such as "Interrupt" for `SIGINT`. Returns `None` if signalnum has no description. Raises `ValueError` if signalnum is invalid.

  Added in version 3.8.

signal.valid_signals()

  Return the set of valid signal numbers on this platform. This can be less than `range(1, NSIG)` if some signals are reserved by the system for internal use.

  Added in version 3.8.

signal.pause()

  シグナルを受け取るまでプロセスを一時停止します; その後、適切なハンドラが呼び出されます。戻り値はありません。

  Availability: Unix.

  See the man page signal(2) for further information.

  `sigwait()`, `sigwaitinfo()`, `sigtimedwait()` `sigpending()` も参照してください。

signal.raise_signal(signum)

  Sends a signal to the calling process. Returns nothing.

  Added in version 3.8.

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

  Send signal sig to the process referred to by file descriptor pidfd. Python does not currently support the siginfo parameter; it must be `None`. The flags argument is provided for future extensions; no flag values are currently defined.

  さらに詳しい情報についてはオンラインマニュアルページ pidfd_send_signal(2) を参照してください。

  Availability: Linux >= 5.1, Android >= `build-time` API level 31

  Added in version 3.9.

signal.pthread_kill(thread_id, signalnum)

  Send the signal signalnum to the thread thread_id, another thread in the same process as the caller. The target thread can be executing any code (Python or not). However, if the target thread is executing the Python interpreter, the Python signal handlers will be executed by the main thread of the main interpreter. Therefore, the only point of sending a signal to a particular Python thread would be to force a running system call to fail with `InterruptedError`.

  Use `threading.get_ident()` or the `ident` attribute of `threading.Thread` objects to get a suitable value for thread_id.

  If signalnum is 0, then no signal is sent, but error checking is still performed; this can be used to check if the target thread is still running.

  引数 `thread_id`, `signalnum` を指定して 監査イベント `signal.pthread_kill` を送出します。

  Availability: Unix.

  See the man page pthread_kill(3) for further information.

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

  Added in version 3.3.

signal.pthread_sigmask(how, mask)

  これを呼び出すスレッドにセットされているシグナルマスクを取り出したり変更したりします。シグナルマスクは、呼び出し側のために現在どのシグナルの配送がブロックされているかを示す集合 (set) です。呼び出し前のもとのシグナルマスクを集合として返却します。

  この関数の振る舞いは how に依存して以下のようになります。

  - `SIG_BLOCK`: mask で指定されるシグナルが現時点のシグナルマスクに追加されます。

  - `SIG_UNBLOCK`: mask で指定されるシグナルが現時点のシグナルマスクから取り除かれます。もともとブロックされていないシグナルをブロック解除しようとしても問題ありません。

  - `SIG_SETMASK`: シグナルマスク全体を mask としてセットします。

  mask はシグナル番号の集合です (例えば {`signal.SIGINT`, `signal.SIGTERM`})。全てのシグナルを含む全集合として `valid_signals()` を使うことが出来ます。

  呼び出しスレッドにセットされたシグナルマスクを問い合わせるには例えば `signal.pthread_sigmask(signal.SIG_BLOCK, [])` とします。

  `SIGKILL` and `SIGSTOP` cannot be blocked.

  Availability: Unix.

  See the man page sigprocmask(2) and pthread_sigmask(3) for further information.

  `pause()`, `sigpending()`, `sigwait()` も参照して下さい。

  Added in version 3.3.

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

  which で指定されたタイマー (`signal.ITIMER_REAL`, `signal.ITIMER_VIRTUAL`, `signal.ITIMER_PROF` のどれか) を、 seconds 秒後と (`alarm()` と異なり、floatを指定できます)、それから (interval が0でなければ) interval 秒間隔で起動するように設定します。 seconds に0を指定すると、which で指定されたタイマーをクリアすることができます。

  インターバルタイマーが起動したとき、シグナルがプロセスに送られます。送られるシグナルは利用されたタイマーの種類に依存します。 `signal.ITIMER_REAL` の場合は `SIGALRM` が、 `signal.ITIMER_VIRTUAL` の場合は `SIGVTALRM` が、 `signal.ITIMER_PROF` の場合は `SIGPROF` が送られます。

  以前の値が (delay, interval) のタプルとして返されます。

  無効なインターバルタイマーを渡すと `ItimerError` 例外が発生します。

  Availability: Unix.

signal.getitimer(which)

  which で指定されたインターバルタイマーの現在の値を返します。

  Availability: Unix.

signal.set_wakeup_fd(fd, *, warn_on_full_buffer=True)

  Set the wakeup file descriptor to fd. When a signal your program has registered a signal handler for is received, the signal number is written as a single byte into the fd. If you haven't registered a signal handler for the signals you care about, then nothing will be written to the wakeup fd. This can be used by a library to wakeup a poll or select call, allowing the signal to be fully processed.

  The old wakeup fd is returned (or -1 if file descriptor wakeup was not enabled). If fd is -1, file descriptor wakeup is disabled. If not -1, fd must be non-blocking. It is up to the library to remove any bytes from fd before calling poll or select again.

  スレッドが有効な場合、この関数は メインインタープリターのメインスレッド からしか実行できません。それ以外のスレッドからこの関数を実行しようとすると `ValueError` 例外が発生します。

  There are two common ways to use this function. In both approaches, you use the fd to wake up when a signal arrives, but then they differ in how they determine which signal or signals have arrived.

  In the first approach, we read the data out of the fd's buffer, and the byte values give you the signal numbers. This is simple, but in rare cases it can run into a problem: generally the fd will have a limited amount of buffer space, and if too many signals arrive too quickly, then the buffer may become full, and some signals may be lost. If you use this approach, then you should set `warn_on_full_buffer=True`, which will at least cause a warning to be printed to stderr when signals are lost.

  In the second approach, we use the wakeup fd only for wakeups, and ignore the actual byte values. In this case, all we care about is whether the fd's buffer is empty or non-empty; a full buffer doesn't indicate a problem at all. If you use this approach, then you should set `warn_on_full_buffer=False`, so that your users are not confused by spurious warning messages.

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

  バージョン 3.7 で変更: `warn_on_full_buffer` 引数が追加されました。

signal.siginterrupt(signalnum, flag)

  システムコールのリスタートの動作を変更します。 flag が `False` の場合、 signalnum シグナルに中断されたシステムコールは再実行されます。それ以外の場合、システムコールは中断されます。戻り値はありません。

  Availability: Unix.

  See the man page siginterrupt(3) for further information.

  Note that installing a signal handler with `signal()` will reset the restart behaviour to interruptible by implicitly calling `siginterrupt()` with a true flag value for the given signal.

signal.signal(signalnum, handler)

  シグナル signalnum に対するハンドラを関数 handler にします。 handler は二つの引数 (下記参照) を取る呼び出し可能な Python オブジェクトか、 `signal.SIG_IGN` あるいは `signal.SIG_DFL` といった特殊な値にすることができます。以前に使われていたシグナルハンドラが返されます (上記の `getsignal()` の記述を参照してください)。 (さらに詳しい情報については Unix マニュアルページ signal(2) を参照してください。)

  スレッドが有効な場合、この関数は メインインタープリターのメインスレッド からしか実行できません。それ以外のスレッドからこの関数を実行しようとすると `ValueError` 例外が発生します。

  handler は二つの引数とともに呼び出されます: シグナル番号、および現在のスタックフレーム (`None` またはフレームオブジェクト; フレームオブジェクトについての記述は 標準型の階層における説明 か、 `inspect` モジュールの属性の説明を参照してください)。

  On Windows, `signal()` can only be called with `SIGABRT`, `SIGFPE`, `SIGILL`, `SIGINT`, `SIGSEGV`, `SIGTERM`, or `SIGBREAK`. A `ValueError` will be raised in any other case. Note that not all systems define the same set of signal names; an `AttributeError` will be raised if a signal name is not defined as `SIG*` module level constant.

signal.sigpending()

  呼び出しスレッドで配送が保留されているシグナル (つまり配送がブロックされている間に発生したシグナル) の集合を調べます。保留中のシグナルの集合を返します。

  Availability: Unix.

  See the man page sigpending(2) for further information.

  `pause()`, `pthread_sigmask()`, `sigwait()` も参照して下さい。

  Added in version 3.3.

signal.sigwait(sigset)

  sigset 集合で指定されたシグナルのうちどれか一つが届くまで呼び出しスレッドを一時停止します。この関数はそのシグナルを受け取ると (それを保留シグナルリストから取り除いて) そのシグナル番号を返します。

  Availability: Unix.

  See the man page sigwait(3) for further information.

  `pause()`, `pthread_sigmask()`, `sigpending()`, `sigwaitinfo()`, `sigtimedwait()` も参照して下さい。

  Added in version 3.3.

signal.sigwaitinfo(sigset)

  Suspend execution of the calling thread until the delivery of one of the signals specified in the signal set sigset. The function accepts the signal and removes it from the pending list of signals. If one of the signals in sigset is already pending for the calling thread, the function will return immediately with information about that signal. The signal handler is not called for the delivered signal. The function raises an `InterruptedError` if it is interrupted by a signal that is not in sigset.

  The return value is an object representing the data contained in the `siginfo_t` structure, namely: `si_signo`, `si_code`, `si_errno`, `si_pid`, `si_uid`, `si_status`, `si_band`.

  Availability: Unix.

  See the man page sigwaitinfo(2) for further information.

  `pause()`, `sigwait()`, `sigtimedwait()` も参照して下さい。

  Added in version 3.3.

  バージョン 3.5 で変更: The function is now retried if interrupted by a signal not in sigset and the signal handler does not raise an exception (see PEP 475 for the rationale).

signal.sigtimedwait(sigset, timeout)

  Like `sigwaitinfo()`, but takes an additional timeout argument specifying a timeout. If timeout is specified as `0`, a poll is performed. Returns `None` if a timeout occurs.

  Availability: Unix.

  See the man page sigtimedwait(2) for further information.

  `pause()`, `sigwait()`, `sigwaitinfo()` も参照して下さい。

  Added in version 3.3.

  バージョン 3.5 で変更: The function is now retried with the recomputed timeout if interrupted by a signal not in sigset and the signal handler does not raise an exception (see PEP 475 for the rationale).

## 使用例

以下は最小限のプログラム例です。この例では `alarm()` を使ってファイルを開く処理を待つのに費やす時間を制限します; 例えば、電源の入っていないシリアルデバイスを開こうとすると、通常 `os.open()` は未定義の期間ハングアップしてしまいますが、この方法はそうした場合に便利です。ここではファイルを開くまで 5 秒間のアラームを設定することで解決しています; ファイルを開く処理が長くかかりすぎると、アラームシグナルが送信され、ハンドラが例外を送出するようになっています。

    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

## Note on SIGPIPE

Piping output of your program to tools like head(1) will cause a `SIGPIPE` signal to be sent to your process when the receiver of its standard output closes early. This results in an exception like `BrokenPipeError: [Errno 32] Broken pipe`. To handle this case, wrap your entry point to catch this exception as follows:

    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 occurs 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...")