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

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

このコミットは、Go言語の実験的なSSA (Static Single Assignment) パッケージ exp/ssa における、BSD系OSでの動作不良(breakage)を修正するものです。具体的には、ディレクトリの内容を読み取るためのシステムコールとして、Linux固有の syscall.Getdents の代わりに、よりポータブルな syscall.ReadDirent を使用するように変更しています。

コミット

commit 877153f04aef18d25757ad3bf4b097460c7c4699
Author: Alan Donovan <adonovan@google.com>
Date:   Wed Feb 27 17:00:02 2013 -0500

    exp/ssa: fix *bsd breakage.
    
    Use portable ReadDirent, not linux Getdents.
    
    R=gri
    TBR=gri
    CC=golang-dev
    https://golang.org/cl/7405051

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

https://github.com/golang/go/commit/877153f04aef18d25757ad3bf4b097460c7c4699

元コミット内容

Go言語の実験的なSSAパッケージにおいて、BSD系OSで発生していた不具合を修正しました。この修正は、Linux固有の syscall.Getdents システムコールではなく、よりポータブルな syscall.ReadDirent を使用するように変更することで実現されました。

変更の背景

このコミットの背景には、Go言語の exp/ssa パッケージが、異なるオペレーティングシステム間でのシステムコール互換性の問題に直面していたことがあります。特に、ディレクトリの内容を読み取るためのシステムコール Getdents はLinuxに特化したものであり、FreeBSDやOpenBSDなどのBSD系OSでは直接利用できませんでした。この非互換性が原因で、exp/ssa パッケージがBSD環境で正しく動作しない「breakage」が発生していました。

Go言語はクロスプラットフォーム対応を重視しており、特定のOSに依存するシステムコールを直接使用することは、移植性の観点から望ましくありません。そのため、より広範なOSで利用可能な、抽象化されたシステムコールまたは同等の機能に置き換える必要がありました。このコミットは、その問題に対処し、exp/ssa パッケージのBSD系OSでの動作を保証することを目的としています。

前提知識の解説

Go言語の syscall パッケージ

Go言語の syscall パッケージは、オペレーティングシステムが提供する低レベルなシステムコールへのインターフェースを提供します。これにより、Goプログラムからファイル操作、プロセス管理、ネットワーク通信など、OSカーネルの機能に直接アクセスできます。しかし、システムコールはOSによってその名称、引数、戻り値が異なるため、syscall パッケージを直接使用するコードは、OS間の移植性に課題を抱えることがあります。

GetdentsReadDirent

  • Getdents: これは主にLinuxカーネルが提供するシステムコールで、ディレクトリ内のエントリ(ファイルやサブディレクトリ)を効率的に読み取るために使用されます。getdents は、ディレクトリの内容を特定のバッファにまとめて読み込み、そのバッファを解析することで個々のエントリ情報を取得します。このシステムコールはLinuxに特化しており、他のUnix系OS(特にBSD系)では同名のシステムコールが存在しないか、あるいは異なるインターフェースを持つことが一般的です。

  • ReadDirent: ReadDirent は、Go言語の syscall パッケージ(またはより新しい golang.org/x/sys パッケージ)において、ディレクトリの内容を読み取るためのよりポータブルな抽象化された関数です。これは、内部的に各OSに最適なシステムコール(Linuxでは getdents、BSDでは getdirentries など)を呼び出すことで、OS間の差異を吸収し、開発者がプラットフォームの違いを意識せずにディレクトリを読み取れるように設計されています。つまり、ReadDirent は、特定のOSのシステムコールに直接依存するのではなく、複数のOSで共通のインターフェースを提供する役割を担っています。

BSD系OS

BSD (Berkeley Software Distribution) 系OSは、Unixの派生であり、FreeBSD, OpenBSD, NetBSD, macOS (Darwin) などが含まれます。これらのOSは、Linuxとは異なるカーネル設計やシステムコールインターフェースを持つことが多く、特に低レベルなシステムプログラミングにおいては、OS間の互換性を考慮する必要があります。

exp/ssa パッケージ

exp/ssa は、Go言語のコンパイラやツールチェインの一部として開発されていた実験的なパッケージで、プログラムの静的単一代入 (Static Single Assignment: SSA) 形式を扱うための機能を提供します。SSA形式は、コンパイラの最適化や静的解析において非常に重要な中間表現です。このパッケージは、プログラムの実行をシミュレートするインタープリタ機能も持っており、その中でファイルシステム操作(ディレクトリの読み取りなど)が必要となる場合があります。

技術的詳細

このコミットの核心は、Go言語の syscall パッケージにおけるシステムコール選択の移植性に関する問題です。

syscall.Getdents は、Linuxカーネルが提供する getdents システムコールを直接ラップしたものです。このシステムコールは、ディレクトリ内のエントリを効率的に読み取るために設計されており、一度のシステムコールで複数のディレクトリエントリをバッファに格納します。しかし、この getdents システムコールはLinux固有であり、BSD系OSには同名のシステムコールが存在しません。BSD系OSでは、ディレクトリの内容を読み取るために getdirentries などの異なるシステムコールが使用されます。

