[インデックス 12681] ファイルの概要
このコミットは、Go言語の標準ライブラリsrc/pkg/syscall
ディレクトリからpassfd_test.go
というテストファイルを削除するものです。このテストファイルは、Unixドメインソケットを介したファイルディスクリプタの受け渡し機能(passfd
)を検証するために使用されていました。コミットメッセージによると、Go 1.0のリリース後にsyscall
パッケージにおけるテストの問題を再検討するという方針のもと、一時的に削除されました。
コミット
commit 4161dfc4feafa08870e347c5ca7da155a9790867
Author: Rob Pike <r@golang.org>
Date: Mon Mar 19 11:12:32 2012 +1100
syscall: delete passfd_test.go
We can revisit the issue of testing in syscall after Go 1.
R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/5844057
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4161dfc4feafa08870e347c5ca7da155a9790867
元コミット内容
このコミットは、src/pkg/syscall/passfd_test.go
というファイルを削除しました。このファイルは、Go言語のsyscall
パッケージが提供するUnixドメインソケットを介したファイルディスクリプタの受け渡し機能(SCM_RIGHTS
メッセージ)が正しく動作するかを検証するためのテストコードを含んでいました。具体的には、親プロセスが子プロセスにソケットを介してファイルディスクリプタを渡し、子プロセスがそのディスクリプタを使ってファイルの内容を読み書きできることを確認するテストでした。
変更の背景
この変更の背景には、Go言語の最初の安定版リリースであるGo 1.0の準備段階における開発方針があります。コミットメッセージ「We can revisit the issue of testing in syscall after Go 1.」が示すように、Go 1.0のリリースでは、言語と標準ライブラリの安定性と互換性の確保が最優先事項でした。
syscall
パッケージは、OS固有のシステムコールをGoから直接呼び出すための低レベルなインターフェースを提供します。ファイルディスクリプタの受け渡しのような機能は、OSや環境に依存する複雑な側面を持つため、テストの安定性や移植性に課題があった可能性があります。Go 1.0のリリース前に、このような複雑なテストケースが全体の安定性を損なうリスクを避けるため、あるいはより広範なテスト戦略が確立されるまで、一時的に削除するという判断がなされたと考えられます。Go 1.0リリース後、より成熟したテストインフラや知見が蓄積された段階で、この種のテストを再導入することが計画されていました。
前提知識の解説
Go言語のsyscall
パッケージ
Go言語のsyscall
パッケージは、オペレーティングシステム(OS)のシステムコールに直接アクセスするための低レベルなインターフェースを提供します。これにより、GoプログラムからOSのカーネル機能(ファイル操作、プロセス管理、ネットワーク通信など)を直接呼び出すことが可能になります。os
やnet
パッケージのような高レベルな抽象化された機能の多くは、内部でこのsyscall
パッケージを利用しています。
ファイルディスクリプタ (File Descriptor, FD)
ファイルディスクリプタ(FD)は、Unix系OSにおいて、プロセスが開いているファイル、ソケット、パイプなどのI/Oリソースを識別するためにカーネルが割り当てる非負の整数値です。プロセスは、このFDを使って対応するリソースに対して読み書きなどの操作を行います。
Unixドメインソケット (Unix Domain Socket)
Unixドメインソケットは、同一ホスト上のプロセス間通信(IPC: Inter-Process Communication)のためのメカニズムです。ネットワークソケット(TCP/IPなど)とは異なり、ネットワークスタックを介さずに直接カーネル内で通信が行われるため、効率的です。Unixドメインソケットの特筆すべき機能の一つに、**ファイルディスクリプタの受け渡し(Passing File Descriptors)**があります。これは、あるプロセスが別のプロセスに、開いているファイルディスクリプタをソケットを介して安全に渡すことができる機能です。これにより、例えば特権プロセスがファイルをオープンし、そのFDを非特権プロセスに渡して操作させるといった、セキュリティやリソース管理のシナリオで利用されます。
SCM_RIGHTS
メッセージ
Unixドメインソケットを介してファイルディスクリプタを渡す際に使用されるのが、SCM_RIGHTS
という制御メッセージです。sendmsg
システムコールでSCM_RIGHTS
を指定してFDを送信し、recvmsg
システムコールで受信します。Go言語のsyscall
パッケージでは、UnixRights
関数でFDのリストからSCM_RIGHTs
メッセージを生成し、ParseUnixRights
関数で受信したSCM_RIGHTS
メッセージからFDを抽出します。
Go 1.0
Go 1.0は、2012年3月28日にリリースされたGo言語の最初の安定版です。このリリースは、Go言語の仕様と標準ライブラリのAPIを安定させ、将来のバージョンとの互換性を保証することを目的としていました。Go 1.0のリリース以降、Go言語は急速に普及し、多くのプロジェクトで利用されるようになりました。このコミットが行われたのはGo 1.0リリース直前の時期であり、安定性確保のための最終調整が行われていたことが伺えます。
技術的詳細
削除されたpassfd_test.go
ファイルは、TestPassFD
とTestPassFDChild
という2つのテスト関数を含んでいました。これらは、Go言語でUnixドメインソケットを介してファイルディスクリプタを安全に受け渡す機能が正しく動作するかを検証するためのものです。
TestPassFD
(親プロセス側のテスト)
この関数は、テストの親プロセスとして動作します。
- ソケットペアの作成:
syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0)
を呼び出して、Unixドメインソケットのペアを作成します。これにより、親プロセスと子プロセスが通信するための双方向のソケットが用意されます。fds[0]
とfds[1]
の2つのファイルディスクリプタが返されます。 - 子プロセスの起動:
os/exec.Command
を使用して、現在のテストバイナリ自身を子プロセスとして起動します。この際、GO_WANT_HELPER_PROCESS=1
という環境変数を設定し、TestPassFDChild
関数を実行するように引数を渡します。 - ファイルディスクリプタの受け渡し準備:
writeFile := os.NewFile(uintptr(fds[0]), "child-writes")
として、ソケットペアの一方のFDをos.File
としてラップし、cmd.ExtraFiles = []*os.File{writeFile}
を通じて子プロセスに渡します。これにより、子プロセスは親プロセスから渡されたソケットを通じて通信できるようになります。 - 子プロセスの実行と出力の確認:
cmd.CombinedOutput()
で子プロセスを実行し、その出力やエラーを確認します。 - ソケット接続の確立:
net.FileConn(readFile)
を使って、ソケットペアのもう一方のFD(親プロセス側)からnet.Conn
インターフェースを取得します。これは、net.UnixConn
型にキャストされます。 - ファイルディスクリプタの受信:
uc.ReadMsgUnix(buf, oob)
を呼び出して、子プロセスから送信されるメッセージと制御メッセージ(oob
)を受信します。このoob
バッファには、子プロセスが送信したファイルディスクリプタが含まれるSCM_RIGHTS
メッセージが格納されます。 - 受信したFDの解析:
syscall.ParseSocketControlMessage(oob[:oobn])
で制御メッセージを解析し、syscall.SocketControlMessage
のリストを取得します。syscall.ParseUnixRights(&scm)
で、取得した制御メッセージからファイルディスクリプタのリスト(gotFds
)を抽出します。
- 受信したFDの検証: 抽出されたFDが期待通り1つであり、そのFDを使ってファイルの内容を読み込み、「Hello from child process!」という文字列が正しく読み取れることを検証します。
TestPassFDChild
(子プロセス側のテストヘルパー)
この関数は、TestPassFD
によって子プロセスとして起動された際に実行されます。
- ヘルパープロセスの識別:
os.Getenv("GO_WANT_HELPER_PROCESS") != "1"
で、自身がヘルパープロセスとして起動されたかどうかを確認します。 - 親プロセスから渡されたソケットの特定: 子プロセスは、親プロセスから
ExtraFiles
を通じて渡されたソケットのファイルディスクリプタを特定します。通常はFD 3以降に割り当てられますが、Goのバグ(issue 2603)を回避するために、FD 3から10までをループしてnet.UnixConn
として開けるFDを探します。 - 送信するファイルの準備:
ioutil.TempFile
で一時ファイルを作成し、「Hello from child process!」という文字列を書き込みます。この一時ファイルのファイルディスクリプタを親プロセスに送信します。 - ファイルディスクリプタの送信:
syscall.UnixRights(int(f.Fd()))
で、送信したいファイルディスクリプタからSCM_RIGHTS
メッセージを生成します。uc.WriteMsgUnix(dummyByte, rights, nil)
を呼び出して、ダミーのデータ(dummyByte
)と、生成したSCM_RIGHTS
メッセージ(rights
)を親プロセスに送信します。
このテストは、Go言語のsyscall
パッケージが提供する低レベルな機能が、異なるプロセス間でどのように連携して動作するかを示す良い例でした。しかし、このようなテストはOSの挙動や環境に強く依存するため、Go 1.0の安定性確保の観点から、一時的に削除されたと考えられます。
コアとなるコードの変更箇所
このコミットによるコアとなるコードの変更は、以下のファイルの削除のみです。
src/pkg/syscall/passfd_test.go
コアとなるコードの解説
削除されたpassfd_test.go
は、Go言語のsyscall
パッケージにおけるファイルディスクリプタの受け渡し機能のテストを担っていました。このテストは、親プロセスと子プロセスがUnixドメインソケットを介して通信し、親プロセスが子プロセスにファイルディスクリプタを渡し、子プロセスがそのディスクリプタを使ってファイルの内容を読み書きできることを検証するものでした。
具体的には、TestPassFD
関数が親プロセスとしてソケットペアを作成し、子プロセス(TestPassFDChild
関数)を起動します。親プロセスはソケットの一端を子プロセスに渡し、子プロセスはもう一方のソケットを通じて親プロセスにファイルディスクリプタを送信します。親プロセスは受信したファイルディスクリプタが有効であることを確認し、そのディスクリプタを使ってファイルの内容を読み込み、期待される結果と比較します。
このテストは、syscall.Socketpair
、net.FileConn
、net.UnixConn
、ReadMsgUnix
、WriteMsgUnix
、ParseSocketControlMessage
、ParseUnixRights
といったsyscall
パッケージの主要な関数を組み合わせて使用しており、ファイルディスクリプタの受け渡しという高度なIPCメカニズムのGo言語での実装と利用方法を示す貴重な例でした。
削除の理由は、Go 1.0のリリースに向けた安定性確保と、syscall
パッケージのテスト戦略の再検討という方針によるものです。これは、この種の低レベルなテストがOSや環境に依存しやすく、当時のGoのテストインフラでは安定したテスト実行が困難であった可能性を示唆しています。
関連リンク
- Go言語の
syscall
パッケージのドキュメント: https://pkg.go.dev/syscall - Unixドメインソケットに関するWikipedia記事: https://ja.wikipedia.org/wiki/Unix%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%82%BD%E3%82%B1%E3%83%83%E3%83%88
- ファイルディスクリプタの受け渡しに関する情報 (Linux man page): https://man7.org/linux/man-pages/man7/unix.7.html (SCM_RIGHTSセクションを参照)
- Go 1.0 Release Notes: https://go.dev/doc/go1
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード(削除された
passfd_test.go
の内容) - Unixドメインソケットとファイルディスクリプタの受け渡しに関する一般的な技術情報
- Go 1.0リリースに関する情報