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

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

このコミットは、Go言語の標準ライブラリにおいて、Plan 9オペレーティングシステムに特有のエラー定数 os.EPLAN9syscall.EPLAN9 へと移行し、それに伴う参照箇所の修正を行うものです。これにより、エラーの定義がより適切な syscall パッケージに集約され、コードの整合性が向上するとともに、Plan 9向けのクロスビルドの問題が修正されます。

コミット

commit 03d4c7c7d79bbe7e1912f407fe1d5ddbccf0f73b
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Fri Feb 17 10:59:30 2012 +0900

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

https://github.com/golang/go/commit/03d4c7c7d79bbe7e1912f407fe1d5ddbccf0f73b

元コミット内容

    net, os, syscall: delete os.EPLAN9
    
    Also fixes plan9 cross-build.
    
    R=rsc, r
    CC=golang-dev
    https://golang.org/cl/5675073

変更の背景

この変更の主な背景は、Go言語の標準ライブラリにおけるエラー定数の適切な配置と、Plan 9オペレーティングシステム向けのクロスビルドの修正です。

Go言語では、オペレーティングシステム固有の低レベルなエラーやシステムコールに関連する定数は通常 syscall パッケージに定義されます。しかし、以前のバージョンでは、Plan 9に特有の「サポートされていない操作」を示すエラー EPLAN9os パッケージに定義されていました。

この配置は、以下の点で問題がありました。

  1. 論理的な不整合: os パッケージはより高レベルなOS抽象化を提供しますが、EPLAN9 のような特定OSのエラーコードは、より低レベルなシステムコールインターフェースを提供する syscall パッケージに属するべきです。
  2. クロスビルドの問題: os パッケージに特定のOSのエラー定数が存在すると、異なるOS向けにGoプログラムをクロスコンパイルする際に、その定数が存在しない環境でビルドエラーが発生する可能性がありました。特にPlan 9は一般的なOSではないため、そのエラー定数が他のOSのビルドに影響を与えることは望ましくありませんでした。

このコミットは、EPLAN9os パッケージから削除し、syscall パッケージに移動することで、これらの問題を解決し、Go標準ライブラリの設計原則に沿った形に修正することを目的としています。

前提知識の解説

Plan 9 from Bell Labs

Plan 9 from Bell Labsは、ベル研究所で開発された分散オペレーティングシステムです。Unixの後継として設計され、すべてのリソース(ファイル、デバイス、ネットワーク接続など)をファイルシステムとして表現するというユニークな哲学を持っています。Go言語の開発者の一部はPlan 9の開発にも携わっており、Go言語の設計思想にもPlan 9の影響が見られます。Go言語は初期からPlan 9をサポート対象OSの一つとしていました。

Go言語の os パッケージと syscall パッケージ

  • os パッケージ: Go言語の os パッケージは、オペレーティングシステムとの基本的な相互作用を提供します。ファイル操作、プロセス管理、環境変数へのアクセスなど、OSに依存しない高レベルな抽象化を提供することを目的としています。例えば、os.Openos.Stat などがあります。
  • syscall パッケージ: syscall パッケージは、低レベルなシステムコールインターフェースを提供します。これは、各オペレーティングシステムが提供するネイティブなシステムコールをGoから直接呼び出すためのものです。OSごとに異なる実装を持ち、OS固有の定数や構造体、関数が含まれます。例えば、syscall.Opensyscall.Stat などがあります。エラー定数も、通常は syscall パッケージ内でOS固有に定義されます。

Go言語のエラーハンドリング

Go言語では、エラーは error インターフェースを実装する値として扱われます。多くの標準ライブラリ関数は、最後の戻り値として error 型を返します。エラーが発生しなかった場合は nil を返します。

os パッケージや syscall パッケージで定義されるエラー定数は、特定の状況で発生する可能性のあるエラーを示すために使用されます。例えば、os.ErrNotExist はファイルが存在しないことを示し、syscall.EINVAL は無効な引数がシステムコールに渡されたことを示します。

技術的詳細

このコミットの技術的な核心は、os.EPLAN9 というエラー定数の定義を os パッケージから削除し、syscall パッケージに移動したことです。

