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

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

コミット

このコミットは、Go言語の標準ライブラリos/execパッケージ内のテストコードから、一時的な回避策として導入されていたブロックを削除し、ファイルディスクリプタの継承に関するテストを再度有効にするものです。以前のコミットで解決された基盤となる問題(CLOEXECフラグの信頼性に関する問題)により、このテストは一時的に無効化されていました。今回の変更により、テストが本来の意図通りに機能するようになります。

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/f2c9d228508d7da944c92804befa28c158e3ea43

元コミット内容

os/exec: enable inherited file descriptor test

Fixes #2596.

R=golang-dev
CC=golang-dev
https://golang.org/cl/5498061

変更の背景

この変更の背景には、Go言語のos/execパッケージにおけるプロセス間通信とファイルディスクリプタの継承に関する問題がありました。特に、子プロセスが親プロセスから予期せぬファイルディスクリプタを継承してしまうというバグが存在していました。

コミットメッセージ内のFixes #2596は、このコミットがGoのIssue 2596を修正することを示しています。このIssueは、os/execパッケージのテストにおいて、子プロセスが親プロセスからファイルディスクリプタを不適切に継承してしまう問題に関連していると考えられます。

また、削除されたコードブロック内のTODOコメントは、このブロックが一時的な回避策であったことを明確に示しています。

  • be47ea17bea0 (set CLOEXEC on epoll/kqueue fds): これは、epollkqueueといったI/O多重化メカニズムに関連するファイルディスクリプタにCLOEXECフラグを設定する修正が行われたことを示唆しています。
  • 5500053 (don't trust O_CLOEXEC on OS X): これは、macOS(OS X)においてO_CLOEXECフラグの挙動が信頼できないという問題に対する修正が行われたことを示唆しています。

これらの基盤となる問題が解決されたため、一時的に無効化されていたファイルディスクリプタ継承テストを再度有効にすることが可能になりました。

前提知識の解説

ファイルディスクリプタ (File Descriptor, FD)

ファイルディスクリプタは、Unix系オペレーティングシステムにおいて、プロセスが開いているファイルやソケット、パイプなどのI/Oリソースを識別するために使用される整数値です。標準入力(0)、標準出力(1)、標準エラー出力(2)は、すべてのプロセスでデフォルトで開かれているファイルディスクリプタです。

CLOEXEC (Close-on-exec) フラグ

CLOEXECは「Close-on-exec」の略で、ファイルディスクリプタに設定できるフラグの一つです。このフラグが設定されたファイルディスクリプタは、execシステムコール(新しいプログラムを実行する際に使用される)が呼び出されたときに自動的に閉じられます。これにより、子プロセスが親プロセスから不要なファイルディスクリプタを継承することを防ぎ、セキュリティリスクの低減やリソースリークの防止に役立ちます。

O_CLOEXEC

O_CLOEXECは、openシステムコールなどでファイルを開く際に、同時にCLOEXECフラグを設定するためのオプションです。これにより、ファイルを開く操作とCLOEXECフラグの設定をアトミックに行うことができます。これは、ファイルを開いてからfcntlなどでCLOEXECを設定するまでの間に、別のスレッドがforkexecを実行してしまうという競合状態(race condition)を防ぐために重要です。

epollkqueue

これらは、Unix系システムにおける高性能なI/O多重化メカニズムです。

  • epoll: Linuxカーネルが提供するI/Oイベント通知メカニズム。多数のファイルディスクリプタを効率的に監視し、イベントが発生したディスクリプタのみを通知します。
  • kqueue: FreeBSD、macOS、NetBSD、OpenBSDなどのBSD系システムが提供するI/Oイベント通知メカニズム。epollと同様に、多数のファイルディスクリプタからのイベントを効率的に処理します。

これらのメカニズム自体もファイルディスクリプタを内部的に使用するため、それらのディスクリプタが子プロセスに不適切に継承されないようにCLOEXECフラグが適切に設定されることが重要です。

os/execパッケージ (Go言語)

Go言語のos/execパッケージは、外部コマンドを実行するための機能を提供します。このパッケージを使用すると、新しいプロセスを起動し、その標準入出力や環境変数を制御することができます。子プロセスが親プロセスからファイルディスクリプタをどのように継承するかは、このパッケージの挙動において非常に重要な側面です。

技術的詳細

このコミットが修正する問題は、os/execパッケージのテストにおいて、子プロセスが親プロセスから予期せぬファイルディスクリプタを継承してしまうというものでした。これは、特にepollkqueueといったI/O多重化メカニズムが使用する内部的なファイルディスクリプタや、macOSにおけるO_CLOEXECの信頼性の問題に起因していました。

以前のバージョンでは、これらの問題が完全に解決されていなかったため、TestHelperProcess関数内のテストコードに一時的な回避策が導入されていました。この回避策は、ファイルディスクリプタの継承テストが失敗するのを防ぐために、特定の条件下でテストを早期に終了させるというものでした。具体的には、fd 3から読み取ったデータ(bs)を標準エラー出力に書き出し、すぐにos.Exit(0)でプロセスを終了させていました。これにより、その後の「他の開いているファイルディスクリプタがないことを検証する」という本来のテストロジックが実行されないようになっていました。

コミットメッセージに記載されているbe47ea17bea05500053というコミット(または変更セット)によって、epoll/kqueueのファイルディスクリプタに対するCLOEXEC設定の信頼性向上と、macOSにおけるO_CLOEXECの挙動の修正が行われました。これらの基盤となる問題が解決されたことで、os/execパッケージのテストは、子プロセスが不要なファイルディスクリプタを継承しないという前提で正しく実行できるようになりました。

したがって、このコミットでは、もはや不要となった一時的な回避策のコードブロックを削除し、ファイルディスクリプタの継承テストが意図通りに実行されるようにしています。

コアとなるコードの変更箇所

変更はsrc/pkg/os/exec/exec_test.goファイルに対して行われました。具体的には、TestHelperProcess関数内の以下の7行が削除されました。

--- a/src/pkg/os/exec/exec_test.go
+++ b/src/pkg/os/exec/exec_test.go
@@ -256,13 +256,6 @@ func TestHelperProcess(*testing.T) {
 		\t\t\tfmt.Printf(\"ReadAll from fd 3: %v\", err)\n \t\t\tos.Exit(1)\n \t\t}\n-\t\t// TODO(bradfitz): remove this block once the builders are restarted\n-\t\t// with a new binary including be47ea17bea0 (set CLOEXEC on epoll/kqueue fds)\n-\t\t// and 5500053 (don\'t trust O_CLOEXEC on OS X).\n-\t\t{\n-\t\t\tos.Stderr.Write(bs)\n-\t\t\tos.Exit(0)\n-\t\t}\n \t\t// Now verify that there are no other open fds.\n \t\tvar files []*os.File\n \t\tfor wantfd := os.Stderr.Fd() + 2; wantfd <= 100; wantfd++ {\

コアとなるコードの解説

削除されたコードブロックは、TestHelperProcess関数内で、ファイルディスクリプタ3からの読み取りエラーが発生した場合の処理の直後に配置されていました。

		// TODO(bradfitz): remove this block once the builders are restarted
		// with a new binary including be47ea17bea0 (set CLOEXEC on epoll/kqueue fds)
		// and 5500053 (don't trust O_CLOEXEC on OS X).
		{
			os.Stderr.Write(bs)
			os.Exit(0)
		}

このブロックは、TODOコメントが示すように、特定のコミット(be47ea17bea05500053)がビルドシステムに反映され、新しいバイナリがデプロイされるまでの間、一時的にテストの失敗を回避するためのものでした。

具体的には、fd 3から読み取ったバイトスライスbsを標準エラー出力に書き出し、その後すぐにos.Exit(0)を呼び出してプロセスを正常終了させていました。これにより、このブロックの直後に続く「他の開いているファイルディスクリプタがないことを検証する」という本来のテストロジックが実行されることなく、テストが成功として扱われていました。

基盤となるファイルディスクリプタの継承に関する問題(特にCLOEXECフラグの挙動やmacOSでのO_CLOEXECの信頼性)が修正されたため、この回避策はもはや不要となりました。このコミットは、その不要な回避策のコードを削除することで、os/execパッケージのファイルディスクリプタ継承テストが、実際のシステムの状態を正確に反映し、期待通りに機能するようになることを保証します。

関連リンク

参考にした情報源リンク

  • Go言語のos/execパッケージに関する公式ドキュメント
  • Unix系システムにおけるファイルディスクリプタ、CLOEXECepollkqueueに関する一般的なドキュメント
  • Go言語のIssueトラッカー(一般的な情報収集のため)
  • Gitのコミットと差分表示に関する情報