[インデックス 17126] ファイルの概要
このコミットは、Go言語の標準ライブラリnet
パッケージにおける、C言語のgetaddrinfo
システムコール呼び出しの挙動を改善するものです。具体的には、getaddrinfo
にSOCK_STREAM
(TCP)タイプのアドレスのみを要求するヒントを与えることで、効率性の向上と、Solaris環境での互換性問題を解決しています。
コミット
commit ba6cf63cba611d8d4602781bd8abf5bade2af3ca
Author: Ian Lance Taylor <iant@golang.org>
Date: Fri Aug 9 09:23:27 2013 -0700
net: give C.getaddrinfo a hint that we only want SOCK_STREAM answers
This should be more efficient everywhere, and appears to be
required on Solaris.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/12583046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ba6cf63cba611d8d4602781bd8abf5bade2af3ca
元コミット内容
net: give C.getaddrinfo a hint that we only want SOCK_STREAM answers
この変更は、C.getaddrinfo
に対して、SOCK_STREAM
(TCP)タイプのアドレスのみを返すようにヒントを与えるものです。これにより、全体的な効率が向上し、特にSolaris環境で必要とされているようです。
変更の背景
Go言語のnet
パッケージは、ネットワークアドレスの解決にC言語の標準ライブラリ関数であるgetaddrinfo
を利用しています。getaddrinfo
は、ホスト名やサービス名から、ネットワークアドレス構造体(addrinfo
構造体)のリストを返す関数です。
従来のgetaddrinfo
の呼び出しでは、特定のソケットタイプ(例: TCPかUDPか)を指定せずにアドレス情報を要求していました。この場合、getaddrinfo
は通常、指定されたホスト名に対して利用可能なすべてのアドレス情報(例えば、TCP用とUDP用の両方)を返します。Goのnet
パッケージでは、TCP接続を確立する際にgetaddrinfo
を使用しますが、UDP用のアドレス情報も取得してしまうと、不要な情報処理が発生し、効率が低下する可能性がありました。
さらに重要な問題として、Solarisのような一部のUNIX系OSでは、getaddrinfo
がデフォルトでTCPとUDPの両方のアドレスを返す際に、特定のソケットタイプを明示的に指定しないと、期待通りの動作をしない、あるいは問題を引き起こすケースがあったと考えられます。コミットメッセージにある「appears to be required on Solaris」という記述は、Solaris環境での安定性や正確な動作のために、この変更が必須であったことを示唆しています。
この変更の目的は、以下の2点に集約されます。
- 効率性の向上: 不要なアドレスタイプ(UDP)の情報を取得しないことで、
getaddrinfo
の呼び出しから返される結果セットが小さくなり、その後の処理(フィルタリングなど)のオーバーヘッドが削減されます。 - 互換性と安定性の確保: Solarisなどの特定のプラットフォームで、
getaddrinfo
の挙動が期待通りになるように修正し、潜在的な問題を回避します。
前提知識の解説
1. getaddrinfo
システムコール
getaddrinfo
は、POSIX標準で定義されているネットワークプログラミングのAPIで、ホスト名やサービス名(ポート番号)から、ネットワークアドレス情報を取得するために使用されます。これは、IPv4とIPv6の両方に対応し、名前解決の複雑さを抽象化します。
getaddrinfo
の主な引数と構造体は以下の通りです。
node
: ホスト名(例: "www.example.com")またはIPアドレス文字列。service
: サービス名(例: "http", "ftp")またはポート番号文字列。hints
:addrinfo
構造体へのポインタで、検索のヒント(条件)を指定します。res
:addrinfo
構造体のリンクリストへのポインタで、結果が格納されます。
hints
構造体には、以下のようなメンバーがあります。
ai_family
: アドレスファミリー(例:AF_INET
(IPv4),AF_INET6
(IPv6),AF_UNSPEC
(指定なし))。ai_socktype
: ソケットタイプ(例:SOCK_STREAM
(TCP),SOCK_DGRAM
(UDP),SOCK_RAW
)。ai_protocol
: プロトコル(例:IPPROTO_TCP
,IPPROTO_UDP
)。ai_flags
: 挙動を制御するフラグ(例:AI_PASSIVE
,AI_CANONNAME
)。
2. SOCK_STREAM
とSOCK_DGRAM
ソケットタイプは、ネットワーク通信の特性を定義します。
SOCK_STREAM
: ストリームソケットを表し、通常はTCPプロトコルと関連付けられます。信頼性のある、コネクション指向のバイトストリーム通信を提供します。データの順序が保証され、再送処理が行われます。SOCK_DGRAM
: データグラムソケットを表し、通常はUDPプロトコルと関連付けられます。コネクションレスで、信頼性のないデータグラム通信を提供します。データの順序や到達は保証されません。
Goのnet
パッケージがTCP接続を確立する際には、SOCK_STREAM
タイプのアドレス情報のみが必要となります。
3. Cgo
Cgoは、GoプログラムからC言語のコードを呼び出すためのGoの機能です。Goのnet
パッケージは、OSのネイティブな名前解決機能(getaddrinfo
など)を利用するためにCgoを使用しています。C.
プレフィックスは、Cgoを通じてインポートされたC言語の型や関数を示します。
技術的詳細
このコミットの技術的変更は、src/pkg/net/cgo_unix.go
ファイル内のcgoLookupIPCNAME
関数に集中しています。この関数は、Goのnet
パッケージがCgoを介してgetaddrinfo
を呼び出す際のラッパーとして機能します。
変更前は、hints
構造体のai_socktype
フィールドが明示的に設定されていませんでした。これは、getaddrinfo
がデフォルトで利用可能なすべてのソケットタイプ(TCPとUDPの両方)のアドレス情報を返すことを意味します。
変更後、以下の行が追加されました。
hints.ai_socktype = C.SOCK_STREAM
この行は、getaddrinfo
に渡すhints
構造体のai_socktype
フィールドにC.SOCK_STREAM
を設定しています。これにより、getaddrinfo
はTCP接続に特化したアドレス情報のみを検索し、結果として返します。
この変更の具体的な影響は以下の通りです。
getaddrinfo
の動作変更:getaddrinfo
は、SOCK_STREAM
タイプのアドレスのみをフィルタリングして返すようになります。これにより、返されるaddrinfo
リンクリストのサイズが小さくなる可能性があります。- ループ処理の最適化: 変更前のコードでは、
getaddrinfo
から返された結果をイテレートする際に、r.ai_socktype != C.SOCK_STREAM
という条件で不要なUDPアドレスをスキップしていました。
このコメントは、// Everything comes back twice, once for UDP and once for TCP. if r.ai_socktype != C.SOCK_STREAM { continue }
getaddrinfo
がTCPとUDPの両方を返すことを前提としていました。 変更後、hints.ai_socktype = C.SOCK_STREAM
が設定されたため、getaddrinfo
は最初からSOCK_STREAM
のアドレスのみを返すことが期待されます。そのため、コメントも以下のように変更されました。
新しいコメントは、「// We only asked for SOCK_STREAM, but check anyhow. if r.ai_socktype != C.SOCK_STREAM { continue }
SOCK_STREAM
のみを要求したが、念のためチェックする」という意図を示しています。これは、getaddrinfo
の実装によっては、ヒントが厳密に適用されない場合や、予期せぬ結果が返される可能性を考慮した防御的なプログラミングスタイルです。しかし、理想的にはこのif
文のcontinue
パスはほとんど実行されなくなるはずです。
この変更により、getaddrinfo
の呼び出し自体がより効率的になり、Goのnet
パッケージが不要なアドレス情報を処理する手間が省かれるため、全体的なパフォーマンスが向上します。また、Solarisのような特定の環境での互換性問題も解決されます。
コアとなるコードの変更箇所
変更はsrc/pkg/net/cgo_unix.go
ファイルにあります。
--- a/src/pkg/net/cgo_unix.go
+++ b/src/pkg/net/cgo_unix.go
@@ -83,6 +83,7 @@ func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err error, complet
var hints C.struct_addrinfo
thints.ai_flags = cgoAddrInfoFlags()
+ thints.ai_socktype = C.SOCK_STREAM
th := C.CString(name)
defer C.free(unsafe.Pointer(h))
@@ -109,7 +110,7 @@ func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err error, complet
}
}
for r := res; r != nil; r = r.ai_next {
- // Everything comes back twice, once for UDP and once for TCP.
+ // We only asked for SOCK_STREAM, but check anyhow.
if r.ai_socktype != C.SOCK_STREAM {
continue
}
コアとなるコードの解説
-
hints.ai_socktype = C.SOCK_STREAM
:hints
はC.struct_addrinfo
型の変数で、getaddrinfo
関数に渡す検索条件(ヒント)を格納します。この行は、hints
構造体のai_socktype
フィールドにC.SOCK_STREAM
という値を設定しています。C.SOCK_STREAM
はC言語の定数で、ストリームソケット(通常はTCP)を意味します。これにより、getaddrinfo
はTCP接続に適したアドレス情報のみを返すように動作します。 -
コメントの変更: 変更前のコメント「
// Everything comes back twice, once for UDP and once for TCP.
」は、getaddrinfo
がTCPとUDPの両方のアドレスを返すという当時の認識を示していました。 変更後のコメント「// We only asked for SOCK_STREAM, but check anyhow.
」は、ai_socktype
を設定したことでSOCK_STREAM
のみを要求しているにもかかわらず、念のためai_socktype
のチェックを継続していることを示しています。これは、システムコールが常に厳密にヒントに従うとは限らない、あるいは将来的な互換性を考慮した堅牢なコード設計の一環です。
この小さな変更により、Goのネットワークスタックがアドレス解決を行う際の効率と正確性が向上し、特に特定のOS環境での安定性が確保されました。
関連リンク
- Go issue: net: getaddrinfo should hint SOCK_STREAM (このコミットに関連する可能性のあるGoのIssue)
- Go Code Review: https://golang.org/cl/12583046 (コミットメッセージに記載されているGoのコードレビューリンク)
参考にした情報源リンク
getaddrinfo
man page: https://man7.org/linux/man-pages/man3/getaddrinfo.3.html- Socket types: https://man7.org/linux/man-pages/man2/socket.2.html
- Go
net
package documentation: https://pkg.go.dev/net