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

このモジュールでは Python でシグナルハンドラを使うための機構を提供しま
す。シグナルとハンドラを扱う上で一般的なルールがいくつかあります:

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

* クリティカルセクションから一時的にシグナルを "ブロック" することは
  で きません (この機能をサポートしない Unix 系システムも存在するため
  です )。

* Python のシグナルハンドラは Python のユーザが望む限り非同期で呼び
  出 されますが、呼び出されるのは Python インタプリタの "原子的な
  (atomic)" 命令実行単位の間です。したがって、 (巨大なサイズのテキスト
  に対する正規表現の一致検索のような) 純粋に C 言語のレベルで実現され
  ている時間のかかる処理中に到着したシグナルは、不定期間遅延する可能性
  があります。

* シグナルが I/O 操作中に到着すると、シグナルハンドラが処理を返した
  後 に I/O 操作が例外を送出する可能性があります。これは背後にある
  Unix システムが割り込みシステムコールにどういう意味付けをしているか
  に依存 します。

* C 言語のシグナルハンドラは常に処理を返すので、 "SIGFPE" や
  "SIGSEGV" のような同期エラーの捕捉はほとんど意味がありません。

* Python は標準でごく少数のシグナルハンドラをインストールしています:
  "SIGPIPE" は無視されます (したがって、パイプやソケットに対する書き込
  みで生じたエラーは通常の Python 例外として報告されます) "SIGINT" は
  "KeyboardInterrupt" 例外に変換されます。これらはどれも上書きすること
  ができます。

* シグナルとスレッドの両方を同じプログラムで使用する場合にはいくつか
  注 意が必要です。シグナルとスレッドを同時に利用する上で基本的に注意
  すべ きことは、 "signal()" 命令は常に主スレッド (main thread) の処理
  中で 実行するということです。どのスレッドも "alarm()" 、
  "getsignal()" 、 "pause()" 、 "setitimer()" 、 "getitimer()" を実行
  することができます 。しかし、主スレッドだけが新たなシグナルハンドラ
  を設定することができ 、したがってシグナルを受け取ることができるのは
  主スレッドだけです (こ れは、背後のスレッド実装が個々のスレッドに対
  するシグナル送信をサポー トしているかに関わらず、 Python "signal" モ
  ジュールが強制している仕 様です)。したがって、シグナルをスレッド間通
  信の手段として使うことは できません。代わりにロック機構を使ってくだ
  さい。

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

signal.SIG_DFL

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

signal.SIG_IGN

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

SIG*

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

signal.CTRL_C_EVENT

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

   利用出来る環境: Windows.

   バージョン 2.7 で追加.

signal.CTRL_BREAK_EVENT

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

   利用出来る環境: Windows.

   バージョン 2.7 で追加.

signal.NSIG

   最も大きいシグナル番号に 1 を足した値です。

signal.ITIMER_REAL

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

signal.ITIMER_VIRTUAL

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

signal.ITIMER_PROF

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

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

exception signal.ItimerError

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

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

signal.alarm(time)

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

signal.getsignal(signalnum)

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

signal.pause()

   シグナルを受け取るまでプロセスを一時停止します; その後、適切なハン
   ドラが呼び出されます。戻り値はありません。Windows では利用できませ
   ん。(Unix マニュアルページ *signal(2)* を参照してください。)

signal.setitimer(which, seconds[, interval])

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

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

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

   無効なインターバルタイマーを渡すと "ItimerError" 例外が発生します。
   利用できる環境: Unix。

   バージョン 2.6 で追加.

signal.getitimer(which)

   *which* で指定されたインターバルタイマーの現在の値を返します。利用
   できる環境: Unix。

   バージョン 2.6 で追加.

signal.set_wakeup_fd(fd)

   wakeup fd を *fd* に設定します。シグナルを受信したときに、 "'\0'"
   バイトがそのfd に書き込まれます。これは、poll や select をしている
   ライブラリを起こして、シグナルの処理をさせるのに利用できます。

   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"
   例外が発生します。

   バージョン 2.6 で追加.

signal.siginterrupt(signalnum, flag)

   システムコールのリスタートの動作を変更します。 *flag* が "False" の
   場合、 *signalnum* シグナルに中断されたシステムコールは再実行されま
   す。それ以外の場合、システムコールは中断されます。戻り値はありませ
   ん。利用できる環境: Unix (詳しい情報についてはマニュアルページ
   *siginterrupt(3)* を参照してください)。

   "signal()" を使ってシグナルハンドラを設定したときに、暗黙のうちに
   *flag* に true を指定して "siginterrupt()" が実行されるため、中断に
   対するリスタートの動作がリセットされることに注意してください。

   バージョン 2.6 で追加.

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" でのみ利用できます。それ以外の場合は
   "ValueError" を発生させます。


17.4.1. 例
==========

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

   import signal, os

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