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

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

このコミットは、Go言語の標準ライブラリであるnetパッケージにおけるsockaddrインターフェースの定義を、インターネットプロトコルファミリーだけでなく、Unixネットワークファミリーのアドレスも扱えるように拡張するものです。具体的には、sockaddrインターフェースの定義がsrc/pkg/net/ipsock_posix.goからsrc/pkg/net/sock_posix.goへ移動され、新たにtoAddr()メソッドが追加されました。この変更は、BSD系のOSにおけるランタイム統合型ネットワークポーラーの準備として行われました。

コミット

commit e257cd8aae199d24c3634e76116c7280417c9dce
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Sun Jul 28 12:52:30 2013 +0900

    net: extend sockaddr interface to the all address families
    
    This CL extends existing sockaddr interface to accommodate not only
    internet protocol family endpoint addressess but unix network family
    endpoint addresses.
    
    This is in preparation for runtime-integrated network pollster for BSD
    variants.
    
    Update #5199
    
    R=golang-dev, dave
    CC=golang-dev
    https://golang.org/cl/11979043

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

https://github.com/golang/go/commit/e257cd8aae199d24c3634e76116c7280417c9dce

元コミット内容

このコミットの目的は、既存のsockaddrインターフェースを拡張し、インターネットプロトコルファミリーのエンドポイントアドレスだけでなく、Unixネットワークファミリーのエンドポイントアドレスも扱えるようにすることです。これは、BSD系のOS向けにランタイムに統合されたネットワークポーラーを準備するための一環として行われました。

変更の背景

Go言語のnetパッケージは、ネットワーク通信を抽象化し、様々なプロトコルやアドレスファミリーに対応しています。しかし、このコミット以前のsockaddrインターフェースは、主にTCP/UDP/IPといったインターネットプロトコルファミリーのアドレスを念頭に設計されていました。

この変更の主な背景には、以下の2点があります。

  1. Unixドメインソケットのサポート強化: Unixドメインソケットは、同じホスト上のプロセス間通信(IPC)に用いられる効率的な通信メカニズムです。ファイルシステムパスをアドレスとして使用するため、IPアドレスとは異なるアドレス表現を必要とします。GoのnetパッケージがUnixドメインソケットをよりシームレスに扱うためには、sockaddrインターフェースがこれを適切に表現できる必要がありました。
  2. BSD向けネットワークポーラーの統合: Goランタイムは、効率的な非同期I/Oを実現するために、OSが提供するネットワークポーラー(Linuxのepoll、macOS/BSDのkqueueなど)を利用しています。BSD系のOS(FreeBSD, OpenBSD, NetBSDなど)では、kqueueが主要なポーリングメカニズムです。このコミットは、BSD環境でUnixドメインソケットを含むあらゆる種類のアドレスを、ランタイムが直接管理するポーラーと連携させるための基盤を整えることを目的としています。これにより、GoアプリケーションがBSD環境でより高性能なネットワークI/Oを実現できるようになります。

前提知識の解説

ソケットアドレス (sockaddr)

ソケットアドレスは、ネットワーク通信のエンドポイント(通信の起点または終点)を識別するためのデータ構造です。C言語の標準ライブラリでは、struct sockaddrという汎用的な構造体が定義されており、これに加えて特定のプロトコルファミリー(例: IPv4, IPv6, Unixドメイン)に応じた具体的な構造体(例: struct sockaddr_in, struct sockaddr_in6, struct sockaddr_un)が存在します。これらの具体的な構造体は、struct sockaddrにキャストして使用されます。

  • struct sockaddr_in / struct sockaddr_in6: インターネットプロトコル(IPv4/IPv6)のアドレスを表現します。IPアドレスとポート番号を含みます。
  • struct sockaddr_un: Unixドメインソケットのアドレスを表現します。ファイルシステム上のパス名を含みます。

Go言語のnetパッケージとsyscall.Sockaddr

Go言語のnetパッケージは、ネットワークI/O操作のための高レベルな抽象化を提供します。しかし、その内部ではOSのシステムコールを呼び出して実際のネットワーク操作を行います。Goのsyscallパッケージは、これらのOSシステムコールをGoから呼び出すためのインターフェースを提供しており、C言語のstruct sockaddrに対応するGoの型としてsyscall.Sockaddrインターフェースが定義されています。

