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

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

このコミットは、Go言語のsyscallパッケージにおけるOpenBSD向けのgetsocknameシステムコールに関するワークアラウンド(回避策)を削除するものです。具体的には、UnixドメインソケットにおいてgetsocknameAF_UNSPEC(未指定)ファミリーを返す問題に対する修正が、OpenBSD 5.2で導入されたため、Goの最小要件がOpenBSD 5.4-currentに引き上げられたことに伴い、不要になったコードを削除しています。

コミット

commit 3233a12f614723317f5d2e127b87b69aa236cd58
Author: Joel Sing <jsing@google.com>
Date:   Mon Jan 13 11:24:56 2014 +1100

    syscall: remove getsockname workaround for openbsd
    
    Remove the getsockname workaround for unix domain sockets on OpenBSD.
    This was fixed in OpenBSD 5.2 and we now have a minimum requirement
    for OpenBSD 5.4-current.
    
    R=golang-codereviews, minux.ma
    CC=golang-codereviews
    https://golang.org/cl/50960043

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

https://github.com/golang/go/commit/3233a12f614723317f5d2e127b87b69aa236cd58

元コミット内容

syscall: remove getsockname workaround for openbsd

このコミットは、OpenBSDにおけるgetsocknameシステムコールに対するワークアラウンドを削除します。 OpenBSD 5.2でこの問題が修正され、GoのOpenBSDに対する最小要件がOpenBSD 5.4-currentになったため、このワークアラウンドは不要になりました。

変更の背景

Go言語のsyscallパッケージは、オペレーティングシステムのシステムコールをGoプログラムから呼び出すためのインターフェースを提供します。getsocknameは、指定されたソケットのローカルアドレスを取得するためのシステムコールです。

過去のOpenBSDのバージョン(5.2より前)では、Unixドメインソケットに対してgetsocknameを呼び出した際に、ソケットアドレス構造体(sockaddr)のsa_familyフィールドがAF_UNSPEC(アドレスファミリーが未指定)として返され、sa_lenフィールドが0になるという問題がありました。これは、Unixドメインソケットが実際にはAF_UNIXファミリーに属しているにもかかわらず、正しく識別されないというバグでした。

Goのランタイムは、このOpenBSDのバグに対応するため、syscall_bsd.goファイル内で特定のワークアラウンドを実装していました。このワークアラウンドは、getsocknameAF_UNSPECと0の長さのアドレスを返した場合に、それがUnixドメインソケットであると仮定して、AF_UNIXファミリーと適切な長さに手動で修正するというものでした。

しかし、OpenBSD 5.2でこのgetsocknameのバグが修正され、Unixドメインソケットに対して正しいAF_UNIXファミリーが返されるようになりました。GoのOpenBSDサポートの最小バージョンがOpenBSD 5.4-currentに引き上げられたため、この古いバグに対するワークアラウンドはもはや必要なくなりました。このコミットは、その不要になったコードを削除し、コードベースをクリーンアップすることを目的としています。

また、コミットメッセージにはDragonFly BSDに関するコメントも含まれており、同様の「バグ」がDragonFly BSDにも存在し、上流に報告されるべきであると指摘されています。これは、異なるBSD系OS間でのシステムコール実装の差異と、それに対するGoランタイムの対応の複雑さを示唆しています。

前提知識の解説

1. システムコール (System Call)

システムコールは、オペレーティングシステム(OS)のカーネルが提供するサービスを、ユーザー空間のプログラムが利用するためのインターフェースです。ファイル操作、メモリ管理、プロセス制御、ネットワーク通信など、OSの基本的な機能にアクセスするために使用されます。Go言語のsyscallパッケージは、これらのシステムコールをGoの関数として抽象化し、クロスプラットフォームで利用できるようにしています。

2. ソケット (Socket)

ソケットは、ネットワーク通信のエンドポイントを抽象化したものです。プロセス間通信(IPC)やネットワーク通信において、データの送受信を行うためのインターフェースとして機能します。ソケットには、TCPソケット、UDPソケット、そしてこのコミットで関連するUnixドメインソケットなど、いくつかの種類があります。

3. Unixドメインソケット (Unix Domain Socket)

