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

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

======================================================================

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


一般的なルール
==============

"signal.signal()" 関数で、シグナルを受信したときに実行するハンドラーを
定義できます。 デフォルトでは少数のハンドラーが設定されています。
"SIGPIPE" は無視されます。 これにより、パイプやソケットへの書き込みエ
ラーを通常の Python 例外として報告できます。 親プロセスが処理方法を変
更していなければ、"SIGINT" は "KeyboardInterrupt" 例外に変換されます。

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

WebAssembly プラットフォームではシグナルをエミュレートするため、動作が
異なります。 これらのプラットフォームでは一部の関数とシグナルを利用で
きません。


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

Python のシグナルハンドラーは、低水準の (C の) シグナルハンドラー内で
は実行されません。 低水準のシグナルハンドラーはフラグを設定し、
*virtual machine* に対して、後の時点 (例えば次の *bytecode* 命令の実行
時) に対応する Python のシグナルハンドラーを実行するよう通知します。
この仕組みにより、次のような動作になります。

* C コードの不正な操作によって生じる "SIGFPE" や "SIGSEGV" などの同期
  的なエラーを捕捉しても、ほとんど意味がありません。 シグナルハンドラ
  ーから C コードに戻ると同じシグナルが再び発生する可能性が高く、
  Python が停止したように見えるためです。 Python 3.3 以降では
  "faulthandler" モジュールで同期的なエラーを報告できます。

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

* ハンドラーが例外を送出すると、メインスレッドではその時点で実行してい
  た処理とは無関係に例外が発生します。 詳しくは 後述の注意事項 を参照
  してください。


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

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

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

警告:

  "threading.Lock" などの同期プリミティブをシグナルハンドラー内で使わ
  ないでください。 予期しないデッドロックを引き起こすおそれがあります
  。


モジュールの内容
================

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

signal モジュールは三つの列挙型を定義します。

class signal.Signals

   SIG* 定数と CTRL_* 定数を集めた "enum.IntEnum" です。

   Added in version 3.5.

class signal.Handlers

   "SIG_DFL" と "SIG_IGN" の定数を集めた "enum.IntEnum" です。

   Added in version 3.5.

class signal.Sigmasks

   "SIG_BLOCK"、"SIG_UNBLOCK"、"SIG_SETMASK" の定数を集めた
   "enum.IntEnum" です。

   利用可能な環境: Unix.

   詳細は *sigprocmask(2)* と *pthread_sigmask(3)* のマニュアルページ
   を参照してください。

   Added in version 3.5.

"signal" モジュールでは以下の変数を定義します。

signal.SIG_DFL

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

signal.SIG_IGN

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

signal.SIGABRT

   *abort(3)* による中止シグナル。

signal.SIGALRM

   *alarm(2)* によるタイマーシグナル。

   利用可能な環境: Unix.

signal.SIGBREAK

   キーボードによる割り込み (CTRL + BREAK)。

   利用可能な環境: Windows.

signal.SIGBUS

   バスエラー (不正なメモリアクセス)。

   利用可能な環境: Unix.

signal.SIGCHLD

   子プロセスの停止または終了。

   利用可能な環境: Unix.

signal.SIGCLD

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

   利用可能な環境: not macOS.

signal.SIGCONT

   停止中のプロセスの実行を再開する

   利用可能な環境: Unix.

signal.SIGFPE

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

   参考: 除算や剰余演算の二つ目の引数がゼロの場合、"ZeroDivisionError" が
       送出されます。

signal.SIGHUP

   制御端末のハングアップの検出、または制御プロセスの終了。

   利用可能な環境: Unix.

signal.SIGILL

   不正な命令。

signal.SIGINT

   キーボードによる割り込み (CTRL + C)。

   デフォルトの動作は "KeyboardInterrupt" の送出です。

signal.SIGKILL

   強制終了シグナル。

   捕捉、ブロック、無視はできません。

   利用可能な環境: Unix.

signal.SIGPIPE

   パイプの切断: 読み取り側のいないパイプへの書き込み。

   デフォルトの動作はシグナルの無視です。

   利用可能な環境: Unix.

signal.SIGPROF

   プロファイリングタイマーの満了。

   利用可能な環境: Unix.