syscall.Sockaddrインターフェースは、OS固有のソケットアドレス構造体を抽象化するためのものです。例えば、syscall.SockaddrInet4syscall.SockaddrInet6syscall.SockaddrUnixといった具体的な型がこのインターフェースを実装し、それぞれIPv4、IPv6、Unixドメインソケットのアドレスを表現します。

インターネットソケットとUnixドメインソケット

  • インターネットソケット (Internet Sockets): TCP/IPプロトコルスイートに基づくネットワーク通信に使用されます。IPアドレスとポート番号によって通信相手を特定し、異なるホスト間での通信が可能です。
  • Unixドメインソケット (Unix Domain Sockets / UDS): 同じホスト上のプロセス間通信(IPC)に特化したソケットです。ネットワークスタックを介さず、ファイルシステム上のパス名をアドレスとして使用するため、TCP/IP通信よりもオーバーヘッドが少なく、高速な通信が可能です。

ネットワークポーラー (Network Pollster)

ネットワークポーラーは、Goランタイムが多数のネットワーク接続を効率的に管理し、非同期I/Oを実現するための重要なコンポーネントです。OSが提供するI/O多重化メカニズム(例: Linuxのepoll、macOS/BSDのkqueue、WindowsのIOCP)を利用して、複数のソケットからのイベント(データの読み書き準備完了など)を監視し、準備ができたソケットに対してのみ処理をディスパッチします。これにより、GoのgoroutineがブロッキングI/Oで待機することなく、効率的に多数の同時接続を処理できるようになります。

特にBSD系のOSでは、kqueueが主要なポーリングメカニズムとして利用されます。Goランタイムは、このkqueueを内部的に利用して、ネットワークI/Oのスケジューリングを行っています。

技術的詳細

このコミットの技術的な核心は、Goのnetパッケージにおけるsockaddrインターフェースの汎用化と、それに伴うtoAddr()メソッドの追加です。

  1. sockaddrインターフェースの移動と汎用化:

    • 変更前は、sockaddrインターフェースの定義がsrc/pkg/net/ipsock_posix.goにありました。このファイル名が示すように、このインターフェースは主にIPソケット(インターネットプロトコル)の文脈で使われることを想定していました。
    • 変更後は、sockaddrインターフェースの定義がsrc/pkg/net/sock_posix.goに移動されました。sock_posix.goは、より一般的なPOSIXソケット操作を扱うファイルであり、この移動はsockaddrインターフェースがIPソケットに限定されず、Unixドメインソケットのような他のアドレスファミリーにも適用されるべきだという設計思想の変更を示しています。
    • このインターフェースは、Addrインターフェース(Goのnetパッケージにおける汎用的なネットワークアドレスを表すインターフェース)を埋め込み、さらにfamily() intisWildcard() boolsockaddr(family int) (syscall.Sockaddr, error)というメソッドを持っていました。これらは、アドレスのプロトコルファミリーの取得、ワイルドカードアドレスかどうかの判定、そしてGoのsyscall.Sockaddr型への変換を行うためのものです。
  2. toAddr()メソッドの追加:

    • 新しいsockaddrインターフェースには、toAddr() sockaddrというメソッドが追加されました。
    • このメソッドの目的は、syscall.SockaddrのようなOSネイティブのソケットアドレス表現から、Goのnetパッケージが提供する具体的なAddr型(例: *TCPAddr, *UDPAddr, *UnixAddr)を実装するsockaddr型への変換を可能にすることです。
    • Goのネットワークポーラーは、OSからソケットイベントを受け取った際に、そのイベントに関連するソケットアドレス情報をsyscall.Sockaddrの形式で受け取ることがあります。このtoAddr()メソッドを通じて、ランタイムは受け取ったsyscall.Sockaddrを、Goのnetパッケージ内で扱えるより具体的なsockaddrインターフェース型に変換し、最終的にユーザーが利用するnet.Addr型として提供できるようになります。
    • これにより、Unixドメインソケットのアドレス(syscall.SockaddrUnix)も、Goのnetパッケージ内で適切に処理され、net.UnixAddrのような型として表現できるようになります。

この変更により、Goのnetパッケージは、異なるアドレスファミリー(特にUnixドメインソケット)をより統一的かつ柔軟に扱えるようになり、BSD環境でのネットワークポーラーとの連携がスムーズに行える基盤が構築されました。

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