Unixドメインソケット(UDS)は、同じホストマシン上のプロセス間で通信を行うためのソケットです。TCP/IPソケットがネットワークインターフェースを介して通信するのに対し、UDSはファイルシステム上のパス名(または抽象的な名前空間)を介して通信します。ネットワークスタックを介さないため、TCP/IPソケットよりも高速で効率的なプロセス間通信が可能です。

4. getsockname システムコール

getsocknameは、ソケットディスクリプタ(fd)で指定されたソケットのローカルアドレス(つまり、ソケットがバインドされているアドレス)を取得するために使用されるシステムコールです。このシステムコールは、sockaddr構造体と呼ばれる汎用的なソケットアドレス構造体にアドレス情報を格納して返します。

5. sockaddr 構造体とアドレスファミリー

sockaddr構造体は、ソケットアドレスを表現するための汎用的な構造体です。この構造体には、アドレスファミリー(sa_family)と、そのファミリーに固有のアドレス情報が含まれます。

  • sa_family: ソケットが属するアドレスファミリーを示すフィールドです。

    • AF_UNSPEC (Address Family Unspecified): アドレスファミリーが未指定であることを示します。これは通常、ソケットがまだ特定のアドレスファミリーにバインドされていない場合や、エラー状態の場合に返されることがあります。
    • AF_UNIX (Address Family Unix): Unixドメインソケットのアドレスファミリーを示します。
    • AF_INET (Address Family Internet): IPv4ソケットのアドレスファミリーを示します。
    • AF_INET6 (Address Family Internet Version 6): IPv6ソケットのアドレスファミリーを示します。
  • sa_len: sockaddr構造体全体の長さをバイト単位で示すフィールドです。

6. OpenBSD と DragonFly BSD

OpenBSDとDragonFly BSDは、BSD(Berkeley Software Distribution)系のオープンソースUNIXライクなオペレーティングシステムです。これらはFreeBSDから派生しており、それぞれ異なる開発目標と哲学を持っています。OSのバージョンやパッチレベルによって、システムコールの挙動やバグの有無が異なることがあります。

技術的詳細

このコミットの核心は、Go言語のsyscallパッケージが、特定のOS(OpenBSD)の過去のシステムコール実装のバグをどのように扱っていたか、そしてそのバグが修正された後にどのようにコードベースを更新したかという点にあります。

Goのsyscall_bsd.goファイルには、GetsocknameというGoの関数が定義されており、これは内部的にC言語のgetsocknameシステムコールを呼び出しています。

元のコードでは、getsocknameが返したSockaddr構造体のAddr.FamilyAF_UNSPECであり、かつAddr.Lenが0である場合に、それがOpenBSDまたはDragonFly BSD上のUnixドメインソケットであると判断し、Addr.FamilyAF_UNIXに、Addr.LenSizeofSockaddrUnix(Unixドメインソケットアドレスの正しいサイズ)に手動で修正していました。これは、OpenBSDの古いバージョンにおけるgetsocknameのバグに対する直接的なワークアラウンドでした。

// 修正前のコードの関連部分(コミット差分から推測)
if (runtime.GOOS == "dragonfly" || runtime.GOOS == "openbsd") && rsa.Addr.Family == AF_UNSPEC && rsa.Addr.Len == 0 {
    rsa.Addr.Family = AF_UNIX
    rsa.Addr.Len = SizeofSockaddrUnix
}

このワークアラウンドは、OpenBSD 5.2でgetsocknameのバグが修正されたことで不要になりました。GoのOpenBSDサポートの最小バージョンがOpenBSD 5.4-currentに引き上げられたため、Goランタイムはもはやこの古いバグを考慮する必要がなくなりました。

コミットは、この条件分岐からruntime.GOOS == "openbsd"の部分を削除し、OpenBSD向けのワークアラウンドを完全に廃止しました。

// 修正後のコードの関連部分
if runtime.GOOS == "dragonfly" && rsa.Addr.Family == AF_UNSPEC && rsa.Addr.Len == 0 {
    rsa.Addr.Family = AF_UNIX
    rsa.Addr.Len = SizeofSockaddrUnix
}

これにより、コードはよりシンプルになり、OpenBSDの最新の挙動に合致するようになりました。ただし、DragonFly BSDについては同様のバグがまだ存在すると考えられているため、DragonFly BSD向けのワークアラウンドは残されています。これは、異なるOS間での互換性を維持しつつ、各OSの特性に合わせた調整を行うGoランタイムの設計思想を反映しています。

