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

[インデックス 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環境におけるシステムコールインターフェースとの整合性を図るための重要な変更です。

変更された主な箇所は以下の通りです。

  1. 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に明示的にキャストして渡すように変更されました。
  2. 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型のファイルディスクリプタを比較する際に、reservedFdintにキャストして比較するように変更されました。

これらの変更により、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におけるファイルディスクリプタの関連性を理解するために使用されました。)