このコミットでは、主に以下の2つのファイルが変更されました。

  1. src/pkg/net/ipsock_posix.go:

    • このファイルから、sockaddrインターフェースの定義が削除されました。
    --- a/src/pkg/net/ipsock_posix.go
    +++ b/src/pkg/net/ipsock_posix.go
    @@ -120,15 +120,6 @@ func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) (family
     
     // Internet sockets (TCP, UDP, IP)
     
    -// A sockaddr represents a TCP, UDP or IP network address that can
    -// be converted into a syscall.Sockaddr.
    -type sockaddr interface {
    -	Addr
    -	family() int
    -	isWildcard() bool
    -	sockaddr(family int) (syscall.Sockaddr, error)
    -}
    -
     func internetSocket(net string, laddr, raddr sockaddr, deadline time.Time, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
     	var la, ra syscall.Sockaddr
     	family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode)
    
  2. src/pkg/net/sock_posix.go:

    • このファイルに、sockaddrインターフェースの定義が追加されました。この新しい定義には、既存のメソッドに加えてtoAddr() sockaddrメソッドが含まれています。
    --- a/src/pkg/net/sock_posix.go
    +++ b/src/pkg/net/sock_posix.go
    @@ -11,6 +11,16 @@ import (
      	"time"
      )
      
    +// A sockaddr represents a TCP, UDP, IP network endpoint address that
    +// can be converted into a syscall.Sockaddr.
    +type sockaddr interface {
    +	Addr
    +	family() int
    +	isWildcard() bool
    +	sockaddr(family int) (syscall.Sockaddr, error)
    +	toAddr() sockaddr
    +}
    +
      // Generic POSIX socket creation.
      func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr, deadline time.Time, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
      	s, err := sysSocket(f, t, p)
    

コアとなるコードの解説

この変更の核心は、sockaddrインターフェースの定義をより汎用的なsock_posix.goに移動し、toAddr()メソッドを追加した点にあります。

  • インターフェース定義の移動:

    • ipsock_posix.goは、IPソケット(TCP, UDP, IP)に特化した処理を扱うファイルでした。ここにsockaddrインターフェースが定義されていると、そのインターフェースがIPアドレスに限定されるかのような印象を与え、他のアドレスファミリー(特にUnixドメインソケット)を扱う際に不自然さや制約が生じる可能性がありました。
    • sock_posix.goは、POSIXシステムにおけるソケットの一般的な操作を扱うファイルであり、より低レベルで汎用的なソケット関連の定義を置くのに適しています。ここにsockaddrインターフェースを移動することで、このインターフェースがIPソケットだけでなく、Unixドメインソケットなど、あらゆる種類のアドレスファミリーに対応する汎用的な概念であることを明確にしました。これは、Goのnetパッケージが提供するネットワーク抽象化の範囲を広げるための重要なステップです。
  • toAddr()メソッドの追加:

    • このメソッドは、sockaddrインターフェースを実装する型が、自身を別のsockaddrインターフェース型として返すことを可能にします。
    • 具体的なユースケースとしては、OSのシステムコールから返されるsyscall.Sockaddr型の情報(これはOSネイティブのソケットアドレス構造体をGoでラップしたもの)を、Goのnetパッケージ内でより扱いやすい、具体的なnet.Addrインターフェースを実装する型(例: *net.TCPAddr, *net.UnixAddr)に変換する際に利用されます。
    • 例えば、ネットワークポーラーがソケットイベントを処理する際、OSから受け取ったsyscall.Sockaddrを、このtoAddr()メソッドを通じてGoのnetパッケージが認識できるsockaddrインターフェース型に変換し、その後の処理(例えば、ReadFromWriteToのようなメソッドで返されるリモートアドレスの特定)に利用できるようになります。
    • これにより、Unixドメインソケットのアドレスも、Goのランタイムとnetパッケージの内部で一貫した方法で処理され、ユーザーレベルのコードからは透過的に扱えるようになります。

この変更は、Goのネットワークスタックの内部的な柔軟性と拡張性を高め、将来的な機能追加(特にUnixドメインソケットのより深い統合や、異なるOS環境でのポーラーの最適化)のための強固な基盤を築きました。

関連リンク

参考にした情報源リンク