Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 14133] ファイルの概要

このコミットは、Go言語の標準ライブラリ os/signal パッケージに Notify 関数の使用例を追加するものです。具体的には、example_test.go という新しいテストファイルが追加され、os.Interruptos.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 関数をどのように使用するかを具体的に示しています。

  1. チャネルの作成:

    c := make(chan os.Signal, 1)
    

    ここで、os.Signal 型のチャネル c が作成されます。重要なのは、このチャネルがバッファサイズ1で作成されている点です。これは、signal.Notify のドキュメントにも明記されているように、シグナルが非同期に発生するため、受信側が常に準備できているとは限らないからです。バッファがない場合、シグナルが送信された瞬間にチャネルがブロックされていると、そのシグナルは失われてしまいます。バッファを設けることで、シグナルがチャネルに一時的に格納され、後で安全に読み取ることができます。バッファサイズが1であれば、少なくとも1つのシグナルを確実に捕捉できます。

  2. シグナルの登録:

    signal.Notify(c, os.Interrupt, os.Kill)
    

    signal.Notify 関数が呼び出され、c チャネルに os.Interrupt (SIGINT) と os.Kill (SIGKILL) シグナルを転送するようにGoランタイムに指示しています。これにより、これらのシグナルがプロセスに送信されると、c チャネルに値が送信されます。

  3. シグナルの待機と処理:

    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 関数の典型的な使用方法を示しています。

  1. package signal_test: この行は、このファイルが signal パッケージの外部にあるテストパッケージであることを示します。これにより、signal パッケージの公開されたAPIのみを使用してテスト(この場合は例)を書くことができます。
  2. import ブロック: fmt (出力用)、os (OS固有の機能、os.Interruptos.Kill の定義を含む)、そして os/signal (シグナル処理機能) パッケージをインポートしています。
  3. 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): SIGINTSIGKILL を捕捉対象として登録します。
    • s := <-c: シグナルが受信されるまでブロックします。
    • fmt.Println("Got signal:", s): 受信したシグナルを出力します。

この例は、Goプログラムでシグナルハンドリングを実装するための最も基本的なパターンであり、多くのGoアプリケーションで graceful shutdown のロジックの出発点となります。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (os/signal パッケージ)
  • Go言語のテストに関するドキュメント (Example関数の動作について)
  • 一般的なオペレーティングシステムシグナルに関する知識
  • Go言語のチャネルに関する知識

[インデックス 14133] ファイルの概要

このコミットは、Go言語の標準ライブラリ os/signal パッケージに Notify 関数の使用例を追加するものです。具体的には、example_test.go という新しいテストファイルが追加され、os.Interruptos.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 関数をどのように使用するかを具体的に示しています。

  1. チャネルの作成:

    c := make(chan os.Signal, 1)
    

    ここで、os.Signal 型のチャネル c が作成されます。重要なのは、このチャネルがバッファサイズ1で作成されている点です。これは、signal.Notify のドキュメントにも明記されているように、シグナルが非同期に発生するため、受信側が常に準備できているとは限らないからです。os/signal パッケージは、シグナルをチャネルに送信する際にブロックしないため、バッファがないチャネルを使用すると、受信側が準備できていない場合にシグナルが失われる可能性があります。バッファを設けることで、シグナルがチャネルに一時的に格納され、後で安全に読み取ることができます。バッファサイズが1であれば、少なくとも1つのシグナルを確実に捕捉できます。これは、最初のシグナル(例えば、グレースフルシャットダウンを開始するための最初の SIGINT)のみを気にする場合に十分です。

  2. シグナルの登録:

    signal.Notify(c, os.Interrupt, os.Kill)
    

    signal.Notify 関数が呼び出され、c チャネルに os.Interrupt (SIGINT) と os.Kill (SIGKILL) シグナルを転送するようにGoランタイムに指示しています。これにより、これらのシグナルがプロセスに送信されると、c チャネルに値が送信されます。

  3. シグナルの待機と処理:

    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 関数の典型的な使用方法を示しています。

  1. package signal_test: この行は、このファイルが signal パッケージの外部にあるテストパッケージであることを示します。これにより、signal パッケージの公開されたAPIのみを使用してテスト(この場合は例)を書くことができます。
  2. import ブロック: fmt (出力用)、os (OS固有の機能、os.Interruptos.Kill の定義を含む)、そして os/signal (シグナル処理機能) パッケージをインポートしています。
  3. 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): SIGINTSIGKILL を捕捉対象として登録します。
    • s := <-c: シグナルが受信されるまでブロックします。
    • fmt.Println("Got signal:", s): 受信したシグナルを出力します。

この例は、Goプログラムでシグナルハンドリングを実装するための最も基本的なパターンであり、多くのGoアプリケーションで graceful shutdown のロジックの出発点となります。

関連リンク

参考にした情報源リンク