[インデックス 11762] ファイルの概要
このコミットは、Go言語のos
パッケージにおける重要なAPI変更とクリーンアップを目的としています。具体的には、os.Exec
関数の削除、os.NewFile
関数およびFile.Fd
メソッドにおけるファイルディスクリプタの型をint
からuintptr
への変更、os.ShellExpand
関数のos.ExpandEnv
へのリネーム、そして一部のファイルオープンフラグ(O_NDELAY
, O_NONBLOCK
, O_NOCTTY
, O_ASYNC
)の削除、およびドキュメントの修正が含まれています。これらの変更は、Go言語のクロスプラットフォーム互換性の向上、APIの一貫性、およびよりクリーンな設計を目指したものです。
コミット
commit 4152b4345724dae4b058d48a23d29ac8f8bda453
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Fri Feb 10 14:16:15 2012 +1100
os: delete Exec, NewFile takes uintptr, rename ShellExpand, doc fixes
Delete O_NDELAY, O_NONBLOCK, O_NOCTTY, O_ASYNC.
Clean up some docs.
Rename ShellExpand -> ExpandEnv.
Make NewFile take a uintptr; change File.Fd to return one.
(for API compatibility between Unix and Windows)
Fixes #2947
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5655045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4152b4345724dae4b058d48a23d29ac8f8bda453
元コミット内容
os
パッケージにおいて、Exec
関数の削除、NewFile
関数がuintptr
を引数にとるように変更、ShellExpand
関数のリネーム、およびドキュメントの修正を行いました。また、O_NDELAY
, O_NONBLOCK
, O_NOCTTY
, O_ASYNC
といったファイルオープンフラグを削除しました。NewFile
とFile.Fd
の型変更は、UnixとWindows間でのAPI互換性を確保するためです。この変更はIssue #2947を修正します。
変更の背景
このコミットの主な背景には、Go言語の標準ライブラリにおけるAPIの一貫性とクロスプラットフォーム互換性の向上が挙げられます。
os.Exec
の削除:os.Exec
は現在のプロセスを新しいプロセスに置き換える機能を提供していましたが、これはsyscall
パッケージのExec
関数と重複しており、より低レベルな操作であるため、os
パッケージの役割としては適切ではないと判断されました。より高レベルなプロセス実行はos/exec
パッケージが担当するべきという設計思想に基づいています。- ファイルディスクリプタの型変更 (
int
からuintptr
へ): Unix系システムではファイルディスクリプタは通常int
型で表現されますが、WindowsではファイルハンドルがHANDLE
型(実体はuintptr
)で表現されます。Go言語がクロスプラットフォームで動作する上で、ファイルディスクリプタを扱うAPIが異なる型を持つことは、内部実装の複雑さを増し、APIの統一性を損ねます。uintptr
はポインタを保持できる整数型であり、プラットフォーム固有のハンドル型を抽象化するのに適しています。これにより、UnixとWindowsの両方で一貫したAPIを提供できるようになります。これは特にIssue #2947で議論された問題への対応です。 ShellExpand
からExpandEnv
へのリネーム: 関数の名前がその機能により正確に合致するように変更されました。ShellExpand
という名前はシェル特有の展開(例: ワイルドカード展開)を想起させる可能性がありますが、実際には環境変数の展開のみを行うため、ExpandEnv
の方が適切です。- ファイルオープンフラグの削除:
O_NDELAY
,O_NONBLOCK
,O_NOCTTY
,O_ASYNC
といったフラグは、ファイルオープン時に非ブロッキングI/Oや端末制御などの特定の動作を指定するためのものですが、Goのos
パッケージの設計思想では、これらの低レベルなI/O制御はsyscall
パッケージや、より高レベルなnet
パッケージなどの特定のコンテキストで扱うべきであり、一般的なファイル操作を行うos
パッケージからは削除されました。これにより、os
パッケージのAPIがよりシンプルで、一般的なファイル操作に特化するようになります。
前提知識の解説
このコミットを理解するためには、以下の概念についての知識が必要です。
- ファイルディスクリプタ (File Descriptor, FD): Unix系オペレーティングシステムにおいて、開かれたファイルやI/Oリソース(ソケット、パイプなど)を識別するためにカーネルがプロセスに割り当てる非負の整数です。標準入力(0)、標準出力(1)、標準エラー出力(2)は予約されています。
- ファイルハンドル (File Handle): Windowsオペレーティングシステムにおいて、ファイルやI/Oリソースを識別するために使用される抽象的な参照です。Unixのファイルディスクリプタに相当しますが、その実体は
HANDLE
型であり、これは通常void*
またはuintptr
として扱われます。 uintptr
(Go言語): Go言語における組み込み型の一つで、ポインタを保持できる符号なし整数型です。ポインタ演算は許可されていませんが、ポインタとuintptr
の間で変換が可能です。主に、C言語との相互運用や、OS固有のシステムコールでポインタを整数として扱う必要がある場合に使用されます。int
型は通常32ビットまたは64ビットの符号付き整数ですが、uintptr
はシステムのアドレス空間のサイズに依存し、ポインタのサイズと同じになります。syscall
パッケージ (Go言語): オペレーティングシステムの低レベルなプリミティブ(システムコール)へのアクセスを提供するGoの標準ライブラリパッケージです。ファイル操作、プロセス管理、ネットワーク通信など、OSに直接働きかける機能が含まれます。プラットフォームごとに異なる実装を持ちます。os
パッケージ (Go言語): オペレーティングシステム機能へのプラットフォームに依存しないインターフェースを提供するGoの標準ライブラリパッケージです。ファイル操作、プロセス情報、環境変数など、より高レベルな抽象化を提供します。os/exec
パッケージ (Go言語): 外部コマンドの実行を扱うためのGoの標準ライブラリパッケージです。os.Exec
よりも高レベルなAPIを提供し、コマンドの実行、標準入出力のリダイレクト、プロセスの待機などを容易にします。- 環境変数 (Environment Variables): オペレーティングシステムがプロセスに提供する動的な名前付きの値の集合です。プログラムの動作を構成するために使用されます。
- ファイルオープンフラグ:
open()
システムコールなどでファイルをオープンする際に、ファイルのアクセスモード(読み取り専用、書き込み専用など)や動作(ファイルが存在しない場合に作成する、非ブロッキングモードなど)を指定するための定数です。
技術的詳細
このコミットにおける技術的変更は多岐にわたりますが、特に重要なのはファイルディスクリプタの型変更とos.Exec
の削除です。
ファイルディスクリプタの型変更 (int
からuintptr
へ)
- 背景: Go言語はクロスプラットフォーム言語であり、異なるOS間で一貫したAPIを提供することが重要です。Unix系OSではファイルディスクリプタは
int
型ですが、Windowsではファイルハンドルがsyscall.Handle
型(実体はuintptr
)です。この違いがAPIの統一性を妨げていました。 - 変更内容:
os.NewFile(fd int, name string) *File
がos.NewFile(fd uintptr, name string) *File
に変更されました。(*File).Fd() int
が(*File).Fd() uintptr
に変更されました。
- 影響:
- これにより、
os
パッケージのファイル操作APIがUnixとWindowsの両方で同じシグネチャを持つことができるようになり、内部実装でプラットフォーム固有の型変換を吸収する形になりました。 - 既存のコードで
os.NewFile
やFile.Fd
を使用している場合、コンパイルエラーが発生し、int()
やuintptr()
による明示的な型変換が必要になります。コミットメッセージのUpdating: Code will fail to compile and must be updated by hand.
はこの点を指しています。 net
パッケージなど、os
パッケージのファイルディスクリプタを直接扱う箇所でも、同様の型変換が導入されています(例:int(s.pr.Fd())
)。
- これにより、
- 利点: APIの統一性、コードの可読性向上、クロスプラットフォーム開発の容易化。
os.Exec
の削除
- 背景:
os.Exec
は、現在のプロセスを新しい実行可能ファイルに置き換えるUnixのexec
システムコールを直接ラップしたものでした。しかし、Goにはより高レベルなプロセス実行を扱うos/exec
パッケージが存在し、また低レベルなシステムコールへのアクセスはsyscall
パッケージが担当するという役割分担が明確化されました。 - 変更内容:
src/pkg/os/exec_plan9.go
とsrc/pkg/os/exec_posix.go
からExec
関数が完全に削除されました。 - 影響:
os.Exec
を使用していた既存のコードはコンパイルエラーとなり、syscall.Exec
(低レベル)またはos/exec
パッケージ(高レベル)を使用するように変更する必要があります。コミットメッセージにもThe Exec function has been removed; callers should use Exec from the syscall package, where available.
と明記されています。 - 利点:
os
パッケージの責務が明確化され、APIがシンプルになりました。プロセス実行に関する機能がos/exec
パッケージに集約され、より使いやすく、安全なAPIが提供されるようになりました。
ShellExpand
からExpandEnv
へのリネーム
- 背景: 関数の名前がその実際の機能(環境変数の展開)をより正確に反映するように変更されました。
- 変更内容:
src/pkg/os/env.go
でShellExpand
関数がExpandEnv
にリネームされました。 - 影響:
os.ShellExpand
を使用していたコードはos.ExpandEnv
に修正する必要があります。 - 利点: APIの意図がより明確になり、誤解を招く可能性が減少しました。
ファイルオープンフラグの削除
- 背景:
O_NDELAY
,O_NONBLOCK
,O_NOCTTY
,O_ASYNC
といったフラグは、ファイルオープン時の低レベルな動作を制御するものであり、Goのos
パッケージの抽象化レベルには合致しないと判断されました。これらの機能は、必要に応じてsyscall
パッケージを直接使用するか、net
パッケージのような特定のコンテキストで内部的に処理されるべきです。 - 変更内容:
src/pkg/os/file.go
からこれらの定数が削除されました。 - 影響: これらのフラグを直接使用していたコードはコンパイルエラーとなります。
- 利点:
os
パッケージのAPIがよりシンプルになり、一般的なファイル操作に集中できるようになりました。
コアとなるコードの変更箇所
このコミットで最も影響の大きいコアとなるコードの変更箇所は以下のファイルに集中しています。
src/pkg/os/file.go
:Stdin
,Stdout
,Stderr
の初期化でsyscall.Stdin
などがuintptr()
でキャストされるようになりました。- ファイルオープンフラグの定数定義から
O_NDELAY
,O_NONBLOCK
,O_NOCTTY
,O_ASYNC
が削除されました。
src/pkg/os/file_unix.go
:(*File).Fd()
メソッドの戻り値の型がint
からuintptr
に変更されました。NewFile
関数の引数fd
の型がint
からuintptr
に変更され、内部でint()
にキャストして使用しています。OpenFile
やPipe
などの関数でNewFile
を呼び出す際に、ファイルディスクリプタがuintptr()
でキャストされるようになりました。
src/pkg/os/file_windows.go
:(*File).Fd()
メソッドの戻り値の型がsyscall.Handle
からuintptr
に変更されました。NewFile
関数の引数fd
の型がsyscall.Handle
からuintptr
に変更され、内部でsyscall.Handle()
にキャストして使用しています。
src/pkg/os/env.go
:ShellExpand
関数がExpandEnv
にリネームされました。Environ
関数のドキュメントが"key=value"
形式の文字列の「コピー」を返すことを明確にするように修正されました。
src/pkg/os/exec_plan9.go
およびsrc/pkg/os/exec_posix.go
:Exec
関数が完全に削除されました。
src/pkg/net/fd.go
,src/pkg/net/file.go
,src/pkg/net/newpollserver.go
,src/pkg/net/sendfile_linux.go
:File.Fd()
の戻り値がuintptr
になったことに伴い、int()
への明示的な型変換が多数追加されました(例:int(s.pr.Fd())
)。os.NewFile
の呼び出し箇所で、引数がuintptr()
でキャストされるようになりました。
doc/go1.html
およびdoc/go1.tmpl
:- Go 1のリリースノートに、
Exec
の削除、ShellExpand
のリネーム、NewFile
/File.Fd
の型変更に関する記述が追加されました。
- Go 1のリリースノートに、
コアとなるコードの解説
src/pkg/os/file_unix.go
と src/pkg/os/file_windows.go
における Fd()
と NewFile()
の変更
変更前 (Unixの例):
// Fd returns the integer Unix file descriptor referencing the open file.
func (f *File) Fd() int {
if f == nil {
return -1
}
return f.fd
}
// NewFile returns a new File with the given file descriptor and name.
func NewFile(fd int, name string) *File {
if fd < 0 {
return nil
}
f := &File{&file{fd: fd, name: name}}
runtime.SetFinalizer(f.file, (*file).close)
return f
}
変更後 (Unixの例):
// Fd returns the integer Unix file descriptor referencing the open file.
func (f *File) Fd() uintptr { // 戻り値がuintptrに変更
if f == nil {
return ^(uintptr(0)) // エラー値もuintptrに合わせる
}
return uintptr(f.fd) // 内部のint型fdをuintptrにキャストして返す
}
// NewFile returns a new File with the given file descriptor and name.
func NewFile(fd uintptr, name string) *File { // 引数がuintptrに変更
fdi := int(fd) // 内部でintにキャスト
if fdi < 0 {
return nil
}
f := &File{&file{fd: fdi, name: name}} // 内部ではint型fdを保持
runtime.SetFinalizer(f.file, (*file).close)
return f
}
解説:
この変更の核心は、Goのos
パッケージが提供するファイルディスクリプタ/ハンドルを扱うAPIの統一です。Unix系システムではファイルディスクリプタはint
ですが、Windowsではsyscall.Handle
(実体はuintptr
)です。このコミットでは、os.File
のFd()
メソッドが返す値と、os.NewFile
関数が受け取る引数の型を、両プラットフォームで共通して扱えるuintptr
に統一しました。
Fd()
メソッドは、内部で保持しているプラットフォーム固有のファイルディスクリプタ(Unixではint
、Windowsではsyscall.Handle
)をuintptr
にキャストして返します。NewFile()
関数は、uintptr
型の引数を受け取りますが、内部ではそれをプラットフォーム固有の型(Unixではint
、Windowsではsyscall.Handle
)にキャストし直して使用します。
これにより、os
パッケージのユーザーは、ファイルディスクリプタをuintptr
として抽象的に扱うことができ、プラットフォームの違いを意識する必要がなくなります。実際のシステムコール呼び出し時には、net
パッケージの例のように、必要に応じてint(fd.sysfile.Fd())
のように明示的にint
にキャストし直す必要があります。
src/pkg/os/env.go
における ShellExpand
から ExpandEnv
へのリネーム
変更前:
// ShellExpand replaces ${var} or $var in the string according to the values
// of the operating system's environment variables. References to undefined
// variables are replaced by the empty string.
func ShellExpand(s string) string {
return Expand(s, Getenv)
}
変更後:
// ExpandEnv replaces ${var} or $var in the string according to the values
// of the current environment variables. References to undefined
// variables are replaced by the empty string.
func ExpandEnv(s string) string {
return Expand(s, Getenv)
}
解説:
これは純粋なAPI名の変更です。ShellExpand
という名前は、シェルの機能(例: グロビング、コマンド置換)を連想させる可能性がありましたが、この関数が実際に行うのは環境変数の展開のみです。そのため、より機能に即したExpandEnv
という名前に変更されました。これにより、APIの意図が明確になり、誤用を防ぐことができます。
src/pkg/os/exec_plan9.go
および src/pkg/os/exec_posix.go
における Exec
関数の削除
変更前 (posixの例):
// Exec replaces the current process with an execution of the
// named binary, with arguments argv and environment envv.
// If successful, Exec never returns. If it fails, it returns an error.
//
// To run a child process, see StartProcess (for a low-level interface)
// or the os/exec package (for higher-level interfaces).
//
// If there is an error, it will be of type *PathError.
func Exec(name string, argv []string, envv []string) error {
if envv == nil {
envv = Environ()
}
e := syscall.Exec(name, argv, envv)
if e != nil {
return &PathError{"exec", name, e}
}
return nil
}
変更後: この関数は完全に削除されました。
解説:
os.Exec
は、現在のプロセスを新しい実行可能ファイルに置き換えるという、非常に低レベルかつ破壊的な操作を提供していました。Goの標準ライブラリの設計原則として、より低レベルなOSプリミティブはsyscall
パッケージに、より高レベルで安全なプロセス実行はos/exec
パッケージに集約するという方針があります。os.Exec
はこれらの役割分担の間に位置し、混乱を招く可能性があったため削除されました。ユーザーは、必要に応じてsyscall.Exec
を直接使用するか、ほとんどのユースケースではos/exec
パッケージのより柔軟で安全なAPI(例: exec.Command
)を使用することが推奨されます。
関連リンク
- Go CL 5655045: https://golang.org/cl/5655045
- Go Issue 2947: https://golang.org/issue/2947
参考にした情報源リンク
- Go言語の公式ドキュメント (os, syscall, os/execパッケージ)
- Go言語のIssueトラッカー (特に #2947)
- Unix系OSのファイルディスクリプタに関する資料
- Windowsのファイルハンドルに関する資料
uintptr
に関するGo言語の仕様や解説記事