...

パッケージ signal

概要 ▾

signal パッケージは,入力シグナルへのアクセスを実装します。

シグナルは主に Unix ライクなシステムで使われています。 Windows および Plan 9 でこのパッケージを使用する場合は,以下を参照してください。

シグナルの種類

シグナル SIGKILL と SIGSTOP はプログラムによって捕捉されない可能性があるため,このパッケージの影響を受けることはありません。

同期シグナルは,プログラム実行中のエラー (SIGBUS , SIGFPE ,および SIGSEGV) によってトリガーされるシグナルです。 これらはプログラムの実行によって引き起こされた場合にのみ同期と見なされ, os.Process.Kill や kill プログラム,あるいは同様のメカニズムを使って送られた場合には同期されません。 一般に,以下で説明する場合を除き, Go プログラムは同期シグナルをランタイムパニックに変換します。

残りのシグナルは非同期シグナルです。 それらはプログラムエラーによって引き起こされるのではなく,代わりにカーネルまたは他のプログラムから送られます。

非同期シグナルのうち,プログラムが制御端末を失うと SIGHUP シグナルが送信されます。 SIGINT シグナルは,制御端末のユーザーが割り込み文字 (デフォルトでは ^C(Control-C)) を押すと送信されます。 SIGQUIT シグナルは,制御端末のユーザーが終了文字を押すと送信されます。 デフォルトでは ^\(Control-Backslash) です。 一般的には, ^C を押すことでプログラムを終了させることができ, ^\ を押すとスタックダンプで終了させることができます。

Go プログラムにおけるシグナルのデフォルトの振る舞い

デフォルトでは,同期シグナルはランタイムパニックに変換されます。 SIGHUP, SIGINT ,または SIGTERM シグナルはプログラムを終了させます。 SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGSTKFLT, SIGEMT ,または SIGSYS シグナルは,プログラムをスタックダンプで終了させます。 SIGTSTP, SIGTTIN ,または SIGTTOU シグナルは,システムのデフォルトの動作になります (これらのシグナルは,ジョブ制御のためにシェルによって使用されます) 。 SIGPROF シグナルは, runtime.CPUProfile を実装するために Go ランタイムによって直接処理されます。 他のシグナルは捕捉されますが,アクションはとられません。

SIGHUP または SIGINT のいずれかを無視して (シグナルハンドラを SIG_IGN に設定して)Go プログラムを起動した場合,それらは無視されたままになります。

Go プログラムが空でないシグナルマスクで起動された場合,それは一般的に尊重されます。 しかし,シグナルの中には明示的にブロック解除されているものがあります : 同期シグナル, SIGILL, SIGTRAP, SIGSTKFLT, SIGCHLD, SIGPROF ,および GNU/Linux ではシグナル 32 (SIGCANCEL) および 33(SIGSETXID)(SIGCANCEL および SIGSETXID は glibc によって内部的に使用されます) 。 os.Exec または os/exec パッケージによって開始されたサブプロセスは,変更されたシグナルマスクを継承します。

Go プログラムでのシグナルの振る舞いを変える

このパッケージの関数により,プログラムは Go プログラムがシグナルを処理する方法を変更することができます。

Notify は,非同期シグナルの特定のセットに対するデフォルトの動作を無効にし,代わりにそれらを 1 つ以上の登録済みチャンネルを介して配信します。 具体的には, SIGHUP , SIGINT , SIGQUIT , SIGABRT ,および SIGTERM の各シグナルに適用されます。 ジョブ制御シグナル SIGTSTP, SIGTTIN ,および SIGTTOU にも適用されます。 この場合,システムのデフォルトの動作は発生しません。 それ以外ではアクションを引き起こさないシグナルにも適用されます。 SIGTHAW, SIGLOST, SIGXRES, SIGJVM1, SIGJVM2 ,およびシステムで使用されるリアルタイムシグナル。 これらのシグナルすべてがすべてのシステムで利用できるわけではないことに注意してください。

プログラムが SIGHUP または SIGINT を無視して開始され,いずれかのシグナルに対して Notify が呼び出された場合,そのシグナルに対してシグナルハンドラがインストールされ,無視されることはなくなります。 後でそのシグナルに対して Reset または Ignore が呼び出されるか,そのシグナルに対して Notify に渡されたすべてのチャンネルで Stop が呼び出されると,そのシグナルはもう一度無視されます。 Reset でシグナルのシステムデフォルトの動作に戻りますが,無視するとシステムはシグナルを完全に無視します。

プログラムが空でないシグナルマスクで開始された場合,いくつかのシグナルは上記のように明示的にブロック解除されます。 Notify がブロックされたシグナルに対して呼び出された場合,そのシグナルはブロック解除されます。 後でそのシグナルに対して Reset が呼び出された場合,またはそのシグナルについて Notify に渡されたすべてのチャンネルで Stop が呼び出された場合,シグナルは再びブロックされます。

SIGPIPE

Go プログラムが壊れたパイプに書き込むと,カーネルは SIGPIPE シグナルを送出します。