一方、syscall.ReadDirent は、Go言語の syscall パッケージが提供する、より高レベルで抽象化されたディレクトリ読み取り関数です。この関数は、内部的にコンパイルターゲットとなるOSに応じて適切なシステムコール(Linuxでは getdents、BSDでは getdirentries など)を呼び出すように実装されています。これにより、開発者は ReadDirent を使用することで、基盤となるOSのシステムコールの違いを意識することなく、ポータブルなコードを書くことができます。

exp/ssa/interp/external.go ファイルは、exp/ssa パッケージのインタープリタが外部のシステムコールをどのように扱うかを定義しています。以前は、このファイルが syscall.Getdents を直接参照していました。これは、Linux環境では問題なく動作しますが、BSD環境では syscall.Getdents が利用できないため、コンパイルエラーまたは実行時エラーを引き起こしていました。

このコミットでは、syscall.Getdents への参照を削除し、代わりに syscall.ReadDirent を使用するように変更することで、この移植性の問題を解決しています。これにより、exp/ssa パッケージは、LinuxだけでなくBSD系OSでも正しくディレクトリの内容を読み取れるようになり、クロスプラットフォームでの動作が保証されます。

各OS固有の実装ファイル(external_plan9.go, external_unix.go, external_windows.go)では、ext۰syscall۰Getdents 関数が削除され、代わりに ext۰syscall۰ReadDirent 関数が追加または修正されています。特に external_unix.go では、ext۰syscall۰Getdents の実装が ext۰syscall۰ReadDirent に置き換えられ、内部で syscall.ReadDirent を呼び出すように変更されています。これにより、Unix系のOS(LinuxやBSDを含む)で共通のインターフェースを通じてディレクトリ読み取りが可能になります。

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

このコミットでは、以下の4つのファイルが変更されています。

  1. src/pkg/exp/ssa/interp/external.go
  2. src/pkg/exp/ssa/interp/external_plan9.go
  3. src/pkg/exp/ssa/interp/external_unix.go
  4. src/pkg/exp/ssa/interp/external_windows.go

src/pkg/exp/ssa/interp/external.go