signal.SIGQUIT

   端末からの終了シグナル。

   利用可能な環境: Unix.

signal.SIGSEGV

   セグメンテーション違反: 不正なメモリ参照。

signal.SIGSTOP

   実行の停止 (捕捉や無視はできません)。

   利用可能な環境: Unix.

signal.SIGSTKFLT

   コプロセッサーのスタック障害。 Linux カーネルはこのシグナルを発生さ
   せず、ユーザー空間からのみ発生させられます。

   利用可能な環境: Linux.

   このシグナルを利用できるアーキテクチャーに限ります。 詳細は
   *signal(7)* のマニュアルページを参照してください。

   Added in version 3.11.

signal.SIGTERM

   終了シグナル。

signal.SIGUSR1

   ユーザー定義シグナル 1。

   利用可能な環境: Unix.

signal.SIGUSR2

   ユーザー定義シグナル 2。

   利用可能な環境: Unix.

signal.SIGVTALRM

   仮想タイマーの満了。

   利用可能な環境: Unix.

signal.SIGWINCH

   ウィンドウサイズ変更シグナル。

   利用可能な環境: Unix.

signal.SIGXCPU

   CPU 時間の制限超過。

   利用可能な環境: Unix.

SIG*

   すべてのシグナル番号はシンボルとして定義されています。 例えばハング
   アップシグナルは "signal.SIGHUP" として定義されています。 変数名は
   "<signal.h>" にある C プログラム用の名前と同じです。 Unix の
   "signal" マニュアルページには、利用可能なシグナルが列挙されています
   (システムによって *signal(2)* または *signal(7)* に記載されています
   )。 すべてのシステムが同じシグナル名を定義しているわけではなく、こ
   のモジュールで定義されるのはそのシステムで定義されている名前だけで
   す。

signal.CTRL_C_EVENT

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

   利用可能な環境: Windows.

   Added in version 3.2.

signal.CTRL_BREAK_EVENT

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

   利用可能な環境: Windows.

   Added in version 3.2.

signal.NSIG

   最大のシグナル番号に 1 を足した値です。 有効なシグナル番号を取得す
   るには "valid_signals()" を使ってください。

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" モジュールは一つの例外を定義します。

exception signal.ItimerError

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

   Added in version 3.3: このエラーは以前は "IOError" のサブタイプでし
   た。 現在、"IOError" は "OSError" のエイリアスです。

"signal" モジュールは以下の関数を定義します。

signal.alarm(time)

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

   利用可能な環境: Unix.

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

signal.getsignal(signalnum)

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

signal.strsignal(signalnum)

   シグナル *signalnum* の説明を返します (例えば "SIGINT" なら
   "Interrupt")。 *signalnum* に説明がなければ "None" を返します。
   *signalnum* が無効な場合は "ValueError" を送出します。

   Added in version 3.8.

signal.valid_signals()

   このプラットフォームで有効なシグナル番号の集合を返します。 システム
   が内部使用のために予約しているシグナルがある場合は、"range(1,
   NSIG)" より少なくなることがあります。

   Added in version 3.8.

signal.pause()

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

   利用可能な環境: Unix.

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

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

signal.raise_signal(signum)

   呼び出し元のプロセスにシグナルを送ります。 戻り値はありません。

   Added in version 3.8.

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

   ファイル記述子 *pidfd* が指すプロセスにシグナル *sig* を送ります。
   Python は現在 *siginfo* 引数に対応していないため、"None" にしなけれ
   ばなりません。 *flags* 引数は将来の拡張用で、現在定義されているフラ
   グ値はありません。

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

   利用可能な環境: 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".

   *thread_id* に指定する値は "threading.get_ident()" または
   "threading.Thread" オブジェクトの "ident" 属性で取得できます。

   *signalnum* が 0 の場合、シグナルは送られませんがエラーチェックは行
   われます。 これを使って対象のスレッドがまだ実行中かどうかを確認でき
   ます。

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

   利用可能な環境: Unix.

   詳細は *pthread_kill(3)* のマニュアルページを参照してください。

   "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" と "SIGSTOP" はブロックできません。

   利用可能な環境: Unix.

   詳細は *sigprocmask(2)* と *pthread_sigmask(3)* のマニュアルページ
   を参照してください。

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

   Added in version 3.3.

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

   *which* で指定したインターバルタイマー ("signal.ITIMER_REAL"、
   "signal.ITIMER_VIRTUAL"、"signal.ITIMER_PROF" のいずれか) を、
   *seconds* 秒後に発火するよう設定します ("alarm()" とは異なり、マイ
   クロ秒単位に切り上げます)。 その後は *interval* がゼロでなければ
   *interval* 秒ごとに発火します。 *seconds* をゼロにすると *which* で
   指定したインターバルタイマーを解除できます。

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

   以前の値を二つの浮動小数点数のタプル ("delay"、"interval") として返
   します。

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

   利用可能な環境: Unix.

   バージョン 3.15 で変更: *seconds* と *interval* に、整数や浮動小数
   点数だけでなく任意の実数を渡せるようになりました。