プログラムが SIGPIPE シグナルを受信するために Notify を呼び出さなかった場合,動作はファイル記述子番号によって異なります。 ファイル記述子 1 または 2 (標準出力または標準エラー) の壊れたパイプへの書き込みは,プログラムを SIGPIPE シグナルで終了させます。 他のファイル記述子の壊れたパイプへの書き込みは SIGPIPE シグナルに対して何のアクションも取らず,書き込みは EPIPE エラーで失敗します。

プログラムが SIGPIPE シグナルを受信するために Notify を呼び出した場合は,ファイル記述子番号は関係ありません。 SIGPIPE シグナルは Notify チャンネルに配信され,書き込みは EPIPE エラーで失敗します。

つまり,デフォルトでは,コマンドラインプログラムは一般的な Unix のコマンドラインプログラムのように動作しますが,他のプログラムは閉じたネットワーク接続に書き込んでも SIGPIPE でクラッシュしません。

cgo や SWIG を使ったプログラムに行く

Go 以外のコード (通常は cgo または SWIG を使用してアクセスされる C/C++ コード) を含む Go プログラムでは,通常, Go のスタートアップコードが最初に実行されます。 Go 以外のスタートアップコードが実行される前に, Go ランタイムが期待するとおりにシグナルハンドラを設定します。 Go 以外のスタートアップコードが独自のシグナルハンドラをインストールしたい場合は, Go をうまく機能させるために一定の手順を踏む必要があります。 このセクションでは,それらの手順と, Go 以外のコードによるシグナルハンドラの設定に対する全体的な効果の変更が Go プログラムに与える可能性があることについて説明します。 まれに,非 Go コードが Go コードの前に実行されることがあります。 その場合は,次のセクションも適用されます。

Go プログラムによって呼び出された Go 以外のコードがシグナルハンドラやマスクを変更しない場合,その動作は純粋な Go プログラムの場合と同じです。

Go 以外のコードがシグナルハンドラをインストールする場合は, sigaction とともに SA_ONSTACK フラグを使用する必要があります。 そうしないと,シグナルを受信した場合にプログラムがクラッシュする可能性があります。 Go プログラムは通常,限られたスタックで実行されるため,代替シグナルスタックを設定します。 また, Go 標準ライブラリは,すべてのシグナルハンドラが SA_RESTART フラグを使用することを期待しています。 これを怠ると,一部のライブラリ呼び出しが "割り込みシステムコール" エラーを返す可能性があります。

非 Go コードが同期シグナル (SIGBUS, SIGFPE, SIGSEGV) のいずれかに対してシグナルハンドラーをインストールする場合は,既存の Go シグナルハンドラーを記録する必要があります。 Go コードの実行中にこれらのシグナルが発生した場合は, Go シグナルハンドラを呼び出す必要があります (Go コードの実行中にシグナルが発生したかどうかは,シグナルハンドラに渡された PC を見ればわかります) 。 そうでなければ, Go の実行時パニックが予想通りに発生しません。

非 Go コードが非同期シグナルのいずれかに対してシグナルハンドラーをインストールする場合, Go シグナルハンドラーを起動するか,または選択したとおりに起動しない可能性があります。 当然, Go シグナルハンドラが呼び出されない場合は,上記の Go 動作は発生しません。 これは特に SIGPROF シグナルの問題です。

Go 以外のコードは, Go ランタイムによって作成されたスレッドのシグナルマスクを変更してはいけません。 Go 以外のコードが独自の新しいスレッドを開始した場合は,シグナルマスクが適切に設定される可能性があります。

非 Go コードが新しいスレッドを開始し,シグナルマスクを変更し,そのスレッドで Go 関数を呼び出すと, Go ランタイムは自動的に特定のシグナルのブロックを解除します。 同期シグナル, SIGILL, SIGTRAP, SIGSTKFLT, SIGCHLD, SIGPROF, SIGCANCEL ,および SIGSETXID 。 Go 関数が戻ると, Go 以外のシグナルマスクが復元されます。

Go シグナルハンドラが Go コードを実行していない Go 以外のスレッドで呼び出された場合,ハンドラは通常,次のようにシグナルを Go 以外のコードに転送します。 シグナルが SIGPROF の場合, Go ハンドラは何もしません。 それ以外の場合は, Go ハンドラは自分自身を削除し,シグナルのブロックを解除して再度呼び出し, Go 以外のハンドラまたはデフォルトのシステムハンドラを呼び出します。 プログラムが終了しない場合は, Go ハンドラはそれ自体を再インストールしてプログラムの実行を続行します。

Go コードを呼び出す Non-Go プログラム

Go コードが -buildmode=c-shared のようなオプションで構築されている場合,それは既存の Go 以外のプログラムの一部として実行されます。 Go 以外のコードは, Go コードの開始時にすでにシグナルハンドラをインストールしている可能性があります。 (これは, cgo または SWIG を使用している場合の異常な場合にも発生する可能性があります。) -buildmode=c- アーカイブの場合, Go ランタイムはグローバルコンストラクタ時にシグナルを初期化します。 -buildmode=c-shared の場合,共有ライブラリがロードされると Go ランタイムはシグナルを初期化します。