--- a/src/pkg/exp/ssa/interp/external.go
+++ b/src/pkg/exp/ssa/interp/external.go
@@ -70,7 +70,6 @@ var externals = map[string]externalFn{
 	"syscall.Close":                   ext۰syscall۰Close,
 	"syscall.Exit":                    ext۰syscall۰Exit,
 	"syscall.Fstat":                   ext۰syscall۰Fstat,
-	"syscall.Getdents":                ext۰syscall۰Getdents,
 	"syscall.Getpid":                  ext۰syscall۰Getpid,
 	"syscall.Getwd":                   ext۰syscall۰Getwd,
 	"syscall.Kill":                    ext۰syscall۰Kill,
@@ -78,6 +77,7 @@ var externals = map[string]externalFn{
 	"syscall.Open":                    ext۰syscall۰Open,
 	"syscall.ParseDirent":             ext۰syscall۰ParseDirent,
 	"syscall.Read":                    ext۰syscall۰Read,
+\t"syscall.ReadDirent":              ext۰syscall۰ReadDirent,
 	"syscall.Stat":                    ext۰syscall۰Stat,
 	"syscall.Write":                   ext۰syscall۰Write,
 	"time.Sleep":                      ext۰time۰Sleep,

src/pkg/exp/ssa/interp/external_plan9.go

--- a/src/pkg/exp/ssa/interp/external_plan9.go
+++ b/src/pkg/exp/ssa/interp/external_plan9.go
@@ -15,9 +15,6 @@ func ext۰syscall۰Close(fn *ssa.Function, args []value) value {
 func ext۰syscall۰Fstat(fn *ssa.Function, args []value) value {
 	panic("syscall.Fstat not yet implemented")
 }
-func ext۰syscall۰Getdents(fn *ssa.Function, args []value) value {
-	panic("syscall.Getdents not yet implemented")
-}
 func ext۰syscall۰Kill(fn *ssa.Function, args []value) value {
 	panic("syscall.Kill not yet implemented")
 }
@@ -33,6 +30,9 @@ func ext۰syscall۰ParseDirent(fn *ssa.Function, args []value) value {
 func ext۰syscall۰Read(fn *ssa.Function, args []value) value {
 	panic("syscall.Read not yet implemented")
 }
+func ext۰syscall۰ReadDirent(fn *ssa.Function, args []value) value {
+\tpanic("syscall.ReadDirent not yet implemented")
+}
 func ext۰syscall۰Stat(fn *ssa.Function, args []value) value {
 	panic("syscall.Stat not yet implemented")
 }

src/pkg/exp/ssa/interp/external_unix.go

--- a/src/pkg/exp/ssa/interp/external_unix.go
+++ b/src/pkg/exp/ssa/interp/external_unix.go
@@ -54,12 +54,12 @@ func ext۰syscall۰Fstat(fn *ssa.Function, args []value) value {
 	return wrapError(err)
 }
 
-func ext۰syscall۰Getdents(fn *ssa.Function, args []value) value {
-	// func GetDents(fd int, buf []byte) (n int, err error)
+func ext۰syscall۰ReadDirent(fn *ssa.Function, args []value) value {
+	// func ReadDirent(fd int, buf []byte) (n int, err error)
 	fd := args[0].(int)
 	p := args[1].([]value)
 	b := make([]byte, len(p))
-	n, err := syscall.Getdents(fd, b)
+	n, err := syscall.ReadDirent(fd, b)
 	for i := 0; i < n; i++ {
 		p[i] = b[i]
 	}

src/pkg/exp/ssa/interp/external_windows.go

--- a/src/pkg/exp/ssa/interp/external_windows.go
+++ b/src/pkg/exp/ssa/interp/external_windows.go
@@ -16,9 +16,6 @@ func ext۰syscall۰Close(fn *ssa.Function, args []value) value {
 func ext۰syscall۰Fstat(fn *ssa.Function, args []value) value {
 	panic("syscall.Fstat not yet implemented")
 }
-func ext۰syscall۰Getdents(fn *ssa.Function, args []value) value {
-	panic("syscall.Getdents not yet implemented")
-}
 func ext۰syscall۰Kill(fn *ssa.Function, args []value) value {
 	panic("syscall.Kill not yet implemented")
 }
@@ -34,6 +31,9 @@ func ext۰syscall۰ParseDirent(fn *ssa.Function, args []value) value {
 func ext۰syscall۰Read(fn *ssa.Function, args []value) value {
 	panic("syscall.Read not yet implemented")
 }
+func ext۰syscall۰ReadDirent(fn *ssa.Function, args []value) value {
+\tpanic("syscall.ReadDirent not yet implemented")
+}
 func ext۰syscall۰Stat(fn *ssa.Function, args []value) value {
 	panic("syscall.Stat not yet implemented")
 }

コアとなるコードの解説

src/pkg/exp/ssa/interp/external.go の変更

このファイルは、exp/ssa インタープリタが外部の syscall 関数をどのようにマッピングするかを定義する externals マップを含んでいます。

  • - "syscall.Getdents": ext۰syscall۰Getdents, の行が削除されました。これは、Linux固有の Getdents システムコールへの参照を完全に削除するためです。
  • + "syscall.ReadDirent": ext۰syscall۰ReadDirent, の行が追加されました。これにより、よりポータブルな ReadDirent システムコールがインタープリタの外部関数として利用可能になります。

この変更により、インタープリタは Getdents ではなく ReadDirent を使用するように切り替わります。

src/pkg/exp/ssa/interp/external_plan9.go, src/pkg/exp/ssa/interp/external_windows.go の変更

これらのファイルは、それぞれPlan 9とWindows環境における syscall 関数のインタープリタ実装を提供します。

  • func ext۰syscall۰Getdents(...) の定義が削除されました。これらのOSでは Getdents がサポートされていないため、以前は panic を発生させるだけのスタブ実装でした。
  • func ext۰syscall۰ReadDirent(...) の定義が追加されました。これらのOSでも ReadDirent はまだ実装されておらず、同様に panic を発生させるスタブ実装となっています。これは、ReadDirent がこれらのプラットフォームでまだ完全にサポートされていないことを示していますが、コードベース全体で Getdents から ReadDirent への移行を進めるための一貫した変更です。

src/pkg/exp/ssa/interp/external_unix.go の変更

このファイルは、Unix系OS(Linux、BSDなど)における syscall 関数のインタープリタ実装を提供します。

  • - func ext۰syscall۰Getdents(fn *ssa.Function, args []value) value { の行が削除されました。
  • + func ext۰syscall۰ReadDirent(fn *ssa.Function, args []value) value { の行が追加されました。これにより、Getdents の実装が ReadDirent の実装に置き換えられました。
  • 内部の呼び出しも - n, err := syscall.Getdents(fd, b) から + n, err := syscall.ReadDirent(fd, b) に変更されました。

この変更が最も重要です。external_unix.go はLinuxとBSDの両方をカバーするため、ここで syscall.Getdentssyscall.ReadDirent に置き換えることで、BSD系OSでの動作不良が修正されます。syscall.ReadDirent は、内部的に各Unix系OSの適切なディレクトリ読み取りシステムコール(Linuxの getdents やBSDの getdirentries など)を呼び出すため、この変更によりコードの移植性が大幅に向上します。

全体として、このコミットは、Go言語の exp/ssa パッケージが特定のOSに依存するシステムコールを使用する問題を解決し、よりポータブルな syscall.ReadDirent を採用することで、クロスプラットフォーム互換性を向上させています。

関連リンク

参考にした情報源リンク