[インデックス 14133] ファイルの概要
このコミットは、Go言語の標準ライブラリ os/signal
パッケージに Notify
関数の使用例を追加するものです。具体的には、example_test.go
という新しいテストファイルが追加され、os.Interrupt
と os.Kill
シグナルを捕捉し、それらが受信された際に処理を行うシンプルな例が示されています。
コミット
commit bd6601f4a05df0aedff66b4846d507615f584c5b
Author: Andrew Gerrand <adg@golang.org>
Date: Fri Oct 12 10:22:13 2012 +1100
os/signal: add Notify example
R=golang-dev, dsymonds, r
CC=golang-dev
https://golang.org/cl/6615078
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/bd6601f4a05df0aedff66b4846d507615f584c5b
元コミット内容
os/signal: add Notify example
R=golang-dev, dsymonds, r
CC=golang-dev
https://golang.org/cl/6615078
変更の背景
Go言語の標準ライブラリは、その機能の理解と適切な使用を促進するために、豊富なドキュメントとコード例を提供しています。os/signal
パッケージは、オペレーティングシステムからのシグナル(例えば、Ctrl+Cによる割り込みシグナル SIGINT
や、プロセス終了シグナル SIGTERM
/SIGKILL
など)をGoプログラムで捕捉し、適切に処理するための機能を提供します。
signal.Notify
関数は、特定のシグナルを受信した際にチャネルに通知を送信するメカニズムを提供しますが、その正確な使用方法、特にバッファ付きチャネルの必要性については、初心者にとって直感的ではない場合があります。このコミットは、signal.Notify
の典型的な使用パターンを示す具体的なコード例を追加することで、開発者がこの機能をより簡単に理解し、自身のアプリケーションに組み込めるようにすることを目的としています。これにより、Goプログラムがシグナルに対して堅牢かつ適切に応答できるようになります。
前提知識の解説
オペレーティングシステムシグナル
オペレーティングシステムシグナルは、プロセスに対して非同期的にイベントを通知するためのメカニズムです。これらは、エラー状態(例: 無効なメモリアクセス)、外部イベント(例: ユーザーがCtrl+Cを押す)、または他のプロセスからの通信(例: kill
コマンド)など、様々な状況で発生します。
主要なシグナルには以下のようなものがあります。
- SIGINT (Interrupt Signal): 通常、ユーザーがターミナルで
Ctrl+C
を押したときにプロセスに送信されます。プログラムに正常終了を促すために使用されます。 - SIGTERM (Termination Signal): プロセスに終了を要求するために送信される汎用的なシグナルです。プロセスはこれを受信すると、クリーンアップ処理(開いているファイルの保存、ネットワーク接続の切断など)を行ってから終了することが期待されます。
- SIGKILL (Kill Signal): プロセスを強制的に終了させるシグナルです。このシグナルは捕捉、ブロック、無視することができず、プロセスは即座に終了します。クリーンアップ処理を行う機会はありません。
Goプログラムがこれらのシグナルを適切に処理することは、サーバーアプリケーションの graceful shutdown(優雅なシャットダウン)や、コマンドラインツールのユーザーフレンドリーな終了動作を実現するために不可欠です。
Go言語の os/signal
パッケージ
os/signal
パッケージは、GoプログラムがOSシグナルと対話するための機能を提供します。
signal.Notify(c chan<- os.Signal, sig ...os.Signal)
: この関数は、指定されたシグナルsig
のいずれかが受信されたときに、それらのシグナルをチャネルc
に転送するようにGoランタイムに指示します。c
: シグナルが送信されるチャネルです。このチャネルはバッファ付きである必要があります。なぜなら、シグナルが非同期に発生するのに対し、チャネルからの受信は同期的に行われるため、チャネルがバッファなしだと、シグナルが送信されたときに受信側が準備できていない場合、シグナルが失われる可能性があるからです。バッファ付きチャネルを使用することで、シグナルがチャネルに一時的に格納され、受信側が準備できたときに安全に取得できます。sig ...os.Signal
: 捕捉したいシグナルの可変引数リストです。何も指定しない場合、signal.Notify
はすべての受信シグナルをチャネルに転送します。
signal.Stop(c chan<- os.Signal)
:signal.Notify
で設定されたシグナル転送を停止します。これにより、指定されたチャネルc
へのシグナル送信が停止し、チャネルはクローズされます。os.Interrupt
:syscall.SIGINT
のエイリアスで、通常はCtrl+C
によって生成される割り込みシグナルを表します。os.Kill
:syscall.SIGKILL
のエイリアスで、プロセスを強制終了させるシグナルを表します。
技術的詳細
このコミットで追加された ExampleNotify
関数は、os/signal
パッケージの Notify
関数をどのように使用するかを具体的に示しています。
-
チャネルの作成:
c := make(chan os.Signal, 1)
ここで、
os.Signal
型のチャネルc
が作成されます。重要なのは、このチャネルがバッファサイズ1で作成されている点です。これは、signal.Notify
のドキュメントにも明記されているように、シグナルが非同期に発生するため、受信側が常に準備できているとは限らないからです。バッファがない場合、シグナルが送信された瞬間にチャネルがブロックされていると、そのシグナルは失われてしまいます。バッファを設けることで、シグナルがチャネルに一時的に格納され、後で安全に読み取ることができます。バッファサイズが1であれば、少なくとも1つのシグナルを確実に捕捉できます。 -
シグナルの登録:
signal.Notify(c, os.Interrupt, os.Kill)
signal.Notify
関数が呼び出され、c
チャネルにos.Interrupt
(SIGINT) とos.Kill
(SIGKILL) シグナルを転送するようにGoランタイムに指示しています。これにより、これらのシグナルがプロセスに送信されると、c
チャネルに値が送信されます。 -
シグナルの待機と処理:
s := <-c fmt.Println("Got signal:", s)
s := <-c
の行は、c
チャネルから値が受信されるまでプログラムの実行をブロックします。つまり、os.Interrupt
またはos.Kill
シグナルが受信されるまで、この行で待機します。シグナルが受信されると、そのシグナルを表すos.Signal
型の値がs
に代入され、その後fmt.Println
で出力されます。
この例は、Goプログラムが外部からのシグナルに対してどのように反応し、クリーンアップ処理などを行うための基本的なフレームワークを提供します。実際のアプリケーションでは、シグナル受信後にデータベース接続のクローズ、ログのフラッシュ、ゴルーチンの終了待機などの処理を行うことになります。
コアとなるコードの変更箇所
このコミットでは、src/pkg/os/signal/example_test.go
という新しいファイルが追加されています。
--- /dev/null
+++ b/src/pkg/os/signal/example_test.go
@@ -0,0 +1,19 @@
+package signal_test
+
+import (
+ "fmt"
+ "os"
+ "os/signal"
+)
+
+func ExampleNotify() {
+ // Set up channel on which to send signal notifications.
+ // We must use a buffered channel or risk missing the signal
+ // if we're not ready to receive when the signal is sent.
+ c := make(chan os.Signal, 1)
+ signal.Notify(c, os.Interrupt, os.Kill)
+
+ // Block until a signal is received.
+ s := <-c
+ fmt.Println("Got signal:", s)
+}
コアとなるコードの解説
追加された example_test.go
ファイルは、Goのテストフレームワークにおける「Example」関数として機能します。Example
関数は、パッケージのドキュメントに直接組み込まれるコード例として扱われ、go test
コマンド実行時にテストとして実行され、その出力が期待される出力と一致するかどうかが検証されます。これにより、コード例が常に最新かつ正確であることが保証されます。
ExampleNotify
関数は、os/signal
パッケージの Notify
関数の典型的な使用方法を示しています。
package signal_test
: この行は、このファイルがsignal
パッケージの外部にあるテストパッケージであることを示します。これにより、signal
パッケージの公開されたAPIのみを使用してテスト(この場合は例)を書くことができます。import
ブロック:fmt
(出力用)、os
(OS固有の機能、os.Interrupt
やos.Kill
の定義を含む)、そしてos/signal
(シグナル処理機能) パッケージをインポートしています。func ExampleNotify()
: この関数がsignal.Notify
の使用例を提供します。- コメントで、バッファ付きチャネルを使用する必要がある理由が明確に説明されています。「We must use a buffered channel or risk missing the signal if we're not ready to receive when the signal is sent.」(シグナルが送信されたときに受信する準備ができていない場合、シグナルを見逃すリスクがあるため、バッファ付きチャネルを使用する必要があります。)
c := make(chan os.Signal, 1)
: 前述の通り、バッファサイズ1のチャネルを作成します。signal.Notify(c, os.Interrupt, os.Kill)
:SIGINT
とSIGKILL
を捕捉対象として登録します。s := <-c
: シグナルが受信されるまでブロックします。fmt.Println("Got signal:", s)
: 受信したシグナルを出力します。
この例は、Goプログラムでシグナルハンドリングを実装するための最も基本的なパターンであり、多くのGoアプリケーションで graceful shutdown のロジックの出発点となります。
関連リンク
- Go言語の
os/signal
パッケージのドキュメント: https://pkg.go.dev/os/signal - Go言語の
os
パッケージのドキュメント: https://pkg.go.dev/os - Go言語の
syscall
パッケージのドキュメント (シグナル定数の詳細): https://pkg.go.dev/syscall
参考にした情報源リンク
- Go言語の公式ドキュメント (
os/signal
パッケージ) - Go言語のテストに関するドキュメント (Example関数の動作について)
- 一般的なオペレーティングシステムシグナルに関する知識
- Go言語のチャネルに関する知識
[インデックス 14133] ファイルの概要
このコミットは、Go言語の標準ライブラリ os/signal
パッケージに Notify
関数の使用例を追加するものです。具体的には、example_test.go
という新しいテストファイルが追加され、os.Interrupt
と os.Kill
シグナルを捕捉し、それらが受信された際に処理を行うシンプルな例が示されています。
コミット
commit bd6601f4a05df0aedff66b4846d507615f584c5b
Author: Andrew Gerrand <adg@golang.org>
Date: Fri Oct 12 10:22:13 2012 +1100
os/signal: add Notify example
R=golang-dev, dsymonds, r
CC=golang-dev
https://golang.org/cl/6615078
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/bd6601f4a05df0aedff66b4846d507615f584c5b
元コミット内容
os/signal: add Notify example
R=golang-dev, dsymonds, r
CC=golang-dev
https://golang.org/cl/6615078
変更の背景
Go言語の標準ライブラリは、その機能の理解と適切な使用を促進するために、豊富なドキュメントとコード例を提供しています。os/signal
パッケージは、オペレーティングシステムからのシグナル(例えば、Ctrl+Cによる割り込みシグナル SIGINT
や、プロセス終了シグナル SIGTERM
/SIGKILL
など)をGoプログラムで捕捉し、適切に処理するための機能を提供します。
signal.Notify
関数は、特定のシグナルを受信した際にチャネルに通知を送信するメカニズムを提供しますが、その正確な使用方法、特にバッファ付きチャネルの必要性については、初心者にとって直感的ではない場合があります。このコミットは、signal.Notify
の典型的な使用パターンを示す具体的なコード例を追加することで、開発者がこの機能をより簡単に理解し、自身のアプリケーションに組み込めるようにすることを目的としています。これにより、Goプログラムがシグナルに対して堅牢かつ適切に応答できるようになります。
前提知識の解説
オペレーティングシステムシグナル
オペレーティングシステムシグナルは、プロセスに対して非同期的にイベントを通知するためのメカニズムです。これらは、エラー状態(例: 無効なメモリアクセス)、外部イベント(例: ユーザーがCtrl+Cを押す)、または他のプロセスからの通信(例: kill
コマンド)など、様々な状況で発生します。
主要なシグナルには以下のようなものがあります。
- SIGINT (Interrupt Signal): 通常、ユーザーがターミナルで
Ctrl+C
を押したときにプロセスに送信されます。プログラムに正常終了を促すために使用されます。 - SIGTERM (Termination Signal): プロセスに終了を要求するために送信される汎用的なシグナルです。プロセスはこれを受信すると、クリーンアップ処理(開いているファイルの保存、ネットワーク接続の切断など)を行ってから終了することが期待されます。
- SIGKILL (Kill Signal): プロセスを強制的に終了させるシグナルです。このシグナルは捕捉、ブロック、無視することができず、プロセスは即座に終了します。クリーンアップ処理を行う機会はありません。
Goプログラムがこれらのシグナルを適切に処理することは、サーバーアプリケーションの graceful shutdown(優雅なシャットダウン)や、コマンドラインツールのユーザーフレンドリーな終了動作を実現するために不可欠です。
Go言語の os/signal
パッケージ
os/signal
パッケージは、GoプログラムがOSシグナルと対話するための機能を提供します。
signal.Notify(c chan<- os.Signal, sig ...os.Signal)
: この関数は、指定されたシグナルsig
のいずれかが受信されたときに、それらのシグナルをチャネルc
に転送するようにGoランタイムに指示します。c
: シグナルが送信されるチャネルです。このチャネルはバッファ付きである必要があります。なぜなら、シグナルが非同期に発生するのに対し、チャネルからの受信は同期的に行われるため、チャネルがバッファなしだと、シグナルが送信されたときに受信側が準備できていない場合、シグナルが失われる可能性があるからです。os/signal
パッケージは、Notify
に提供されたチャネルにシグナルを送信する際にブロックしません。もしチャネルがバッファなしで、受信側が準備できていない場合、シグナルは破棄されてしまいます。バッファ付きチャネルを使用することで、シグナルがチャネルに一時的に格納され、受信側が準備できたときに安全に取得できます。sig ...os.Signal
: 捕捉したいシグナルの可変引数リストです。何も指定しない場合、signal.Notify
はすべての受信シグナルをチャネルに転送します。
signal.Stop(c chan<- os.Signal)
:signal.Notify
で設定されたシグナル転送を停止します。これにより、指定されたチャネルc
へのシグナル送信が停止し、チャネルはクローズされます。os.Interrupt
:syscall.SIGINT
のエイリアスで、通常はCtrl+C
によって生成される割り込みシグナルを表します。os.Kill
:syscall.SIGKILL
のエイリアスで、プロセスを強制終了させるシグナルを表します。
技術的詳細
このコミットで追加された ExampleNotify
関数は、os/signal
パッケージの Notify
関数をどのように使用するかを具体的に示しています。
-
チャネルの作成:
c := make(chan os.Signal, 1)
ここで、
os.Signal
型のチャネルc
が作成されます。重要なのは、このチャネルがバッファサイズ1で作成されている点です。これは、signal.Notify
のドキュメントにも明記されているように、シグナルが非同期に発生するため、受信側が常に準備できているとは限らないからです。os/signal
パッケージは、シグナルをチャネルに送信する際にブロックしないため、バッファがないチャネルを使用すると、受信側が準備できていない場合にシグナルが失われる可能性があります。バッファを設けることで、シグナルがチャネルに一時的に格納され、後で安全に読み取ることができます。バッファサイズが1であれば、少なくとも1つのシグナルを確実に捕捉できます。これは、最初のシグナル(例えば、グレースフルシャットダウンを開始するための最初のSIGINT
)のみを気にする場合に十分です。 -
シグナルの登録:
signal.Notify(c, os.Interrupt, os.Kill)
signal.Notify
関数が呼び出され、c
チャネルにos.Interrupt
(SIGINT) とos.Kill
(SIGKILL) シグナルを転送するようにGoランタイムに指示しています。これにより、これらのシグナルがプロセスに送信されると、c
チャネルに値が送信されます。 -
シグナルの待機と処理:
s := <-c fmt.Println("Got signal:", s)
s := <-c
の行は、c
チャネルから値が受信されるまでプログラムの実行をブロックします。つまり、os.Interrupt
またはos.Kill
シグナルが受信されるまで、この行で待機します。シグナルが受信されると、そのシグナルを表すos.Signal
型の値がs
に代入され、その後fmt.Println
で出力されます。
この例は、Goプログラムが外部からのシグナルに対してどのように反応し、クリーンアップ処理などを行うための基本的なフレームワークを提供します。実際のアプリケーションでは、シグナル受信後にデータベース接続のクローズ、ログのフラッシュ、ゴルーチンの終了待機などの処理を行うことになります。
コアとなるコードの変更箇所
このコミットでは、src/pkg/os/signal/example_test.go
という新しいファイルが追加されています。
--- /dev/null
+++ b/src/pkg/os/signal/example_test.go
@@ -0,0 +1,19 @@
+package signal_test
+
+import (
+ "fmt"
+ "os"
+ "os/signal"
+)
+
+func ExampleNotify() {
+ // Set up channel on which to send signal notifications.
+ // We must use a buffered channel or risk missing the signal
+ // if we're not ready to receive when the signal is sent.
+ c := make(chan os.Signal, 1)
+ signal.Notify(c, os.Interrupt, os.Kill)
+
+ // Block until a signal is received.
+ s := <-c
+ fmt.Println("Got signal:", s)
+}
コアとなるコードの解説
追加された example_test.go
ファイルは、Goのテストフレームワークにおける「Example」関数として機能します。Example
関数は、パッケージのドキュメントに直接組み込まれるコード例として扱われ、go test
コマンド実行時にテストとして実行され、その出力が期待される出力と一致するかどうかが検証されます。これにより、コード例が常に最新かつ正確であることが保証されます。
ExampleNotify
関数は、os/signal
パッケージの Notify
関数の典型的な使用方法を示しています。
package signal_test
: この行は、このファイルがsignal
パッケージの外部にあるテストパッケージであることを示します。これにより、signal
パッケージの公開されたAPIのみを使用してテスト(この場合は例)を書くことができます。import
ブロック:fmt
(出力用)、os
(OS固有の機能、os.Interrupt
やos.Kill
の定義を含む)、そしてos/signal
(シグナル処理機能) パッケージをインポートしています。func ExampleNotify()
: この関数がsignal.Notify
の使用例を提供します。- コメントで、バッファ付きチャネルを使用する必要がある理由が明確に説明されています。「We must use a buffered channel or risk missing the signal if we're not ready to receive when the signal is sent.」(シグナルが送信されたときに受信する準備ができていない場合、シグナルを見逃すリスクがあるため、バッファ付きチャネルを使用する必要があります。)
c := make(chan os.Signal, 1)
: 前述の通り、バッファサイズ1のチャネルを作成します。signal.Notify(c, os.Interrupt, os.Kill)
:SIGINT
とSIGKILL
を捕捉対象として登録します。s := <-c
: シグナルが受信されるまでブロックします。fmt.Println("Got signal:", s)
: 受信したシグナルを出力します。
この例は、Goプログラムでシグナルハンドリングを実装するための最も基本的なパターンであり、多くのGoアプリケーションで graceful shutdown のロジックの出発点となります。
関連リンク
- Go言語の
os/signal
パッケージのドキュメント: https://pkg.go.dev/os/signal - Go言語の
os
パッケージのドキュメント: https://pkg.go.dev/os - Go言語の
syscall
パッケージのドキュメント (シグナル定数の詳細): https://pkg.go.dev/syscall
参考にした情報源リンク
- Go言語の公式ドキュメント (
os/signal
パッケージ) - Go言語のテストに関するドキュメント (Example関数の動作について)
- 一般的なオペレーティングシステムシグナルに関する知識
- Go言語のチャネルに関する知識
- Why a buffered channel is typically used with
os/signal.Notify
: https://calhoun.io/a-buffered-channel-is-often-needed-with-os-signal-notify/ - Go by Example: Signals: https://gobyexample.com/signals