[インデックス 18233] ファイルの概要
このコミットは、Go言語の標準ライブラリである os/exec
パッケージ内のテスト TestHelperProcess
において、Plan 9 オペレーティングシステム上でのファイルディスクリプタ (FD) のチェックを無効化する変更を導入しています。Plan 9 環境では、テストが予期しないファイルディスクリプタのリークを示すため、一時的にこのチェックをスキップし、問題の根本原因の調査を継続するための措置が取られました。
コミット
commit 1f0ca748c24e84fa6849e1d9669232dd99cd61ad
Author: David du Colombier <0intro@gmail.com>
Date: Mon Jan 13 23:03:22 2014 +0100
os/exec: disable fd check in TestHelperProcess on Plan 9
On Plan 9, we can observe the following open file descriptors:
0 r c 0 (000000000000000a 0 00) 0 0 /dev/null
1 rw | 0 (0000000001df6742 0 00) 65536 54 #|/data1
2 rw | 0 (0000000001df6782 0 00) 65536 0 #|/data1
3 rw M 1956 (0000000000d66dd2 0 00) 8192 12 /tmp/333163398
4 r c 0 (0000000000000001 0 00) 0 528 /dev/bintime
5 r M 1956 (0000000000d66dd1 854 00) 8192 0 /tmp/go-build843954301/os/exec/_test/exec.test
6 r M 1956 (0000000000d66dd1 854 00) 8192 0 /tmp/go-build843954301/os/exec/_test/exec.test
7 r M 1956 (0000000000d66dd1 854 00) 8192 0 /tmp/go-build843954301/os/exec/_test/exec.test
8 r M 1956 (0000000000d66dd1 854 00) 8192 0 /tmp/go-build843954301/os/exec/_test/exec.test
9 r M 1956 (0000000000d66dd1 854 00) 8192 0 /tmp/go-build843954301/os/exec/_test/exec.test
10 r M 1956 (0000000000d66dd1 854 00) 8192 0 /tmp/go-build843954301/os/exec/_test/exec.test
11 r c 0 (000000000000000f 0 00) 0 32 /dev/random
12 r M 1956 (0000000000d66dd1 854 00) 8192 0 /tmp/go-build843954301/os/exec/_test/exec.test
13 r c 0 (000000000000000a 0 00) 0 0 /dev/null
14 rw | 0 (0000000001df6801 0 00) 65536 0 #|/data
15 rw | 0 (0000000001df6802 0 00) 65536 1275 #|/data1
R=rsc, bradfitz, aram
CC=golang-codereviews
https://golang.org/cl/51420044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1f0ca748c24e84fa6849e1d9669232dd99cd61ad
元コミット内容
このコミットは、os/exec
パッケージのテスト TestHelperProcess
において、Plan 9 環境でファイルディスクリプタのチェックを無効化することを目的としています。コミットメッセージには、Plan 9 上で TestHelperProcess
を実行した際に観測された、予期せぬ開かれたファイルディスクリプタのリストが示されています。これは、テストが期待する状態(特定のファイルディスクリプタのみが開かれている状態)と異なるため、テストが失敗する原因となっていました。
変更の背景
Go言語の os/exec
パッケージは、外部コマンドの実行を扱うための機能を提供します。このパッケージのテストは、コマンドの実行がシステムリソース(特にファイルディスクリプタ)を適切に管理していることを確認するために重要です。TestHelperProcess
は、子プロセスが親プロセスから不要なファイルディスクリプタを継承していないかを検証するテストの一つです。
しかし、Plan 9 オペレーティングシステム上では、このテストが予期せぬファイルディスクリプタのリークを報告していました。コミットメッセージに示されているように、テスト実行時に多数のファイルディスクリプタが開かれている状態が観測され、これがテストの失敗につながっていました。この問題は、GoのIssue 7118 (http://golang.org/issue/7118
) として報告されており、根本原因の特定と解決にはさらなる調査が必要でした。
このコミットは、根本原因が特定されるまでの間、Plan 9 上での TestHelperProcess
の失敗を回避し、CI/CDパイプラインのブロックを防ぐための一時的な措置として導入されました。これにより、他のOSでのテストの健全性を維持しつつ、Plan 9 特有の問題の調査を継続することが可能になります。
前提知識の解説
ファイルディスクリプタ (File Descriptor, FD)
ファイルディスクリプタは、Unix系オペレーティングシステムにおいて、プロセスが開いているファイルやその他のI/Oリソース(ソケット、パイプなど)を参照するために使用される抽象的な識別子です。各プロセスは、0、1、2といった非負の整数値で表されるファイルディスクリプタのテーブルを持っています。
- 0: 標準入力 (stdin)
- 1: 標準出力 (stdout)
- 2: 標準エラー出力 (stderr) これらは通常、プロセスが起動する際に自動的に開かれます。
プログラムがファイルを開いたり、ネットワーク接続を確立したりすると、新しいファイルディスクリプタが割り当てられます。プロセスが終了する際には、通常、開いているすべてのファイルディスクリプタが自動的に閉じられます。しかし、プログラムのバグや特定のシステム動作により、不要なファイルディスクリプタが閉じられずに残ってしまうことがあり、これを「ファイルディスクリプタのリーク」と呼びます。リークが発生すると、システムリソースの枯渇やパフォーマンスの低下につながる可能性があります。
os/exec
パッケージと子プロセス
Go言語の os/exec
パッケージは、Goプログラムから外部コマンドを実行するための機能を提供します。exec.Command
を使用してコマンドを構築し、Run
、Output
、CombinedOutput
などのメソッドで実行します。
子プロセスは、親プロセスからファイルディスクリプタを継承することが一般的です。しかし、セキュリティやリソース管理の観点から、子プロセスには必要最小限のファイルディスクリプタのみを継承させることが望ましいとされます。os/exec
パッケージは、この継承を制御するためのメカニズムを提供しており、テストではこの制御が正しく機能しているかを検証します。
Plan 9 オペレーティングシステム
Plan 9 from Bell Labsは、Unixの設計思想をさらに推し進めた分散オペレーティングシステムです。Unixが「すべてをファイルとして扱う」という哲学を持つのに対し、Plan 9は「すべてをファイルシステムとして扱う」という哲学を持っています。これは、デバイス、プロセス、ネットワークリソースなど、システム内のあらゆるものがファイルシステム階層内のファイルとして表現され、標準的なファイル操作(読み書き、オープン、クローズなど)を通じてアクセスできることを意味します。
Plan 9のファイルディスクリプタの管理やプロセスの状態表現は、一般的なUnix系システムとは異なる場合があります。例えば、プロセスに関する情報は /proc
ファイルシステムを通じて提供されますが、その構造やアクセス方法はLinuxなどのシステムとは異なります。この違いが、GoのテストがPlan 9上で予期せぬファイルディスクリプタを検出する原因となる可能性があります。
Go Issue 3955と7118
-
Issue 3955 (
http://golang.org/issue/3955
): このIssueは、os/exec
のテストにおける/dev/urandom
のファイルディスクリプタのクローンに関する問題に言及しています。一部のシステムでは、/dev/urandom
を開くと、そのファイルディスクリプタが予期せずクローンされ、子プロセスに継承されることがありました。これは、テストが期待するファイルディスクリプタの数と実際の数が異なる原因となっていました。 -
Issue 7118 (
http://golang.org/issue/7118
): このIssueは、まさにこのコミットが対処しようとしているPlan 9上でのファイルディスクリプタリークの問題を追跡するために作成されました。コミットメッセージのTODO
コメントにも明記されており、Plan 9環境でTestHelperProcess
が失敗する根本原因を特定し、解決するための調査が継続中であることを示しています。
技術的詳細
TestHelperProcess
は、Goのテストフレームワークの一部として、os/exec
パッケージが外部コマンドを起動した際に、その子プロセスが親プロセスから不要なファイルディスクリプタを継承していないことを検証します。このテストは、子プロセスが起動した直後に、開かれているファイルディスクリプタのリストを取得し、その数が期待される数(通常は標準入出力の3つ)と一致するかどうかを確認します。
ファイルディスクリプタのリストを取得する方法は、オペレーティングシステムによって異なります。
- BSD系OS (DragonFly, FreeBSD, NetBSD, OpenBSD):
fstat
コマンドを使用します。fstat -p <pid>
のように、特定のプロセスのファイルディスクリプタ情報を取得します。 - Plan 9: このコミット以前は、Plan 9でも
fstat
を使用しようとしていた可能性がありますが、Plan 9にはfstat
コマンドは存在しません。代わりに、Plan 9では/proc/<pid>/fd
のようなパスを通じてプロセスのファイルディスクリプタ情報をファイルシステムとして公開しています。このため、/bin/cat /proc/<pid>/fd
のようにして情報を取得するのが一般的です。
このコミットの主要な変更点は、Plan 9 環境でのファイルディスクリプタのチェック方法と、そのチェックを一時的に無効化するロジックの導入です。
-
ファイルディスクリプタ情報取得コマンドの変更: Plan 9 では
fstat
コマンドが存在しないため、TestHelperProcess
がファイルディスクリプタ情報を取得するために使用するコマンドを/bin/cat
に変更しました。これにより、Plan 9 の/proc
ファイルシステムを通じてファイルディスクリプタの情報を読み取ることが可能になります。 -
Plan 9 での FD チェックの無効化:
TestHelperProcess
内で、runtime.GOOS
が "plan9" の場合、ファイルディスクリプタのリークチェックをスキップするTODO
コメントが追加されました。これは、Plan 9 上で予期せぬファイルディスクリプタのリークが発生していることが確認されており、その根本原因が特定されるまで、テストの失敗を防ぐための一時的な措置です。コミットメッセージに示されているように、多数のファイルディスクリプタがテスト実行時に開かれていることが観測されており、これらがテストの期待値と一致しないため、テストが失敗していました。 -
FD 情報表示コマンドの引数変更: ファイルディスクリプタのリークが検出された際に、詳細な情報を出力するために実行されるコマンドの引数が、Plan 9 向けに調整されました。Plan 9 では
/proc/<pid>/fd
の形式でファイルディスクリプタのパスが指定されるため、exec.Command
に渡す引数もそれに合わせて変更されています。
この変更は、Plan 9 という特定のOS環境におけるGoのテストの安定性を向上させるためのものであり、Goのクロスプラットフォーム対応の複雑さの一端を示しています。
コアとなるコードの変更箇所
変更は src/pkg/os/exec/exec_test.go
ファイルに集中しています。
--- a/src/pkg/os/exec/exec_test.go
+++ b/src/pkg/os/exec/exec_test.go
@@ -480,6 +480,8 @@ func TestHelperProcess(*testing.T) {
switch runtime.GOOS {
case "dragonfly", "freebsd", "netbsd", "openbsd":
ofcmd = "fstat"
+ case "plan9":
+ ofcmd = "/bin/cat"
}
args := os.Args
@@ -570,6 +572,10 @@ func TestHelperProcess(*testing.T) {
// the cloned file descriptors that result from opening
// /dev/urandom.
// http://golang.org/issue/3955
+ case "plan9":
+ // TODO(0intro): Determine why Plan 9 is leaking
+ // file descriptors.
+ // http://golang.org/issue/7118
case "solaris":
// TODO(aram): This fails on Solaris because libc opens
// its own files, as it sees fit. Darwin does the same,
@@ -585,7 +591,14 @@ func TestHelperProcess(*testing.T) {
if got := f.Fd(); got != wantfd {
fmt.Printf("leaked parent file. fd = %d; want %d\n", got, wantfd)
out, _ := exec.Command(ofcmd, "-p", fmt.Sprint(os.Getpid())).CombinedOutput()
+ var args []string
+ switch runtime.GOOS {
+ case "plan9":
+ args = []string{fmt.Sprintf("/proc/%d/fd", os.Getpid())}
+ default:
+ args = []string{"-p", fmt.Sprint(os.Getpid())}
+ }
+ out, _ := exec.Command(ofcmd, args...).CombinedOutput()
fmt.Print(string(out))
os.Exit(1)
}
コアとなるコードの解説
-
ofcmd
の設定 (L480-482):ofcmd
変数は、開いているファイルディスクリプタの情報を取得するために使用されるコマンドを格納します。- 既存の
dragonfly
,freebsd
,netbsd
,openbsd
のケースではfstat
が設定されています。 - 新たに
case "plan9":
が追加され、ofcmd = "/bin/cat"
が設定されました。これは、Plan 9 ではfstat
の代わりに/bin/cat
を使用して/proc
ファイルシステムからFD情報を読み取るためです。
- 既存の
-
Plan 9 での FD チェックのスキップ (L570-575):
TestHelperProcess
内のファイルディスクリプタのリークチェックを行うswitch runtime.GOOS
ブロックに、case "plan9":
が追加されました。- このブロック内には
TODO
コメントがあり、「Plan 9 がファイルディスクリプタをリークしている理由を特定する」という課題 (http://golang.org/issue/7118
) が示されています。 - この
case
ブロックの追加により、Plan 9 環境では、この特定のファイルディスクリプタリークチェックが実質的にスキップされることになります。これは、テストが失敗するのを防ぐための一時的な回避策です。
- このブロック内には
-
FD 情報表示コマンドの引数変更 (L585-594): ファイルディスクリプタのリークが検出された際に、デバッグ情報として開いているFDのリストを出力するために
exec.Command
が使用されます。このコマンドに渡す引数がruntime.GOOS
に応じて分岐されるようになりました。- 既存のコードでは、
ofcmd
(例:fstat
) に対して-p
とプロセスIDを引数として渡していました。 - 新たに
switch runtime.GOOS
が導入され、case "plan9":
の場合、args
は[]string{fmt.Sprintf("/proc/%d/fd", os.Getpid())}
となります。これは、Plan 9 の/proc
ファイルシステムにおけるFD情報のパスを指定しています。 default:
ケースでは、以前と同様に-p
とプロセスIDを引数として渡します。 この変更により、Plan 9 環境で正しいコマンドと引数を使用してFD情報を取得し、デバッグ出力に含めることができるようになりました。
- 既存のコードでは、
これらの変更は、Plan 9 という特定のOS環境におけるGoのテストの互換性と安定性を向上させるためのものであり、Goのクロスプラットフォーム対応の複雑さの一端を示しています。
関連リンク
- Go GitHub リポジトリ: https://github.com/golang/go
- Go Issue 7118: https://golang.org/issue/7118 (Plan 9 でのファイルディスクリプタリークに関する問題)
- Go Issue 3955: https://golang.org/issue/3955 (/dev/urandom のFDクローンに関する問題)
- Gerrit Code Review (CL 51420044): https://golang.org/cl/51420044 (このコミットの元のコードレビューページ)
参考にした情報源リンク
- Plan 9 from Bell Labs: https://9p.io/plan9/
- Unix系OSにおけるファイルディスクリプタの概念
- Go言語
os/exec
パッケージのドキュメント