[インデックス 11972] ファイルの概要
このコミットは、Go言語のos
およびsyscall
パッケージにおけるPlan 9オペレーティングシステム向けのビルド修正に関するものです。具体的には、ファイルディスクリプタの型をint
からuintptr
に変更し、syscall.ProcAttr.Files
フィールドも[]int
から[]uintptr
に修正することで、Plan 9環境でのプロセス起動とファイル操作の互換性と正確性を向上させています。
コミット
- Author: David du Colombier 0intro@gmail.com
- Date: Thu Feb 16 14:04:51 2012 -0500
- Commit Message:
os,syscall: fix plan 9 build NewFile take uintptr make syscall.ProcAttr.Files be []uintptr R=rsc CC=golang-dev https://golang.org/cl/5656073
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/11f4a6c9df1616da9abe4f4ad1c2f41b4cf33fbf
元コミット内容
os,syscall: fix plan 9 build
NewFile take uintptr
make syscall.ProcAttr.Files be []uintptr
R=rsc
CC=golang-dev
https://golang.org/cl/5656073
変更の背景
この変更の背景には、Go言語がサポートする様々なオペレーティングシステム(OS)間での互換性の確保と、各OSのシステムコールインターフェースへの正確な対応があります。特にPlan 9というOSは、Unix系OSとは異なる設計思想を持つため、ファイルディスクリプタのような基本的な概念の扱いにも差異が生じることがあります。
従来のGoのコードでは、ファイルディスクリプタを一般的なUnix系OSで用いられるint
型として扱っていました。しかし、Plan 9環境においては、ファイルディスクリプタやその他のシステムハンドルがuintptr
型として表現される方が適切であるか、あるいはそのように扱われることを期待するAPIが存在した可能性があります。
この不一致が原因で、Plan 9上でのGoプログラムのビルドエラーや実行時エラーが発生していたと考えられます。具体的には、os
パッケージのNewFile
関数がファイルディスクリプタを受け取る際や、syscall
パッケージのProcAttr
構造体が子プロセスに渡すファイルディスクリプタのリストを保持する際に、型の不整合が生じていたと推測されます。
このコミットは、これらの型不整合を解消し、Plan 9環境でのGoの安定した動作とビルドを保証することを目的としています。
前提知識の解説
Plan 9 from Bell Labs
Plan 9 from Bell Labsは、ベル研究所で開発された分散オペレーティングシステムです。Unixの設計思想をさらに推し進め、すべてのリソース(ファイル、デバイス、ネットワーク接続など)をファイルシステムとして表現するという「すべてはファイルである」という原則を徹底しています。これにより、システム全体が統一されたインターフェースでアクセス可能となり、分散コンピューティング環境での柔軟なリソース共有を可能にしています。
Plan 9のシステムコールやAPIは、Unix系OSとは異なる独自の規約を持つことが多く、特にファイルディスクリプタのようなシステムリソースの識別子についても、その表現方法や型が異なる場合があります。
uintptr
型 (Go言語)
Go言語におけるuintptr
型は、符号なし整数型であり、ポインタのビットパターンを保持するのに十分な大きさを持つことが保証されています。これは、ポインタを整数として扱う必要がある場合(例えば、C言語のポインタとGoのポインタの間で変換を行う場合や、低レベルのシステムプログラミングでメモリアドレスを直接操作する場合)に用いられます。
uintptr
は、ポインタ演算を直接行うためのものではなく、ガベージコレクタによって追跡されないため、誤った使用はメモリリークやクラッシュの原因となる可能性があります。しかし、システムコールやOS固有のAPIとのインターフェースにおいては、OSが提供するハンドルや識別子が整数値として表現される場合に、その値をGoの型システムに適合させるために使用されることがあります。
ファイルディスクリプタ (File Descriptor)
ファイルディスクリプタは、オペレーティングシステムがプロセスに割り当てる、開かれたファイルやI/Oリソース(ソケット、パイプなど)を一意に識別するための抽象的なハンドルです。Unix系OSでは通常、非負の整数値(int
型)として表現されます。プロセスはファイルディスクリプタを通じて、対応するファイルやリソースに対して読み書きなどの操作を行います。
syscall
パッケージ (Go言語)
Go言語の標準ライブラリに含まれるsyscall
パッケージは、低レベルのオペレーティングシステムプリミティブへのアクセスを提供します。これには、システムコール(ファイル操作、プロセス管理、ネットワーク通信など)の呼び出しや、OS固有のデータ構造の定義が含まれます。syscall
パッケージは、GoプログラムがOSの機能と直接対話するためのブリッジとして機能し、OSに依存するコードを記述する際に利用されます。
os
パッケージ (Go言語)
Go言語の標準ライブラリに含まれるos
パッケージは、オペレーティングシステムに依存しないインターフェースを提供し、ファイルシステム操作、プロセス管理、環境変数へのアクセスなど、OSの基本的な機能を利用するための高レベルなAPIを提供します。syscall
パッケージが低レベルなOSインターフェースを提供するのに対し、os
パッケージはより抽象化された、一般的な操作を提供します。
技術的詳細
このコミットの核心は、ファイルディスクリプタの表現型をint
からuintptr
に変更することにあります。これは、特にPlan 9環境におけるシステムコールインターフェースとの整合性を図るための重要な変更です。
変更された主な箇所は以下の通りです。
-
src/pkg/os/file_plan9.go
:File.Fd()
メソッドの戻り値の型がint
からuintptr
に変更されました。これにより、File
オブジェクトが持つファイルディスクリプタをPlan 9のシステムが期待するuintptr
型として返すようになります。NewFile
関数の引数fd
の型がint
からuintptr
に変更されました。これは、外部からファイルディスクリプタを受け取ってFile
オブジェクトを生成する際に、uintptr
型を直接受け入れることを意味します。関数内部では、このuintptr
値をint
にキャストして内部のfile.fd
フィールドに格納しています。OpenFile
関数やPipe
関数など、ファイルディスクリプタを生成してNewFile
に渡す箇所で、int
型のファイルディスクリプタをuintptr
に明示的にキャストして渡すように変更されました。
-
src/pkg/syscall/exec_plan9.go
:ProcAttr
構造体のFiles
フィールドの型が[]int
から[]uintptr
に変更されました。ProcAttr
は、子プロセスを起動する際にそのプロセスに渡すファイルディスクリプタのリストを保持します。この変更により、Plan 9のシステムコールが期待するuintptr
型の配列としてファイルディスクリプタを渡せるようになります。forkAndExecInChild
関数内で、attr.Files
([]uintptr
型)からfd
([]int
型)を生成するループが追加されました。これは、syscall.StartProcess
などの低レベルなシステムコールが依然としてint
型のファイルディスクリプタを期待する場合があるため、uintptr
からint
への変換が必要となるためです。forkExec
関数内で、attr.Files
に含まれるuintptr
型のファイルディスクリプタと、既存のint
型のファイルディスクリプタを比較する際に、reservedFd
をint
にキャストして比較するように変更されました。
これらの変更により、GoのランタイムがPlan 9のシステムコールとより正確に連携できるようになり、ファイルディスクリプタの受け渡しにおける型の不整合が解消されます。
コアとなるコードの変更箇所
src/pkg/os/exec_plan9.go
--- a/src/pkg/os/exec_plan9.go
+++ b/src/pkg/os/exec_plan9.go
@@ -20,18 +20,10 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
Sys: attr.Sys,
}
- // Create array of integer (system) fds.
- intfd := make([]int, len(attr.Files))
- for i, f := range attr.Files {
- if f == nil {
- intfd[i] = -1
- } else {
- intfd[i] = f.Fd()
- }
+ for _, f := range attr.Files {
+ sysattr.Files = append(sysattr.Files, f.Fd())
}
- sysattr.Files = intfd
-
pid, h, e := syscall.StartProcess(name, argv, sysattr)
if e != nil {
return nil, &PathError{"fork/exec", name, e}
src/pkg/os/file_plan9.go
--- a/src/pkg/os/file_plan9.go
+++ b/src/pkg/os/file_plan9.go
@@ -26,19 +26,20 @@ type file struct {
}
// Fd returns the integer Unix file descriptor referencing the open file.
-func (file *File) Fd() int {
- if file == nil {
- return -1
+func (f *File) Fd() uintptr {
+ if f == nil {
+ return ^(uintptr(0))
}
- return file.fd
+ return uintptr(f.fd)
}
// NewFile returns a new File with the given file descriptor and name.
-func NewFile(fd int, name string) *File {
- if fd < 0 {
+func NewFile(fd uintptr, name string) *File {
+ fdi := int(fd)
+ if fdi < 0 {
return nil
}
- f := &File{&file{fd: fd, name: name}}
+ f := &File{&file{fd: fdi, name: name}}
runtime.SetFinalizer(f.file, (*file).close)
return f
}
@@ -128,7 +129,7 @@ func OpenFile(name string, flag int, perm FileMode) (file *File, err error) {
}
}
- return NewFile(fd, name), nil
+ return NewFile(uintptr(fd), name), nil
}
// Close closes the File, rendering it unusable for I/O.
@@ -330,7 +331,7 @@ func Pipe() (r *File, w *File, err error) {
}\n \tsyscall.ForkLock.RUnlock()\n \n-\treturn NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil
+\treturn NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil
}\n \n // not supported on Plan 9
src/pkg/syscall/exec_plan9.go
--- a/src/pkg/syscall/exec_plan9.go
+++ b/src/pkg/syscall/exec_plan9.go
@@ -182,7 +182,10 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at
)
// guard against side effects of shuffling fds below.
- fd := append([]int(nil), attr.Files...)
+ fd := make([]int, len(attr.Files))
+ for i, ufd := range attr.Files {
+ fd[i] = int(ufd)
+ }
if envv != nil {
clearenv = RFCENVG
@@ -338,9 +341,9 @@ type envItem struct {
}
type ProcAttr struct {
- Dir string // Current working directory.
- Env []string // Environment.
- Files []int // File descriptors.
+ Dir string // Current working directory.
+ Env []string // Environment.
+ Files []uintptr // File descriptors.
Sys *SysProcAttr
}
@@ -423,7 +426,7 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)\n for _, fd := range openFds {\n isReserved := false\n for _, reservedFd := range attr.Files {\n- if fd == reservedFd {\n+ if fd == int(reservedFd) {\n isReserved = true\n break\n }\n```
## コアとなるコードの解説
### `src/pkg/os/exec_plan9.go` の変更
このファイルでは、`os.StartProcess`関数内で子プロセスに渡すファイルディスクリプタの処理が変更されています。
変更前は、`attr.Files`(`*os.File`のリスト)から`int`型のファイルディスクリプタの配列`intfd`を生成していました。
変更後は、`attr.Files`の各`*os.File`から`f.Fd()`を呼び出して`uintptr`型のファイルディスクリプタを取得し、それを直接`sysattr.Files`(`[]uintptr`型)に追加しています。
これにより、`syscall.StartProcess`に渡されるファイルディスクリプタのリストが、Plan 9が期待する`uintptr`型で統一されるようになりました。
### `src/pkg/os/file_plan9.go` の変更
このファイルは、Goの`os`パッケージにおけるPlan 9固有のファイル操作の実装を含んでいます。
* **`File.Fd()` メソッド**:
* 変更前は`int`を返していましたが、変更後は`uintptr`を返すようになりました。これは、`File`オブジェクトが保持するファイルディスクリプタを、Plan 9のシステムコールが期待する`uintptr`型として公開するためです。
* `file == nil`の場合の戻り値も、`int`の`-1`から`uintptr`の`^(uintptr(0))`(すべてのビットが1、つまり最大値)に変更されています。これは、`uintptr`における無効な値の表現として適切です。
* **`NewFile` 関数**:
* 変更前は`int`型の`fd`を受け取っていましたが、変更後は`uintptr`型の`fd`を受け取るようになりました。
* 関数内部では、受け取った`uintptr`型の`fd`を`int(fd)`として`int`型にキャストし、内部の`file.fd`フィールド(これは`int`型)に格納しています。これは、Goの内部的なファイルディスクリプタの表現が依然として`int`であるため、外部インターフェースと内部実装の間の変換を行っていることを示しています。
* **`OpenFile` および `Pipe` 関数**:
* これらの関数は、ファイルディスクリプタを生成した後、`NewFile`関数を呼び出して`*os.File`オブジェクトを生成します。
* 変更前は`int`型のファイルディスクリプタをそのまま`NewFile`に渡していましたが、変更後は`uintptr(fd)`のように明示的に`uintptr`にキャストしてから`NewFile`に渡すようになりました。これは、`NewFile`関数のシグネチャ変更に対応したものです。
### `src/pkg/syscall/exec_plan9.go` の変更
このファイルは、Goの`syscall`パッケージにおけるPlan 9固有のプロセス実行(`exec`)の実装を含んでいます。
* **`ProcAttr` 構造体**:
* `Files`フィールドの型が`[]int`から`[]uintptr`に変更されました。これにより、子プロセスに渡すファイルディスクリプタのリストが、Plan 9のシステムコールが期待する`uintptr`型のスライスとして保持されるようになります。
* **`forkAndExecInChild` 関数**:
* この関数は、子プロセスをフォークして実行する際の低レベルな処理を行います。
* 変更前は`attr.Files`(`[]int`)を直接`fd`(`[]int`)にコピーしていましたが、変更後は`attr.Files`が`[]uintptr`になったため、ループを使って各`uintptr`を`int`にキャストして`fd`(`[]int`)に格納するように変更されました。これは、この関数が呼び出すさらに低レベルなシステムコールが、依然として`int`型のファイルディスクリプタを期待している可能性があるためです。
* **`forkExec` 関数**:
* この関数は、既存のファイルディスクリプタと`attr.Files`に含まれる予約されたファイルディスクリプタを比較するロジックを含んでいます。
* 変更前は`fd == reservedFd`のように直接比較していましたが、`reservedFd`が`uintptr`型になったため、`fd == int(reservedFd)`のように`reservedFd`を`int`にキャストしてから比較するように変更されました。これにより、型の一貫性が保たれます。
これらの変更は、GoのPlan 9ポートが、Plan 9のシステムコールインターフェースとより正確に整合するようにするためのものであり、特にファイルディスクリプタの型表現に関するOS固有の要件に対応しています。
## 関連リンク
* Go言語の`os`パッケージドキュメント: [https://pkg.go.dev/os](https://pkg.go.dev/os)
* Go言語の`syscall`パッケージドキュメント: [https://pkg.go.dev/syscall](https://pkg.go.dev/syscall)
* Go言語の`uintptr`型に関するドキュメント (Go言語仕様): [https://go.dev/ref/spec#Numeric_types](https://go.dev/ref/spec#Numeric_types)
* Plan 9 from Bell Labs 公式サイト: [https://9p.io/plan9/](https://9p.io/plan9/)
## 参考にした情報源リンク
* Go言語の公式ドキュメント
* Plan 9に関する一般的な情報源
* `uintptr`のGo言語における役割に関する技術記事 (一般的な知識に基づくため特定のURLはなし)
* ファイルディスクリプタに関する一般的なオペレーティングシステム概念 (一般的な知識に基づくため特定のURLはなし)
* Goのソースコード(コミット差分)
* Goのコードレビューシステム (Gerrit) の変更リスト: [https://golang.org/cl/5656073](https://golang.org/cl/5656073) (コミットメッセージに記載)
* Google検索: "Go uintptr file descriptor", "Go Plan 9 file descriptor", "Plan 9 file descriptor type", "Go syscall.ProcAttr.Files uintptr" (これらの検索クエリは、`uintptr`とPlan 9におけるファイルディスクリプタの関連性を理解するために使用されました。)