この変更は、GoがサポートするOSのバージョンアップに伴い、過去のOSのバグに対する特定の回避策が不要になった場合に、積極的にコードをクリーンアップしていくという開発プロセスの一環です。これにより、コードの複雑性が軽減され、将来的なメンテナンスが容易になります。

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

変更はsrc/pkg/syscall/syscall_bsd.goファイルにあります。

--- a/src/pkg/syscall/syscall_bsd.go
+++ b/src/pkg/syscall/syscall_bsd.go
@@ -296,10 +296,9 @@ func Getsockname(fd int) (sa Sockaddr, err error) {
 	if err = getsockname(fd, &rsa, &len); err != nil {
 		return
 	}
-	// TODO(jsing): Remove after OpenBSD 5.4 is released (see issue 3349).
-	// TODO(jsing): Apparently dragonfly has the same "bug", which should
-	// be reported upstream.
-	if (runtime.GOOS == "dragonfly" || runtime.GOOS == "openbsd") && rsa.Addr.Family == AF_UNSPEC && rsa.Addr.Len == 0 {
+	// TODO(jsing): DragonFly has a "bug" (see issue 3349), which should be
+	// reported upstream.
+	if runtime.GOOS == "dragonfly" && rsa.Addr.Family == AF_UNSPEC && rsa.Addr.Len == 0 {
 		rsa.Addr.Family = AF_UNIX
 		rsa.Addr.Len = SizeofSockaddrUnix
 	}

コアとなるコードの解説

変更されたコードブロックは、Getsockname関数内にあります。この関数は、ソケットディスクリプタfdを受け取り、そのソケットのローカルアドレスをSockaddrインターフェースとして返します。

  1. if err = getsockname(fd, &rsa, &len); err != nil { return }

    • ここで実際のシステムコールgetsocknameが呼び出され、ソケットアドレス情報がrsaRawSockaddrAny型、汎用的なsockaddr構造体)に格納されます。エラーが発生した場合は、すぐに処理を終了します。
  2. // TODO(jsing): DragonFly has a "bug" (see issue 3349), which should be // reported upstream.

    • これは、DragonFly BSDにおける同様のバグに関するコメントです。このコメントは、このワークアラウンドがDragonFly BSD向けにはまだ必要であることを示唆しています。また、このバグはGoのコードベースで修正するのではなく、OSの上流プロジェクトに報告して修正されるべきであるという方針も示しています。
  3. if runtime.GOOS == "dragonfly" && rsa.Addr.Family == AF_UNSPEC && rsa.Addr.Len == 0 { ... }

    • これが変更された条件分岐です。
    • 変更前は (runtime.GOOS == "dragonfly" || runtime.GOOS == "openbsd") でした。
    • 変更後は runtime.GOOS == "dragonfly" のみになりました。
    • この条件は、現在のGoの実行環境がDragonFly BSDであり、かつgetsocknameが返したソケットアドレスのファミリーがAF_UNSPECで、長さが0である場合に真となります。
    • この条件が真の場合、以下の2行が実行されます。
      • rsa.Addr.Family = AF_UNIX: ソケットアドレスのファミリーをAF_UNIXに強制的に設定します。これは、getsocknameが誤ってAF_UNSPECを返した場合に、それがUnixドメインソケットであることをGoランタイムが「推測」して修正するものです。
      • rsa.Addr.Len = SizeofSockaddrUnix: ソケットアドレスの長さをSizeofSockaddrUnix(Unixドメインソケットアドレスの正しいサイズ)に強制的に設定します。これも同様に、getsocknameが誤って0を返した場合の修正です。

この変更により、OpenBSD向けの不要なワークアラウンドが削除され、コードがより簡潔になりました。DragonFly BSD向けのワークアラウンドは、そのOSのバグが修正されるまで維持されます。

関連リンク

参考にした情報源リンク

  • コミットメッセージと差分
  • Go言語のsyscallパッケージのドキュメント
  • getsocknameシステムコールに関する一般的な情報(Linux man pagesなど)
  • OpenBSDおよびDragonFly BSDのリリースノートやバグトラッカー(具体的な修正内容の確認のため)
  • Go issue 3349 (直接的な情報源ではないが、関連する議論の可能性)
  • Stack Overflowや技術ブログでのAF_UNSPECとUnixドメインソケットに関する議論