以前は、Plan 9環境でサポートされていない操作が行われた場合に返されるエラーとして os.EPLAN9 が存在しました。これは、os パッケージが提供する高レベルなAPIが、内部的にPlan 9の特定の機能に依存しており、それが利用できない場合にこのエラーを返すという設計になっていたためと考えられます。

しかし、EPLAN9 は「Plan 9に特有の、サポートされていない操作」という、非常にOS固有かつ低レベルな意味合いを持つエラーです。このようなエラーは、OSのシステムコール層と直接関連する syscall パッケージで定義されるのがより適切です。syscall パッケージは、各OSのシステムコールインターフェースを直接ラップするため、OS固有のエラー定数を保持するのに適した場所です。

この変更により、以下の影響があります。

  1. エラー定義の一元化: EPLAN9 のようなOS固有のエラーが syscall パッケージに集約され、エラーの定義がより論理的に整理されました。
  2. クロスビルドの改善: os パッケージは、Goプログラムが動作するすべてのOSで共通のインターフェースを提供することを目指しています。os.EPLAN9 のような特定のOSにしか存在しないエラー定数が os パッケージにあると、他のOS向けにクロスビルドする際に、その定数が未定義であるためにコンパイルエラーが発生する可能性がありました。syscall パッケージはOSごとに異なる実装を持つことが前提であるため、そこに EPLAN9 を定義してもクロスビルドの問題は発生しません。
  3. 依存関係の整理: net パッケージ内のPlan 9固有のファイル (file_plan9.go, iprawsock_plan9.go など) や os/exec/lp_plan9.go などで os.EPLAN9 を参照していた箇所が syscall.EPLAN9 に変更されました。これにより、これらのファイルは os パッケージではなく syscall パッケージに依存するようになり、依存関係がより明確になりました。
  4. syscall/zerrors_plan9_386.go の変更: このファイルはPlan 9 (386アーキテクチャ) 向けのエラー定数を定義するファイルです。このコミットでは、EPLAN9 を含む複数のエラー定数(EINVAL, ENOTDIR, ENOENT, EEXIST, EIO, ENAMETOOLONG, EPERM)が errors.New を使って var として定義されるようになりました。これにより、これらのエラーがGoの error インターフェースを実装する具体的なエラー値として利用可能になります。また、Signal 型の定義と SIGINT, SIGKILL 定数も追加され、Plan 9におけるシグナルハンドリングの基礎が整備されています。
  5. os/signal/signal_stub.go の追加: Plan 9環境では、一般的なUnix系OSのようなシグナルハンドリングの仕組みが異なるため、signal_stub.go というスタブファイルが追加されました。これは、os/signal パッケージがPlan 9向けにビルドされる際に、シグナル関連の関数が適切に処理されるようにするためのものです。enableSignal 関数が空の実装で提供されており、Plan 9ではシグナルがサポートされていないか、異なる方法で処理されることを示唆しています。

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

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

  • src/pkg/net/file_plan9.go
  • src/pkg/net/iprawsock_plan9.go
  • src/pkg/net/ipsock_plan9.go
  • src/pkg/net/lookup_plan9.go
  • src/pkg/net/tcpsock_plan9.go
  • src/pkg/net/udpsock_plan9.go
  • src/pkg/net/unixsock_plan9.go
  • src/pkg/os/exec/lp_plan9.go
  • src/pkg/os/signal/signal_stub.go (新規追加)
  • src/pkg/syscall/syscall_plan9.go
  • src/pkg/syscall/zerrors_plan9_386.go

主要な変更は、os.EPLAN9 の参照を syscall.EPLAN9 に変更する部分と、syscall/zerrors_plan9_386.goEPLAN9 を含むエラー定数を syscall パッケージ内で定義する部分です。

例: src/pkg/net/file_plan9.go の変更

--- a/src/pkg/net/file_plan9.go
+++ b/src/pkg/net/file_plan9.go
@@ -6,6 +6,7 @@ package net
 
 import (
 	"os"
+	"syscall"
 )
 
 // FileConn returns a copy of the network connection corresponding to
