[インデックス 11783] ファイルの概要
このコミットは、Go言語の標準ライブラリにおけるos
およびsyscall
パッケージの変更に関するものです。特に、Windowsビルドの修正に焦点を当てており、syscall.ProcAttr.Files
フィールドの型を[]int
(またはWindows固有の[]Handle
)から[]uintptr
に変更することで、プロセス生成時のファイルディスクリプタ(Windowsではハンドル)の扱いを改善しています。
コミット
commit fbab6d8512c876dcef65e85f7a400117bc1f08f3
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Sat Feb 11 08:47:19 2012 +1100
os,syscall: fix windows build
make syscall.ProcAttr.Files be []uintptr
all.bash passes on Linux.
things seem to compile on GOOS={darwin,windows}
R=golang-dev, mattn.jp, alex.brainman, rsc
CC=golang-dev
https://golang.org/cl/5653055
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/fbab6d8512c876dcef65e85f7a400117bc1f08f3
元コミット内容
このコミットの主な目的は、Go言語のWindowsビルドにおける問題を修正することです。具体的には、syscall.ProcAttr
構造体のFiles
フィールドのデータ型を[]uintptr
に変更することで、プロセス生成時に子プロセスに渡されるファイルディスクリプタ(Windowsではハンドル)の取り扱いを改善しています。コミットメッセージには、Linuxでのall.bash
テストの成功と、Darwin(macOS)およびWindowsでのコンパイルが確認されたことが記されています。
変更の背景
Go言語はクロスプラットフォーム対応を重視しており、異なるオペレーティングシステム(OS)間で一貫した動作を提供することを目指しています。しかし、OSによってはファイルディスクリプタやプロセスハンドルといった低レベルのリソースの表現方法が異なります。
Windowsでは、ファイルやI/Oオブジェクトは「ハンドル」と呼ばれる抽象的な識別子で管理されます。これらのハンドルは通常、ポインタサイズに相当するuintptr_t
のような型で表現されます。一方、Unix系OSでは、ファイルディスクリプタは通常int
型の整数で表現されます。
Goのsyscall.ProcAttr
構造体は、新しいプロセスを起動する際にそのプロセスに適用される属性(作業ディレクトリ、環境変数、そして継承されるファイルディスクリプタなど)を定義します。以前のバージョンでは、このFiles
フィールドが[]int
型(またはWindowsでは[]Handle
型)として定義されていましたが、これがWindows環境でのビルドや実行時に問題を引き起こす可能性がありました。特に、int
型がWindowsのハンドルを完全に表現できない場合(例えば、int
が32ビットでハンドルが64ビットの場合など)に、データの切り捨てや不正な値の伝達が発生するリスクがありました。
このコミットは、このようなクロスプラットフォーム間の型の不一致に起因する問題を解決し、特にWindows環境でのos/exec
パッケージの安定性と正確性を確保するために行われました。
前提知識の解説
- ファイルディスクリプタ (File Descriptor / FD): Unix系OSにおいて、開かれたファイルやソケット、パイプなどのI/Oリソースを識別するためにカーネルがプロセスに割り当てる非負の整数です。
- ハンドル (Handle): Windows OSにおいて、ファイル、レジストリキー、プロセス、スレッドなどのシステムオブジェクトを識別するために使用される抽象的な識別子です。多くの場合、ポインタとして扱われるため、
void*
やuintptr_t
のような型で表現されます。 syscall
パッケージ: Go言語の標準ライブラリの一部で、OS固有の低レベルなシステムコールへのアクセスを提供します。これにより、GoプログラムがOSの機能と直接対話できるようになります。os/exec
パッケージ: Go言語で外部コマンドを実行するためのパッケージです。このパッケージは内部的にsyscall
パッケージを利用して、新しいプロセスの生成やI/Oのリダイレクトなどを行います。ProcAttr
構造体:syscall
パッケージ内で定義される構造体で、StartProcess
関数などを用いて新しいプロセスを起動する際に、そのプロセスの属性(環境変数、作業ディレクトリ、継承するファイルディスクリプタなど)を設定するために使用されます。uintptr
型: Go言語の組み込み型の一つで、ポインタのビットパターンを保持するのに十分な大きさの符号なし整数型です。これは、GoのポインタとC言語のポインタ(またはOSのハンドル)の間で値を変換する際に、型安全性を保ちつつ低レベルな操作を行うために使用されます。unsafe.Pointer
と組み合わせて使用されることが多いですが、このコミットでは直接uintptr
が使われています。
技術的詳細
このコミットの核心は、syscall.ProcAttr
構造体のFiles
フィールドの型を[]int
(またはWindowsの[]Handle
)から[]uintptr
に変更した点にあります。
-
uintptr
の採用理由:- クロスプラットフォーム互換性:
uintptr
はポインタのサイズを保持できるため、Windowsのハンドル(通常はポインタサイズ)とUnix系のファイルディスクリプタ(整数)の両方を安全に表現できます。これにより、異なるOS間での型の不一致による問題を回避し、より堅牢なクロスプラットフォームコードを実現します。 - Windowsハンドルの正確な表現: Windowsのハンドルは、
int
型では表現しきれない場合があるため、uintptr
を使用することで、ハンドルの値が正確に保持され、切り捨てや誤った解釈を防ぎます。 - 低レベルAPIとの整合性:
syscall
パッケージはOSの低レベルAPIと直接対話するため、OSが期待する型(ポインタやハンドル)に合わせたuintptr
の使用は、API呼び出しの正確性を高めます。
- クロスプラットフォーム互換性:
-
変更の影響範囲:
syscall.ProcAttr
の定義が変更されたため、この構造体を使用しているすべての箇所で、Files
フィールドへのアクセスや値の代入方法が調整されました。- 特に、
os/exec
パッケージ内のプロセス起動ロジック(exec_posix.go
、exec_bsd.go
、exec_linux.go
、exec_windows.go
)では、f.Fd()
から取得したファイルディスクリプタ(またはハンドル)をuintptr
型にキャストしてsysattr.Files
に追加する変更が行われました。 os/file_windows.go
では、NewFile
関数を呼び出す際に、ファイルディスクリプタをuintptr
にキャストして渡すように変更されています。src/pkg/net/sendfile_windows.go
では、f.Fd()
から取得した値をsyscall.Handle
にキャストしてo.src
に代入する箇所が、syscall.Handle
がuintptr
のエイリアスであるため、この変更の影響を受けています。src/pkg/os/exec/exec_test.go
では、テストコード内でファイルディスクリプタを閉じる際に、直接syscall.Close(fd)
を呼び出すのではなく、os.NewFile(fd, "").Close()
を使用するように変更されました。これは、fd
がuintptr
型になったことに対応し、os
パッケージのより高レベルな抽象化を通じて安全にリソースを解放するための変更と考えられます。
この変更により、Goのプロセス管理機能がWindows環境でより安定し、正確に動作するようになりました。
コアとなるコードの変更箇所
このコミットにおける主要なコード変更は、syscall.ProcAttr
構造体のFiles
フィールドの型定義と、それに関連するファイルディスクリプタの型変換です。
1. src/pkg/syscall/exec_unix.go
(および src/pkg/syscall/exec_windows.go
)
--- a/src/pkg/syscall/exec_unix.go
+++ b/src/pkg/syscall/exec_unix.go
@@ -101,9 +101,9 @@ type Credential struct {
// ProcAttr holds attributes that will be applied to a new process started
// by StartProcess.
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
}
この変更は、syscall.ProcAttr
構造体のFiles
フィールドの型を[]int
から[]uintptr
に変更しています。Windows版のexec_windows.go
でも同様に[]Handle
から[]uintptr
に変更されています。
2. src/pkg/os/exec_posix.go
(および src/pkg/syscall/exec_bsd.go
, src/pkg/syscall/exec_linux.go
)
--- a/src/pkg/os/exec_posix.go
+++ b/src/pkg/os/exec_posix.go
@@ -38,7 +38,7 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
sysattr.Env = Environ()
}
for _, f := range attr.Files {
- sysattr.Files = append(sysattr.Files, int(f.Fd()))
+ sysattr.Files = append(sysattr.Files, f.Fd())
}
pid, h, e := syscall.StartProcess(name, argv, sysattr)
attr.Files
が[]uintptr
になったため、f.Fd()
の戻り値(uintptr
)をint
にキャストする必要がなくなりました。syscall/exec_bsd.go
とsyscall/exec_linux.go
では、attr.Files
からfd
スライスを生成する際に、int(ufd)
と明示的にキャストするループが追加されています。これは、内部的な処理でint
型のファイルディスクリプタが必要なためです。
3. src/pkg/os/file_windows.go
--- a/src/pkg/os/file_windows.go
+++ b/src/pkg/os/file_windows.go
@@ -70,7 +70,7 @@ func openFile(name string, flag int, perm FileMode) (file *File, err error) {
syscall.CloseOnExec(r)
}
- return NewFile(r, name), nil
+ return NewFile(uintptr(r), name), nil
}
func openDir(name string) (file *File, err error) {
@@ -79,7 +79,7 @@ func openDir(name string) (file *File, err error) {
if e != nil {
return nil, &PathError{"open", name, e}
}
- f := NewFile(r, name)
+ f := NewFile(uintptr(r), name)
f.dirinfo = d
return f, nil
}
@@ -313,7 +313,7 @@ func Pipe() (r *File, w *File, err error) {
syscall.CloseOnExec(p[1])
syscall.ForkLock.RUnlock()
- return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil
+ return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil
}
Windows固有のファイル操作関数において、syscall
パッケージから返されるハンドル(uintptr
型)をos.NewFile
に渡す際に、明示的にuintptr
にキャストしています。
4. src/pkg/os/exec/exec_test.go
--- a/src/pkg/os/exec/exec_test.go
+++ b/src/pkg/os/exec/exec_test.go
@@ -17,7 +17,6 @@ import (
"runtime"
"strconv"
"strings"
- "syscall"
"testing"
)
@@ -153,8 +152,8 @@ func TestExtraFiles(t *testing.T) {
// Ensure that file descriptors have not already been leaked into
// our environment.
- for fd := int(os.Stderr.Fd()) + 1; fd <= 101; fd++ {
- err := syscall.Close(fd)
+ for fd := os.Stderr.Fd() + 1; fd <= 101; fd++ {
+ err := os.NewFile(fd, "").Close()
if err == nil {
t.Logf("Something already leaked - closed fd %d", fd)
}
テストコード内でファイルディスクリプタを閉じるロジックが変更されました。syscall.Close(fd)
の直接呼び出しから、os.NewFile(fd, "").Close()
というos
パッケージの抽象化を通じた方法に変更されています。これは、fd
がuintptr
型になったことへの対応と、よりGoらしいリソース管理の方法への移行を示唆しています。
コアとなるコードの解説
このコミットの主要な変更は、syscall.ProcAttr.Files
の型を[]int
から[]uintptr
に変更したことです。
-
syscall.ProcAttr.Files []uintptr
:syscall.ProcAttr
は、新しいプロセスを起動する際にそのプロセスに渡す属性を定義します。Files
フィールドは、子プロセスが継承するファイルディスクリプタ(またはWindowsのハンドル)のリストを保持します。uintptr
型は、ポインタのビットパターンを保持できる符号なし整数型であり、OSの低レベルAPIが扱うハンドルやポインタを安全に表現するのに適しています。- この変更により、特にWindows環境で、
int
型では表現しきれない可能性のあるハンドル値を正確に扱うことができるようになり、クロスプラットフォームでの互換性と堅牢性が向上しました。
-
f.Fd()
の戻り値と型変換:os.File
のFd()
メソッドは、基となるファイルディスクリプタ(またはハンドル)をuintptr
型で返します。- 以前は、この
uintptr
をint
にキャストしてsyscall.ProcAttr.Files
([]int
)に追加していましたが、Files
が[]uintptr
になったことで、この明示的なint
へのキャストが不要になりました。これにより、コードがよりシンプルになり、潜在的な型変換エラーが減少します。 - ただし、Unix系の
syscall/exec_bsd.go
やsyscall/exec_linux.go
では、内部的にint
型のファイルディスクリプタが必要な処理があるため、uintptr
からint
への明示的なキャストが引き続き行われています。これは、uintptr
が汎用的なハンドル型として使われつつも、特定のOSのシステムコールが期待する具体的な整数型に変換する必要があるためです。
-
os.NewFile(fd, "").Close()
への変更:exec_test.go
におけるこの変更は、テストのクリーンアップ処理に関連しています。以前はsyscall.Close(fd)
を直接呼び出していましたが、fd
がuintptr
型になったことと、よりGoらしいリソース管理の観点から変更されました。os.NewFile(fd, "")
は、与えられたファイルディスクリプタ(uintptr
)から*os.File
オブジェクトを作成します。- その
*os.File
オブジェクトに対してClose()
メソッドを呼び出すことで、Goのos
パッケージが提供する抽象化を通じて、ファイルディスクリプタが適切に閉じられます。これにより、OS固有のsyscall.Close
を直接呼び出すよりも、より安全で移植性の高い方法でリソースを解放できます。
これらの変更は、Go言語が低レベルなOSインタラクションを扱う際の堅牢性とクロスプラットフォーム互換性を高める上で重要なステップでした。
関連リンク
- Go言語の
syscall
パッケージドキュメント: https://pkg.go.dev/syscall - Go言語の
os/exec
パッケージドキュメント: https://pkg.go.dev/os/exec - Go言語の
uintptr
に関する議論(Goの設計原則など): Goの公式ドキュメントやブログ記事でuintptr
とunsafe.Pointer
に関する詳細な説明が見つかることがあります。
参考にした情報源リンク
- Goのコミット履歴: https://github.com/golang/go/commits/master
- Goのコードレビューシステム (Gerrit): https://go-review.googlesource.com/ (コミットメッセージに記載されている
https://golang.org/cl/5653055
は、このGerritの変更リストへのリンクです。) - Windows APIのハンドルに関するMicrosoftのドキュメント (例:
HANDLE
型): https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types - Unix系OSのファイルディスクリプタに関する一般的な情報 (例:
man 2 open
,man 2 close
) - Go言語の
uintptr
とunsafe.Pointer
に関する解説記事やブログ(一般的なプログラミング知識として)