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

[インデックス 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といったファイルオープンフラグを削除しました。NewFileFile.Fdの型変更は、UnixとWindows間でのAPI互換性を確保するためです。この変更はIssue #2947を修正します。

変更の背景

このコミットの主な背景には、Go言語の標準ライブラリにおけるAPIの一貫性とクロスプラットフォーム互換性の向上が挙げられます。

  1. os.Execの削除: os.Execは現在のプロセスを新しいプロセスに置き換える機能を提供していましたが、これはsyscallパッケージのExec関数と重複しており、より低レベルな操作であるため、osパッケージの役割としては適切ではないと判断されました。より高レベルなプロセス実行はos/execパッケージが担当するべきという設計思想に基づいています。
  2. ファイルディスクリプタの型変更 (intからuintptrへ): Unix系システムではファイルディスクリプタは通常int型で表現されますが、WindowsではファイルハンドルがHANDLE型(実体はuintptr)で表現されます。Go言語がクロスプラットフォームで動作する上で、ファイルディスクリプタを扱うAPIが異なる型を持つことは、内部実装の複雑さを増し、APIの統一性を損ねます。uintptrはポインタを保持できる整数型であり、プラットフォーム固有のハンドル型を抽象化するのに適しています。これにより、UnixとWindowsの両方で一貫したAPIを提供できるようになります。これは特にIssue #2947で議論された問題への対応です。
  3. ShellExpandからExpandEnvへのリネーム: 関数の名前がその機能により正確に合致するように変更されました。ShellExpandという名前はシェル特有の展開(例: ワイルドカード展開)を想起させる可能性がありますが、実際には環境変数の展開のみを行うため、ExpandEnvの方が適切です。
  4. ファイルオープンフラグの削除: 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) *Fileos.NewFile(fd uintptr, name string) *File に変更されました。
    • (*File).Fd() int(*File).Fd() uintptr に変更されました。
  • 影響:
    • これにより、osパッケージのファイル操作APIがUnixとWindowsの両方で同じシグネチャを持つことができるようになり、内部実装でプラットフォーム固有の型変換を吸収する形になりました。
    • 既存のコードでos.NewFileFile.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.gosrc/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.goShellExpand関数が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がよりシンプルになり、一般的なファイル操作に集中できるようになりました。

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

このコミットで最も影響の大きいコアとなるコードの変更箇所は以下のファイルに集中しています。

  1. src/pkg/os/file.go:
    • Stdin, Stdout, Stderrの初期化でsyscall.Stdinなどがuintptr()でキャストされるようになりました。
    • ファイルオープンフラグの定数定義からO_NDELAY, O_NONBLOCK, O_NOCTTY, O_ASYNCが削除されました。
  2. src/pkg/os/file_unix.go:
    • (*File).Fd()メソッドの戻り値の型がintからuintptrに変更されました。
    • NewFile関数の引数fdの型がintからuintptrに変更され、内部でint()にキャストして使用しています。
    • OpenFilePipeなどの関数でNewFileを呼び出す際に、ファイルディスクリプタがuintptr()でキャストされるようになりました。
  3. src/pkg/os/file_windows.go:
    • (*File).Fd()メソッドの戻り値の型がsyscall.Handleからuintptrに変更されました。
    • NewFile関数の引数fdの型がsyscall.Handleからuintptrに変更され、内部でsyscall.Handle()にキャストして使用しています。
  4. src/pkg/os/env.go:
    • ShellExpand関数がExpandEnvにリネームされました。
    • Environ関数のドキュメントが"key=value"形式の文字列の「コピー」を返すことを明確にするように修正されました。
  5. src/pkg/os/exec_plan9.go および src/pkg/os/exec_posix.go:
    • Exec関数が完全に削除されました。
  6. 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()でキャストされるようになりました。
  7. doc/go1.html および doc/go1.tmpl:
    • Go 1のリリースノートに、Execの削除、ShellExpandのリネーム、NewFile/File.Fdの型変更に関する記述が追加されました。

コアとなるコードの解説

src/pkg/os/file_unix.gosrc/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.FileFd()メソッドが返す値と、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言語の公式ドキュメント (os, syscall, os/execパッケージ)
  • Go言語のIssueトラッカー (特に #2947)
  • Unix系OSのファイルディスクリプタに関する資料
  • Windowsのファイルハンドルに関する資料
  • uintptrに関するGo言語の仕様や解説記事