signal.getitimer(which)

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

   利用可能な環境: Unix.

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

   待機を解除するためのファイル記述子を *fd* に設定します。 プログラム
   がハンドラーを登録したシグナルを受信すると、その番号が 1 バイトとし
   てこのファイル記述子に書き込まれます。 対象のシグナルにハンドラーを
   登録していなければ何も書き込まれません。 ライブラリはこの仕組みで
   poll や select の待機を解除し、シグナルを最後まで処理できます。

   以前の待機解除用ファイル記述子を返します (有効になっていなければ
   -1)。 *fd* が -1 の場合はファイル記述子による待機解除を無効にします
   。 -1 でない場合、*fd* は非ブロッキングでなければなりません。 poll
   や select を再び呼ぶ前に *fd* のバイトをすべて読み出すのはライブラ
   リの責任です。

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

   この関数には一般的な使い方が二つあります。 どちらもシグナルの到着時
   にファイル記述子で待機を解除しますが、*どの* シグナルが到着したかを
   判別する方法が異なります。

   一つ目の方法では、ファイル記述子のバッファからデータを読み出し、そ
   のバイト値からシグナル番号を取得します。 これは単純ですが、まれに問
   題が起こります。 通常、ファイル記述子のバッファ容量には上限があるた
   め、シグナルが短時間に大量に到着するとバッファがいっぱいになり、一
   部のシグナルを取りこぼす可能性があります。 この方法を使う場合は
   "warn_on_full_buffer=True" を設定してください。 そうすれば、少なく
   ともシグナルを取りこぼしたときに標準エラー出力に警告が表示されます
   。

   二つ目の方法では、ファイル記述子を待機解除のため *だけ* に使い、実
   際のバイト値は無視します。 この場合はバッファが空かどうかだけを見て
   おり、バッファがいっぱいになっても問題はありません。 不要な警告メッ
   セージで利用者を混乱させないよう、この方法では
   "warn_on_full_buffer=False" を設定してください。

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

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

signal.siginterrupt(signalnum, flag)

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

   利用可能な環境: Unix.

   詳細は *siginterrupt(3)* のマニュアルページを参照してください。

   "signal()" でシグナルハンドラーを登録すると、対象のシグナルに対して
   *flag* を真にして "siginterrupt()" が暗黙に呼び出され、システムコー
   ルを中断する動作にリセットされることに注意してください。

signal.signal(signalnum, handler)

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

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

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

   Windows では "signal()" に指定できるのは "SIGABRT"、"SIGFPE"、
   "SIGILL"、"SIGINT"、"SIGSEGV"、"SIGTERM"、"SIGBREAK" だけです。 他
   の値では "ValueError" が送出されます。 すべてのシステムが同じシグナ
   ル名を定義しているわけではなく、"SIG*" というモジュールレベルの定数
   が定義されていない名前を参照すると "AttributeError" が送出されます
   。

signal.sigpending()

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

   利用可能な環境: Unix.

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

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

   Added in version 3.3.

signal.sigwait(sigset)

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

   利用可能な環境: Unix.

   詳細は *sigwait(3)* のマニュアルページを参照してください。

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

   戻り値は "siginfo_t" 構造体に含まれるデータ、すなわち "si_signo"、
   "si_code"、"si_errno"、"si_pid"、"si_uid"、"si_status"、"si_band"
   を表すオブジェクトです。

   利用可能な環境: Unix.

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

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

   Added in version 3.3.

   バージョン 3.5 で変更: *sigset* に含まれないシグナルで中断され、そ
   のハンドラーが例外を送出しなかった場合、関数を再試行するようになり
   ました (理由は **PEP 475** を参照)。

