[インデックス 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)
: これは、epoll
やkqueue
といった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
を設定するまでの間に、別のスレッドがfork
とexec
を実行してしまうという競合状態(race condition)を防ぐために重要です。
epoll
とkqueue
これらは、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
パッケージのテストにおいて、子プロセスが親プロセスから予期せぬファイルディスクリプタを継承してしまうというものでした。これは、特にepoll
やkqueue
といったI/O多重化メカニズムが使用する内部的なファイルディスクリプタや、macOSにおけるO_CLOEXEC
の信頼性の問題に起因していました。
以前のバージョンでは、これらの問題が完全に解決されていなかったため、TestHelperProcess
関数内のテストコードに一時的な回避策が導入されていました。この回避策は、ファイルディスクリプタの継承テストが失敗するのを防ぐために、特定の条件下でテストを早期に終了させるというものでした。具体的には、fd 3
から読み取ったデータ(bs
)を標準エラー出力に書き出し、すぐにos.Exit(0)
でプロセスを終了させていました。これにより、その後の「他の開いているファイルディスクリプタがないことを検証する」という本来のテストロジックが実行されないようになっていました。
コミットメッセージに記載されているbe47ea17bea0
と5500053
というコミット(または変更セット)によって、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
コメントが示すように、特定のコミット(be47ea17bea0
と5500053
)がビルドシステムに反映され、新しいバイナリがデプロイされるまでの間、一時的にテストの失敗を回避するためのものでした。
具体的には、fd 3
から読み取ったバイトスライスbs
を標準エラー出力に書き出し、その後すぐにos.Exit(0)
を呼び出してプロセスを正常終了させていました。これにより、このブロックの直後に続く「他の開いているファイルディスクリプタがないことを検証する」という本来のテストロジックが実行されることなく、テストが成功として扱われていました。
基盤となるファイルディスクリプタの継承に関する問題(特にCLOEXEC
フラグの挙動やmacOSでのO_CLOEXEC
の信頼性)が修正されたため、この回避策はもはや不要となりました。このコミットは、その不要な回避策のコードを削除することで、os/exec
パッケージのファイルディスクリプタ継承テストが、実際のシステムの状態を正確に反映し、期待通りに機能するようになることを保証します。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/f2c9d228508d7da944c92804befa28c158e3ea43
- Go Issue #2596 (直接のリンクは特定できませんでしたが、コミットメッセージで参照されています)
- Go Gerrit Change-ID:
https://golang.org/cl/5498061
(Goのコードレビューシステムへのリンク)
参考にした情報源リンク
- Go言語の
os/exec
パッケージに関する公式ドキュメント - Unix系システムにおけるファイルディスクリプタ、
CLOEXEC
、epoll
、kqueue
に関する一般的なドキュメント - Go言語のIssueトラッカー(一般的な情報収集のため)
- Gitのコミットと差分表示に関する情報