Go ランタイムが SIGCANCEL または SIGSETXID シグナル用の既存のシグナルハンドラ (GNU/Linux でのみ使用される) を見た場合, SA_ONSTACK フラグをオンにし,それ以外の場合はシグナルハンドラを保持します。

同期シグナルと SIGPIPE の場合, Go ランタイムはシグナルハンドラをインストールします。 既存のシグナルハンドラを保存します。 Go 以外のコードの実行中に同期シグナルが到着した場合, Go ランタイムは Go シグナルハンドラの代わりに既存のシグナルハンドラを呼び出します。

-buildmode=c-archive または -buildmode=c-shared でビルドされた go コードは,デフォルトでは他のシグナルハンドラをインストールしません。 既存のシグナルハンドラがある場合, Go ランタイムは SA_ONSTACK フラグをオンにし,それ以外の場合はシグナルハンドラを保持します。 非同期シグナルに対して Notify が呼び出された場合,そのシグナルに対して Go シグナルハンドラがインストールされます。 後でそのシグナルに対して Reset が呼び出されると,そのシグナルの元の処理が再インストールされ,存在する場合は Go 以外のシグナルハンドラが復元されます。

-buildmode=c-archive または -buildmode=c-shared なしでビルドされた go コードは,上記の非同期シグナル用のシグナルハンドラをインストールし,既存のシグナルハンドラを保存します。 シグナルが Go 以外のスレッドに配信された場合,既存の Go 以外のシグナルハンドラがある場合は,そのシグナルが発生する前にそのハンドラがインストールされます。

Windows

Windows では,通常 ^C (Control-C) または ^BREAK (Control-Break) を押すとプログラムが終了します。 Notify が os.Interrupt に対して呼び出された場合, ^C または ^BREAK は os.Interrupt をそのチャンネルで送信させ,プログラムは終了しません。 Reset が呼び出された場合,または Notify に渡されたすべてのチャンネルで Stop が呼び出された場合は,デフォルトの動作に戻ります。

Plan 9

Plan 9 では,シグナルの型は syscall.Note です。 これは文字列です。 syscall.Note で Notify を呼び出すと,その文字列がメモとして投稿されたときにその値がチャンネルに送信されます。

func Ignore 1.5

func Ignore(sig ...os.Signal)

Ignore は,渡されたシグナルを無視します。 それらがプログラムによって受信されても​​,何も起こりません。 無視は,渡されたシグナルを通知するための以前の呼び出しの効果を元に戻します。 シグナルが渡されていない場合,すべての着信シグナルは無視されます。

func Ignored 1.11

func Ignored(sig os.Signal) bool

Ignored は,sig が現在無視されているかどうかを報告します。

func Notify

func Notify(c chan<- os.Signal, sig ...os.Signal)

Notify は,signal パッケージが c に着信シグナルを中継するようにします。 シグナルが渡されていない場合は,すべての着信シグナルが c に中継されます。 それ以外の場合は,渡されたシグナルだけが有効です。

signal パッケージは c への送信をブロックしません : 呼び出し側は c が期待されるシグナルレートに追いつくために十分なバッファスペースを持っていることを確認しなければなりません。 たった 1 つのシグナル値の通知に使用されるチャンネルの場合,サイズ 1 のバッファで十分です。

同じチャンネルで Notify を複数回呼び出すことができます。 各呼び出しは,そのチャンネルに送信されたシグナルのセットを拡張します。 セットからシグナルを削除する唯一の方法は, Stop を呼び出すことです。

異なるチャンネルと同じシグナルで Notify を複数回呼び出すことができます。 各チャンネルは独立して着信シグナルのコピーを受信します。

コード:

// シグナル通知を送信するチャンネルを設定します。
// シグナルが送信されたときに受信する準備が整っていない場合は,バッファチャンネルを使用するか,シグナルを見逃す危険性があります。
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)

// シグナルが受信されるまでブロックします。
s := <-c
fmt.Println("Got signal:", s)

例 (AllSignals)

コード:

// シグナル通知を送信するチャンネルを設定します。
// シグナルが送信されたときに受信する準備が整っていない場合は,バッファチャンネルを使用するか,シグナルを見逃す危険性があります。
c := make(chan os.Signal, 1)

// Notify にシグナルを渡さないと,すべてのシグナルがそのチャンネルに送信されます。
signal.Notify(c)

// シグナルが受信されるまでブロックします。
s := <-c
fmt.Println("Got signal:", s)

func Reset 1.5

func Reset(sig ...os.Signal)

Reset は,指定されたシグナルについて,以前に呼び出した Notify の呼び出しの効果が元に戻ります。 シグナルが渡されていない場合は,すべてのシグナルハンドラがリセットされます。

func Stop 1.1

func Stop(c chan<- os.Signal)

Stop で, signal パッケージは c への着信シグナルの中継を停止します。 c を使用した Notify への以前のすべての呼び出しの効果を元に戻します。 Stop が戻ったとき, c がそれ以上シグナルを受け取らないことが保証されています。

サブディレクトリ

名前 概要
..