signal.sigtimedwait(sigset, timeout)

   "sigwaitinfo()" と同様ですが、タイムアウトを指定する *timeout* 引数
   を追加で受け取ります。 *timeout* に "0" を指定するとポーリングを行
   います。 タイムアウトした場合は "None" を返します。

   利用可能な環境: Unix.

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

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

   Added in version 3.3.

   バージョン 3.5 で変更: *sigset* に含まれないシグナルで中断され、そ
   のハンドラーが例外を送出しなかった場合、*timeout* を再計算して関数
   を再試行するようになりました (理由は **PEP 475** を参照)。

   バージョン 3.15 で変更: *timeout* に、整数や浮動小数点数だけでなく
   任意の実数を渡せるようになりました。


使用例
======

以下は最小限のプログラム例です。 この例では "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!")

   # シグナルハンドラーと 5 秒後のアラームを設定する
   signal.signal(signal.SIGALRM, handler)
   signal.alarm(5)

   # この open() は無期限に待機する可能性がある
   fd = os.open('/dev/ttyS0', os.O_RDWR)

   signal.alarm(0)          # アラームを解除する


SIGPIPE についての注意
======================

プログラムの出力を *head(1)* などのツールにパイプで渡していて、標準出
力の受け取り側が先に閉じると、プロセスに "SIGPIPE" シグナルが送られま
す。 この結果、"BrokenPipeError: [Errno 32] Broken pipe" のような例外
が発生します。 この状況に対処するには、次のようにエントリーポイントの
処理を囲んで例外を捕捉します。

   import os
   import sys

   def main():
       try:
           # 大量の出力を模擬する (このループを実際のコードに置き換える)
           for x in range(10000):
               print("y")
           # この try ブロック内で SIGPIPE が発生するよう、
           # ここで出力をフラッシュする。
           sys.stdout.flush()
       except BrokenPipeError:
           # Python は終了時に標準ストリームをフラッシュするので、残りの出力を
           # devnull にリダイレクトし、終了時の BrokenPipeError 再発を防ぐ
           devnull = os.open(os.devnull, os.O_WRONLY)
           os.dup2(devnull, sys.stdout.fileno())
           sys.exit(1)  # EPIPE では Python はエラーコード 1 で終了する

   if __name__ == '__main__':
       main()

"BrokenPipeError" を避けるために "SIGPIPE" の処理方法を "SIG_DFL" に設
定しないでください。 そうすると、ソケットへの書き込み中に接続が切れる
たびに、プログラムが予期せず終了してしまいます。


シグナルハンドラーと例外についての注意
======================================

シグナルハンドラーが例外を送出すると、その例外はメインスレッドに伝播し
、どの *bytecode* 命令の実行後にも発生する可能性があります。 特に
"KeyboardInterrupt" は実行中のどの時点でも発生し得ます。 標準ライブラ
リを含むほとんどの Python コードはこれに対して堅牢にできないため、
"KeyboardInterrupt" (またはシグナルハンドラーによる他の例外) によって
、まれにプログラムが予期しない状態になることがあります。

この問題を示す例として、次のコードを考えてください。

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

       def __enter__(self):
           # ここで KeyboardInterrupt が発生しても問題ない
           self.lock.acquire()
           # ここで KeyboardInterrupt が発生すると __exit__ は呼ばれない
           ...
           # 関数から戻る直前に KeyboardInterrupt が発生する可能性もある

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

多くのプログラム、特に "KeyboardInterrupt" で単に終了するだけのプログ
ラムでは問題になりませんが、複雑なアプリケーションや高い信頼性が必要な
アプリケーションではシグナルハンドラーからの例外送出を避けるべきです。
また、正常に終了処理を行う手段として "KeyboardInterrupt" を捕捉するこ
とも避けてください。 代わりに独自の "SIGINT" ハンドラーを登録してくだ
さい。 以下は "KeyboardInterrupt" を発生させない HTTP サーバーの例です
。

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