Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 11280] ファイルの概要

コミット

commit 7f4936a1c5d828b39efea48787bb266f4666d95c
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Fri Jan 20 08:33:37 2012 +0900

    net: fix windows build

    R=golang-dev, alex.brainman
    CC=golang-dev
    https://golang.org/cl/5532102
---
 src/pkg/net/fd_windows.go | 10 +++++-----\n 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/pkg/net/fd_windows.go b/src/pkg/net/fd_windows.go
index 637510b732..6e37b4eb6f 100644
--- a/src/pkg/net/fd_windows.go
+++ b/src/pkg/net/fd_windows.go
@@ -230,7 +230,7 @@ type netFD struct {
 	// immutable until Close
 	sysfd   syscall.Handle
 	family  int
-	proto   int
+	sotype  int
 	net     string
 	laddr   Addr
 	raddr   Addr
@@ -244,11 +244,11 @@ type netFD struct {
 	wio       sync.Mutex
 }

-func allocFD(fd syscall.Handle, family, proto int, net string) (f *netFD) {
+func allocFD(fd syscall.Handle, family, sotype int, net string) (f *netFD) {
 	f = &netFD{
 		sysfd:  fd,
 		family: family,
-		proto:  proto,
+		sotype: sotype,
 		net:    net,
 	}
 	runtime.SetFinalizer(f, (*netFD).Close)
@@ -506,7 +506,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err
 	// Get new socket.
 	// See ../syscall/exec.go for description of ForkLock.
 	syscall.ForkLock.RLock()
-	s, e := syscall.Socket(fd.family, fd.proto, 0)
+	s, e := syscall.Socket(fd.family, fd.sotype, 0)
 	if e != nil {
 		syscall.ForkLock.RUnlock()
 		return nil, e
@@ -546,7 +546,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err
 	lsa, _ := lrsa.Sockaddr()
 	rsa, _ := rrsa.Sockaddr()

-	nfd = allocFD(s, fd.family, fd.proto, fd.net)
+	nfd = allocFD(s, fd.family, fd.sotype, fd.net)
 	nfd.setAddr(toAddr(lsa), toAddr(rsa))
 	return nfd, nil
 }

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/7f4936a1c5d828b39efea48787bb266f4666d95c

元コミット内容

このコミットは、Go言語のネットワークパッケージ (net) におけるWindowsビルドの修正を目的としています。具体的には、netFD 構造体のフィールド名と、それに関連する allocFD 関数および accept メソッド内の syscall.Socket の引数を proto から sotype へと変更しています。

変更の背景

Go言語の syscall.Socket 関数は、オペレーティングシステムの低レベルなソケットAPIを呼び出すために使用されます。この関数は通常、アドレスファミリー (AF_INETAF_INET6 など)、ソケットタイプ (SOCK_STREAMSOCK_DGRAM など)、およびプロトコル (IPPROTO_TCPIPPROTO_UDP など) の3つの主要な引数を取ります。

元のコードでは、netFD 構造体内でソケットタイプを表すために proto というフィールド名が使われており、syscall.Socket 関数に渡す際にもこの proto フィールドがソケットタイプとして使用されていました。しかし、これは命名規則と実際の引数の意味合いにおいて混乱を招く可能性がありました。

Windows環境では、syscall.Socket の第2引数はソケットタイプ (sotype) を表し、第3引数はプロトコル (protocol) を表します。このコミットは、このWindows固有の syscall.Socket の引数と、Goの net パッケージ内の内部表現との整合性を取ることを目的としています。proto という名前がプロトコルを連想させるため、ソケットタイプを意味する sotype に変更することで、コードの可読性と正確性を向上させています。これにより、Windows上でのビルドや実行時の潜在的な問題を修正し、より堅牢なネットワーク処理を実現します。

前提知識の解説

ソケットプログラミングの基本

ソケットは、ネットワーク通信のエンドポイントを抽象化したものです。アプリケーションはソケットを通じてデータを送受信します。ソケットを作成する際には、以下の3つの主要なパラメータを指定します。

  1. アドレスファミリー (Address Family / Domain): どのネットワークプロトコルファミリーを使用するかを指定します。

    • AF_INET (または PF_INET): IPv4インターネットプロトコル用。
    • AF_INET6 (または PF_INET6): IPv6インターネットプロトコル用。
    • AF_UNIX (または PF_UNIX): 同一システム内のプロセス間通信用(Unixドメインソケット)。
  2. ソケットタイプ (Socket Type): ソケットの通信特性を指定します。

    • SOCK_STREAM: 信頼性のある、接続指向のバイトストリームを提供します。通常、TCP (Transmission Control Protocol) で使用されます。データの順序が保証され、欠損がありません。
    • SOCK_DGRAM: 信頼性のない、コネクションレスなデータグラムを提供します。通常、UDP (User Datagram Protocol) で使用されます。データの順序や到達は保証されません。
    • SOCK_RAW: 生のネットワークプロトコルに直接アクセスします。
  3. プロトコル (Protocol): ソケットタイプ内で使用する特定のプロトコルを指定します。通常、ソケットタイプによってプロトコルは暗黙的に決定されるため、0 を指定することが多いですが、明示的に指定することも可能です。

    • IPPROTO_TCP: TCPプロトコル。
    • IPPROTO_UDP: UDPプロトコル。
    • IPPROTO_IP: IPプロトコル(SOCK_RAW と組み合わせて使用されることが多い)。

Go言語の syscall パッケージ

Go言語の syscall パッケージは、オペレーティングシステムが提供する低レベルなシステムコールに直接アクセスするための機能を提供します。これにより、GoプログラムからOS固有の機能(ファイル操作、プロセス管理、ネットワークソケットなど)を直接呼び出すことができます。

特にネットワーク関連では、syscall.Socket 関数がソケットの作成に使用されます。この関数はOSの socket() システムコールをラップしており、OSごとに異なる引数の解釈や挙動を吸収する役割も担っています。

protosotype の違い

  • proto (Protocol): ネットワークプロトコル層における特定のプロトコルを指します。例えば、TCPやUDPなどです。syscall.Socket の第3引数に相当します。
  • sotype (Socket Type): ソケットの基本的な動作モードを指します。例えば、ストリームソケット(TCP)やデータグラムソケット(UDP)などです。syscall.Socket の第2引数に相当します。

このコミットの背景にある問題は、Goの内部構造体でソケットタイプを proto という名前で保持していたため、syscall.Socket に渡す際に、本来ソケットタイプを期待される第2引数に proto の値が渡され、それがWindowsのAPIの期待と一致しない場合に問題が発生する可能性があったということです。

技術的詳細

このコミットは、Go言語の src/pkg/net/fd_windows.go ファイルに焦点を当てています。このファイルは、Windows環境におけるファイルディスクリプタ(Goの netFD 構造体で抽象化される)の管理と、ネットワーク操作の低レベルな実装を扱っています。

問題の核心は、netFD 構造体の定義と、ソケットを作成する allocFD 関数、および接続を受け入れる accept メソッドにおける syscall.Socket の呼び出しにありました。

  1. netFD 構造体の変更:

    • 元のコードでは、netFD 構造体内に proto int というフィールドがありました。このフィールドは、ソケットの「タイプ」(SOCK_STREAMSOCK_DGRAM など)を格納するために使用されていました。
    • 変更後、このフィールドは sotype int にリネームされました。これにより、フィールド名がその役割(ソケットタイプ)をより正確に反映するようになりました。
  2. allocFD 関数の変更:

    • allocFD 関数は、新しい netFD オブジェクトを割り当て、初期化する役割を担っています。この関数は、syscall.Handle 型のファイルディスクリプタ、アドレスファミリー (family)、ソケットタイプ (proto または sotype)、およびネットワークタイプ (net) を引数として受け取ります。
    • 変更前は func allocFD(fd syscall.Handle, family, proto int, net string) でしたが、変更後は func allocFD(fd syscall.Handle, family, sotype int, net string) となり、引数名も proto から sotype に変更されました。
    • 関数内部でも、f.proto = protof.sotype = sotype に修正され、構造体フィールドへの代入も整合性が取られました。
  3. accept メソッド内の syscall.Socket 呼び出しの変更:

    • accept メソッドは、新しい接続を受け入れる際に、新しいソケットを作成するために syscall.Socket を呼び出します。
    • 元のコードでは、syscall.Socket(fd.family, fd.proto, 0) となっていました。ここで fd.protonetFD 構造体の proto フィールド(ソケットタイプを格納)を参照していました。
    • 変更後、この呼び出しは syscall.Socket(fd.family, fd.sotype, 0) となりました。これにより、syscall.Socket の第2引数に、netFD 構造体の sotype フィールド(ソケットタイプを格納)が正しく渡されるようになりました。0 はプロトコル引数であり、この場合はソケットタイプから暗黙的に決定されるため、明示的に指定する必要がないことを示しています。

この変更は、Windowsの socket APIの期待する引数の順序と意味論にGoの内部コードを合わせることで、Windows環境でのネットワークソケットの作成と管理における潜在的なバグを修正しています。特に、syscall.Socket の第2引数がソケットタイプ、第3引数がプロトコルであるというWindowsの慣習に準拠することで、クロスプラットフォームな互換性を高めています。

コアとなるコードの変更箇所

src/pkg/net/fd_windows.go ファイルにおいて、以下の変更が行われました。

  1. netFD 構造体定義の変更:

    --- a/src/pkg/net/fd_windows.go
    +++ b/src/pkg/net/fd_windows.go
    @@ -230,7 +230,7 @@ type netFD struct {
     	// immutable until Close
     	sysfd   syscall.Handle
     	family  int
    -	proto   int
    +	sotype  int
     	net     string
     	laddr   Addr
     	raddr   Addr
    
  2. allocFD 関数のシグネチャと実装の変更:

    --- a/src/pkg/net/fd_windows.go
    +++ b/src/pkg/net/fd_windows.go
    @@ -244,11 +244,11 @@ type netFD struct {
     	wio       sync.Mutex
     }
    
    -func allocFD(fd syscall.Handle, family, proto int, net string) (f *netFD) {
    +func allocFD(fd syscall.Handle, family, sotype int, net string) (f *netFD) {
     	f = &netFD{
     		sysfd:  fd,
     		family: family,
    -		proto:  proto,
    +		sotype: sotype,
     		net:    net,
     	}
     	runtime.SetFinalizer(f, (*netFD).Close)
    
  3. accept メソッド内の syscall.Socket 呼び出しの変更:

    --- a/src/pkg/net/fd_windows.go
    +++ b/src/pkg/net/fd_windows.go
    @@ -506,7 +506,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err
     	// Get new socket.
     	// See ../syscall/exec.go for description of ForkLock.
     	syscall.ForkLock.RLock()
    -	s, e := syscall.Socket(fd.family, fd.proto, 0)
    +	s, e := syscall.Socket(fd.family, fd.sotype, 0)
     	if e != nil {
     		syscall.ForkLock.RUnlock()
     		return nil, e
    @@ -546,7 +546,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err
     	lsa, _ := lrsa.Sockaddr()
     	rsa, _ := rrsa.Sockaddr()
    
    -	nfd = allocFD(s, fd.family, fd.proto, fd.net)
    +	nfd = allocFD(s, fd.family, fd.sotype, fd.net)
     	nfd.setAddr(toAddr(lsa), toAddr(rsa))
     	return nfd, nil
     }
    

コアとなるコードの解説

このコミットの核心は、netFD 構造体内の proto フィールドが、実際にはソケットタイプ(SOCK_STREAMSOCK_DGRAM など)を格納していたにもかかわらず、その名前がプロトコル(IPPROTO_TCPIPPROTO_UDP など)を連想させるという命名の不整合を解消することにあります。

Windowsの socket APIでは、第2引数がソケットタイプ、第3引数がプロトコルです。Goの syscall.Socket 関数もこの慣習に従っています。

  • netFD 構造体の proto から sotype へのリネーム: これは単なる名前の変更以上の意味を持ちます。コードの意図を明確にし、ソケットの「タイプ」を保持するフィールドであることを明示することで、将来的な誤解やバグの発生を防ぎます。

  • allocFD 関数の引数と内部処理の変更: allocFD 関数は、新しいソケットディスクリプタをラップする netFD オブジェクトを生成します。この関数に渡される引数名も proto から sotype に変更され、内部で f.sotype = sotype と代入されることで、netFD 構造体の新しいフィールド名と整合性が取られます。これにより、ソケットタイプが正しく netFD オブジェクトに格納されることが保証されます。

  • accept メソッド内の syscall.Socket 呼び出しの修正: accept メソッドは、新しい接続を受け入れる際に、内部で syscall.Socket を呼び出して新しいソケットを作成します。この呼び出しにおいて、syscall.Socket(fd.family, fd.proto, 0)syscall.Socket(fd.family, fd.sotype, 0) に変更されました。 この修正は非常に重要です。fd.sotypenetFD 構造体から取得されるソケットタイプであり、これが syscall.Socket の第2引数に渡されることで、Windows APIが期待する正しいソケットタイプが指定されるようになります。第3引数の 0 は、プロトコルがソケットタイプから暗黙的に決定されることを意味します。

この一連の変更により、Goの net パッケージはWindows環境において、より正確かつ堅牢にソケットを操作できるようになりました。これは、Goがクロスプラットフォームな言語として、各OSのシステムコールAPIの微妙な違いを適切に吸収し、一貫したインターフェースを提供する上で重要な改善点です。

関連リンク

  • Go言語の syscall パッケージに関する公式ドキュメント (当時のバージョンに基づく): https://pkg.go.dev/syscall (現在のドキュメントですが、当時のAPIの概念を理解するのに役立ちます)
  • Go言語の net パッケージに関する公式ドキュメント: https://pkg.go.dev/net

参考にした情報源リンク