[インデックス 17688] ファイルの概要
このコミットは、Go言語の net
パッケージにおけるUnixドメインソケットのオートバインド機能に関するバグ修正です。具体的には、オートバインドされたUnixリスナーを Close
する際の挙動が修正されています。
コミット
commit 2be62360b2748636a2849e959dede7359383b200
Author: Albert Strasheim <fullung@gmail.com>
Date: Mon Sep 23 22:33:42 2013 -0400
net: fix Close of autobind unix listener
Fixes #6455.
R=mikioh.mikioh, rsc
CC=golang-dev
https://golang.org/cl/13457058
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2be62360b2748636a2849e959dede7359383b200
元コミット内容
net: fix Close of autobind unix listener
Fixes #6455.
R=mikioh.mikioh, rsc
CC=golang-dev
https://golang.org/cl/13457058
変更の背景
このコミットは、Go言語の net
パッケージにおいて、Unixドメインソケットのオートバインド機能を使用した場合に発生する Close
処理の不具合を修正するために行われました。
Unixドメインソケットでは、ソケットをファイルシステム上のパスにバインドします。通常、ユーザーは UnixAddr
構造体の Name
フィールドに明示的なパスを指定してソケットをバインドします。しかし、Name
フィールドを空文字列 (""
) に設定すると、システムが一時的なユニークなパスを自動的に割り当ててソケットをバインドする「オートバインド」という機能があります。これは、特にクライアント側で一時的なソケットが必要な場合や、サーバー側で一時的な通信チャネルを確立する場合に便利です。
このオートバインドされたソケットを Close
する際、Goの net
パッケージの内部実装において、ソケットが実際にバインドされた一時的なパスが正しく認識されず、結果としてソケットファイルが適切にクリーンアップされないという問題がありました。これにより、一時ファイルが残り続け、リソースリークやファイルシステム上のゴミの蓄積につながる可能性がありました。
コミットメッセージにある "Fixes #6455" は、GoのIssueトラッカーにおける特定のバグ報告を指している可能性が高いですが、一般的なWeb検索ではRFC 6455 (WebSocket Protocol) がヒットするため、この番号がGoのIssue番号として直接参照できないか、あるいは内部的な参照である可能性があります。しかし、コミットの内容から、この問題がオートバインドされたUnixリスナーの Close
処理に関するものであることは明確です。
前提知識の解説
Unixドメインソケット (Unix Domain Sockets, UDS)
Unixドメインソケットは、同じホストマシン上で動作するプロセス間通信 (IPC) の一種です。TCP/IPソケットがネットワークを介した通信に使用されるのに対し、Unixドメインソケットはファイルシステム上のパス(通常はファイル)を介して通信を行います。これにより、ネットワークスタックを介さないため、TCP/IPソケットよりも高速で低レイテンシな通信が可能です。
Unixドメインソケットには、ストリーム型(TCPに類似)とデータグラム型(UDPに類似)があります。Goの net
パッケージでは、ListenUnix
や DialUnix
などの関数を通じてUnixドメインソケットを扱います。
オートバインド (Autobind)
Unixドメインソケットにおいて、ソケットを特定のファイルパスに明示的にバインドする代わりに、システムに一時的なユニークなファイルパスを自動的に割り当てさせる機能です。Goの net
パッケージでは、UnixAddr
構造体の Name
フィールドを空文字列 (""
) に設定することでオートバインドが有効になります。
オートバインドされたソケットは、通常、そのソケットが閉じられたときに、システムによって自動的に作成された一時ファイルも削除されることが期待されます。
net
パッケージ (Go言語)
Go言語の標準ライブラリである net
パッケージは、ネットワークI/Oのプリミティブを提供します。TCP/IP、UDP、Unixドメインソケットなど、様々なネットワークプロトコルを扱うためのインターフェースが含まれています。
net
パッケージにおけるリスナー(net.Listener
インターフェースを実装する型)は、通常、Close()
メソッドを提供します。このメソッドは、リスナーを閉じ、関連するリソース(ソケットファイルなど)を解放する責任があります。Unixドメインソケットの場合、Close()
はバインドされたソケットファイルを削除することも含みます。
UnixAddr
構造体
net
パッケージにおける UnixAddr
構造体は、Unixドメインソケットのアドレスを表します。
type UnixAddr struct {
Name string // ソケットのパス名
Net string // ネットワークタイプ ("unix", "unixgram", "unixpacket")
}
Name
フィールドがソケットがバインドされるファイルパスを指定します。オートバインドの場合、このフィールドは初期化時に空文字列になります。
UnixListener
構造体
net
パッケージにおける UnixListener
構造体は、Unixドメインソケットのリスナーを表します。
type UnixListener struct {
fd *netFD // ファイルディスクリプタ
// ...
laddr string // リスナーのローカルアドレスの文字列表現
}
laddr
フィールドは、リスナーのローカルアドレスの文字列表現を保持します。オートバインドの場合、この laddr
にはシステムが割り当てた一時的なパスが格納されるべきです。
技術的詳細
このコミットの技術的な核心は、オートバインドされたUnixドメインソケットの UnixListener
が、その laddr
フィールドに正しいローカルアドレス(システムが割り当てた一時的なパス)を保持していなかった点にあります。
ListenUnix
関数は、Unixドメインソケットをリッスンするために使用されます。オートバインドが指定された場合(laddr.Name
が空文字列の場合)、カーネルは一時的なユニークなパスを生成し、そのパスにソケットをバインドします。しかし、ListenUnix
の以前の実装では、UnixListener
構造体の laddr
フィールドに、初期の laddr.Name
(つまり空文字列)がそのまま格納されていました。
UnixListener.Close()
メソッドが呼び出された際、この laddr
フィールドの値を使用して、バインドされたソケットファイルを削除しようとします。しかし、オートバインドの場合、laddr
が空文字列であるため、Close()
メソッドは正しい一時ファイルパスを特定できず、結果としてソケットファイルが削除されずに残ってしまうというバグがありました。
このコミットは、ListenUnix
関数内で UnixListener
を初期化する際に、laddr.Name
の代わりに fd.laddr.String()
を使用するように変更することで、この問題を解決しています。fd.laddr.String()
は、ソケットが実際にバインドされた後の、カーネルによって割り当てられた実際のローカルアドレスの文字列表現を返します。これにより、UnixListener
の laddr
フィールドには常に正しいパスが格納されるようになり、Close()
メソッドが呼び出された際に、そのパスにある一時ファイルが適切に削除されるようになります。
また、この修正を検証するために、src/pkg/net/unix_test.go
に TestUnixAutobindClose
という新しいテストケースが追加されました。このテストは、オートバインドされたUnixリスナーを作成し、すぐに Close()
を呼び出すことで、ソケットファイルが正しくクリーンアップされることを確認します。
コアとなるコードの変更箇所
src/pkg/net/unix_test.go
--- a/src/pkg/net/unix_test.go
+++ b/src/pkg/net/unix_test.go
@@ -107,7 +107,7 @@ func TestReadUnixgramWithZeroBytesBuffer(t *testing.T) {
}
}
-func TestUnixAutobind(t *testing.T) {
+func TestUnixgramAutobind(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skip("skipping: autobind is linux only")
}
@@ -139,6 +139,18 @@ func TestUnixAutobind(t *testing.T) {
}
}
+func TestUnixAutobindClose(t *testing.T) {
+ if runtime.GOOS != "linux" {
+ t.Skip("skipping: autobind is linux only")
+ }
+ laddr := &UnixAddr{Name: "", Net: "unix"}
+ ln, err := ListenUnix("unix", laddr)
+ if err != nil {
+ t.Fatalf("ListenUnix failed: %v", err)
+ }
+ ln.Close()
+}
+
func TestUnixConnLocalAndRemoteNames(t *testing.T) {
for _, laddr := range []string{"", testUnixAddr()} {
\tladdr := laddr
TestUnixAutobind
がTestUnixgramAutobind
にリネームされました。TestUnixAutobindClose
という新しいテスト関数が追加されました。このテストは、オートバインドされたUnixリスナーを作成し、すぐにClose()
を呼び出すことで、ソケットファイルが正しくクリーンアップされることを検証します。
src/pkg/net/unixsock_posix.go
--- a/src/pkg/net/unixsock_posix.go
+++ b/src/pkg/net/unixsock_posix.go
@@ -271,7 +271,7 @@ func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
if err != nil {
return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: err}
}
- return &UnixListener{fd, laddr.Name}, nil
+ return &UnixListener{fd, fd.laddr.String()}, nil
}
// AcceptUnix accepts the next incoming call and returns the new
ListenUnix
関数内でUnixListener
を初期化する行が変更されました。- 以前は
laddr.Name
を使用していましたが、fd.laddr.String()
を使用するように変更されました。
コアとなるコードの解説
このコミットの核心的な変更は、src/pkg/net/unixsock_posix.go
ファイルの ListenUnix
関数にあります。
ListenUnix
関数は、Unixドメインソケットのリスナーを作成する役割を担っています。この関数内で、ソケットが作成され、指定されたアドレスにバインドされます。オートバインドの場合、laddr.Name
は空文字列ですが、ソケットが実際にバインドされると、カーネルは一時的なユニークなファイルパスを割り当てます。
変更前のコードでは、UnixListener
構造体を初期化する際に、リスナーのローカルアドレスを保持する laddr
フィールドに、引数として渡された laddr.Name
の値(オートバインドの場合は空文字列)をそのまま格納していました。
// 変更前
return &UnixListener{fd, laddr.Name}, nil
このため、UnixListener
の laddr
フィールドには、ソケットが実際にバインドされた一時的なパスではなく、初期の空文字列が格納されていました。
変更後のコードでは、fd.laddr.String()
を使用して UnixListener
の laddr
フィールドを初期化しています。
// 変更後
return &UnixListener{fd, fd.laddr.String()}, nil
ここで fd
は、ソケットのファイルディスクリプタをラップする内部構造体 netFD
のインスタンスです。fd.laddr
は、ソケットが実際にバインドされた後のローカルアドレス情報を保持しており、fd.laddr.String()
はそのアドレスの文字列表現(つまり、カーネルが割り当てた一時的なファイルパス)を返します。
この変更により、UnixListener
の laddr
フィールドには、オートバインドされたソケットの実際のファイルパスが正確に格納されるようになります。その結果、UnixListener.Close()
メソッドが呼び出された際に、この laddr
フィールドの値を使用して、バインドされた一時ファイルが正しく特定され、削除されるようになります。これにより、オートバインドされたUnixリスナーを閉じた際に、一時ファイルが残り続けるというバグが解消されます。
src/pkg/net/unix_test.go
に追加された TestUnixAutobindClose
テストは、この修正が正しく機能することを確認するためのものです。このテストは、オートバインドされたリスナーを作成し、すぐに Close()
を呼び出すというシンプルなシナリオで、問題が解決されたことを検証しています。
関連リンク
- Go言語の
net
パッケージドキュメント: https://pkg.go.dev/net - Go言語の
net/unix
パッケージドキュメント: https://pkg.go.dev/net/unix - Gerrit Code Review (Goプロジェクト): https://go-review.googlesource.com/
参考にした情報源リンク
- コミットのGerritリンク: https://golang.org/cl/13457058
- Unix Domain Sockets (Wikipedia): https://en.wikipedia.org/wiki/Unix_domain_socket
- Go言語の
net
パッケージのソースコード (GitHub): https://github.com/golang/go/tree/master/src/net - Go言語の
net/unixsock_posix.go
ソースコード (GitHub): https://github.com/golang/go/blob/master/src/net/unixsock_posix.go - Go言語の
net/unix_test.go
ソースコード (GitHub): https://github.com/golang/go/blob/master/src/net/unix_test.go - Go言語のIssueトラッカー (GitHub): https://github.com/golang/go/issues (ただし、#6455は直接見つからず)