@@ -13,7 +14,7 @@ import (
 // finished.  Closing c does not affect f, and closing f does not
 // affect c.
 func FileConn(f *os.File) (c Conn, err error) {
-	return nil, os.EPLAN9
+	return nil, syscall.EPLAN9
 }
 
 // FileListener returns a copy of the network listener corresponding
@@ -21,7 +22,7 @@ func FileConn(f *os.File) (c Conn, err error) {
 // when finished.  Closing c does not affect l, and closing l does not
 // affect c.
 func FileListener(f *os.File) (l Listener, err error) {
-	return nil, os.EPLAN9
+	return nil, syscall.EPLAN9
 }
 
 // FilePacketConn returns a copy of the packet network connection
@@ -29,5 +30,5 @@ func FileListener(f *os.File) (l Listener, err error) {
 // responsibility to close f when finished.  Closing c does not affect
 // f, and closing f does not affect c.
 func FilePacketConn(f *os.File) (c PacketConn, err error) {
-	return nil, os.EPLAN9
+	return nil, syscall.EPLAN9
 }

例: src/pkg/syscall/zerrors_plan9_386.go の変更

--- a/src/pkg/syscall/zerrors_plan9_386.go
+++ b/src/pkg/syscall/zerrors_plan9_386.go
@@ -4,6 +4,8 @@
 
 package syscall
 
+import "errors"
+
 // Constants
 const (
 	// Invented values to support what package os expects.
@@ -22,6 +24,19 @@ const (
 	S_IFREG  = 0x8000
 	S_IFLNK  = 0xa000
 	S_IFSOCK = 0xc000
+\
+\tSIGINT  = Signal(0x2)
+\tSIGKILL = Signal(0x9)
 )
 
-// Error table
+// Errors
+var (
+\tEINVAL       = errors.New("bad arg in system call")
+\tENOTDIR      = errors.New("not a directory")
+\tENOENT       = errors.New("file does not exist")
+\tEEXIST       = errors.New("file already exists")
+\tEIO          = errors.New("i/o error")
+\tENAMETOOLONG = errors.New("file name too long")
+\tEPERM        = errors.New("permission denied")
+\tEPLAN9       = errors.New("not supported by plan 9")
+)

コアとなるコードの解説

上記のコード変更は、Go言語の標準ライブラリにおけるエラーハンドリングとOS抽象化の設計原則を強化するものです。

net パッケージ内のPlan 9固有のファイル群 (file_plan9.go, iprawsock_plan9.go など) では、FileConn, FileListener, FilePacketConn などの関数が、Plan 9環境ではサポートされていない機能に対して os.EPLAN9 を返していました。これらの関数は、ファイルディスクリプタからネットワーク接続を生成するような、OSの低レベルな機能に密接に関連しています。このコミットでは、これらの関数が返すエラーを syscall.EPLAN9 に変更することで、エラーの発生源が syscall パッケージに由来することを明確にしています。これは、os パッケージが提供する高レベルな抽象化のレイヤーではなく、より低レベルなシステムコール層で発生するエラーであることを示唆しています。

src/pkg/syscall/zerrors_plan9_386.go の変更は、Plan 9 (386アーキテクチャ) 向けに、Goの error インターフェースを実装する具体的なエラー値を syscall パッケージ内で定義したものです。以前はコメントアウトされていた Error table の部分が、var 宣言と errors.New を用いて実際のGoのエラー値として定義されました。これにより、syscall.EINVALsyscall.EPLAN9 などのエラーを、Goの標準的なエラーハンドリングメカニズム (if err == syscall.EPLAN9) で直接比較できるようになります。

また、syscall/syscall_plan9.goSignal 型が追加され、Signal インターフェースを実装することで、Goの os.Signal 型との互換性を持たせています。これは、Plan 9におけるシグナル処理の基盤を整備するものです。

src/pkg/os/signal/signal_stub.go の追加は、Plan 9が他のUnix系OSとは異なるシグナル処理モデルを持つため、os/signal パッケージがPlan 9向けにビルドされる際に、シグナル関連の関数が適切に処理されるようにするためのものです。このスタブファイルは、Plan 9ではシグナルがサポートされていないか、異なる方法で処理されることを示唆しており、クロスビルド時のエラーを回避する役割も果たします。

これらの変更は、Go言語の標準ライブラリが、各OSの特性を尊重しつつ、よりクリーンで一貫性のあるAPIを提供するための継続的な努力の一環と言えます。

関連リンク

参考にした情報源リンク