[インデックス 13326] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにおけるWindows固有の実装において、GetCurrentProcessId
関数をエクスポート(外部からアクセス可能)しないように変更するものです。具体的には、関数名をGetCurrentProcessId
からgetCurrentProcessId
へと変更し、Goの命名規則に従って非エクスポート関数とすることで、内部的な利用に限定します。これは、以前のコミット(CL 5909043)で誤ってエクスポートされてしまったGetpid
の実装に関連する問題を修正するためのものです。
コミット
- コミットハッシュ:
7762a9dc1fea7ac450d5a68d9d9649d62f58cf38
- 作者: Russ Cox rsc@golang.org
- 日付: Fri Jun 8 14:28:29 2012 -0400
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/7762a9dc1fea7ac450d5a68d9d9649d62f58cf38
元コミット内容
syscall: unexport GetCurrentProcessId on Windows
This slipped in with the implementation of Getpid in CL 5909043.
I'd exclude that CL entirely but it is tangled up in the Win32finddata changes.
R=golang-dev, minux.ma
CC=golang-dev
https://golang.org/cl/6297065
変更の背景
この変更の背景には、Go言語の標準ライブラリであるsyscall
パッケージにおけるWindows固有のプロセスID取得に関する設計上の意図と、過去のコミットにおける偶発的なエクスポートがあります。
Go言語では、パッケージ内の関数や変数の名前が大文字で始まる場合、それはそのパッケージの外部からアクセス可能な「エクスポートされた」エンティティとなります。一方、小文字で始まる場合は、そのパッケージ内でのみ利用可能な「非エクスポートされた」(または内部的な)エンティティとなります。
以前のコミット(CL 5909043)で、Windows環境におけるプロセスIDを取得するためのGetpid
関数の実装が行われました。この際、Windows APIのGetCurrentProcessId
関数をGoのsyscall
パッケージ内でラップする形で導入されました。しかし、このラッパー関数が誤ってGetCurrentProcessId
という名前でエクスポートされてしまい、パッケージの外部から直接アクセス可能な状態になっていました。
Goの設計思想として、低レベルなシステムコールを直接エクスポートすることは、通常は避けるべきとされています。これは、システムコールはOS固有の挙動に依存し、プラットフォーム間の互換性を損なう可能性があるためです。代わりに、os
パッケージのような高レベルな抽象化レイヤーを通じて、プラットフォームに依存しない形で機能を提供することが推奨されます。Getpid
関数はos
パッケージで提供されるべきであり、syscall
パッケージの内部実装が外部に漏れるべきではありませんでした。
コミットメッセージによると、作者はCL 5909043全体を元に戻すことも検討したようですが、そのコミットがWin32finddata
(Windowsのファイル検索データ構造)に関する変更と絡み合っていたため、部分的な修正としてGetCurrentProcessId
の非エクスポートを選択しました。この変更は、syscall
パッケージの内部実装を適切にカプセル化し、Goの設計原則に沿ったものにするための修正です。
前提知識の解説
Go言語のパッケージとエクスポート/非エクスポート
Go言語では、コードは「パッケージ」という単位で整理されます。パッケージは、関連する機能やデータ型をまとめたものです。Goの命名規則には、エクスポート(公開)と非エクスポート(非公開)の概念があります。
- エクスポートされた識別子: 関数、変数、型、定数などの名前が大文字で始まる場合、その識別子はパッケージの外部からアクセス可能です。これは、他のパッケージからその機能を利用できることを意味します。
- 非エクスポートされた識別子: 名前が小文字で始まる場合、その識別子はパッケージの内部でのみアクセス可能です。これは、その機能がパッケージの実装詳細であり、外部に公開すべきではないことを示します。
このコミットでは、GetCurrentProcessId
という大文字で始まる関数名をgetCurrentProcessId
という小文字で始まる名前に変更することで、この関数を非エクスポート化しています。
syscall
パッケージ
syscall
パッケージは、Goプログラムからオペレーティングシステム(OS)の低レベルなシステムコールにアクセスするための機能を提供します。これには、ファイル操作、プロセス管理、ネットワーク通信など、OSが提供する基本的なサービスが含まれます。
syscall
パッケージは、OS固有の機能に直接アクセスするため、プラットフォームに依存するコードが多く含まれます。そのため、通常はアプリケーション開発者が直接利用するよりも、os
やnet
といったより高レベルな標準ライブラリが内部的に利用することが多いです。
Windows API GetCurrentProcessId
GetCurrentProcessId
は、Windowsオペレーティングシステムが提供するAPI(Application Programming Interface)関数の一つです。この関数は、現在実行中のプロセスのプロセス識別子(PID)を取得するために使用されます。PIDは、OSがプロセスを一意に識別するために割り当てる数値です。
Goのsyscall
パッケージは、このようなOSネイティブのAPIをGoの関数としてラップし、Goプログラムから呼び出せるようにしています。
Getpid
関数
Getpid
関数は、Go言語において現在のプロセスのプロセスIDを取得するための標準的な関数です。Goの標準ライブラリでは、os
パッケージにos.Getpid()
として提供されています。この関数は、内部的に各OSのシステムコール(WindowsではGetCurrentProcessId
、Unix系ではgetpid
など)を呼び出して実装されています。
このコミットの文脈では、syscall
パッケージ内のGetpid
関数が、内部的にGetCurrentProcessId
(変更後はgetCurrentProcessId
)を呼び出すように実装されています。
技術的詳細
このコミットの技術的な核心は、Goのsyscall
パッケージにおけるWindows固有のプロセスID取得関数の可視性(エクスポート状態)の変更にあります。
変更は主に以下の3つのファイルで行われています。
src/pkg/syscall/syscall_windows.go
src/pkg/syscall/zsyscall_windows_386.go
(32ビットWindowsアーキテクチャ用)src/pkg/syscall/zsyscall_windows_amd64.go
(64ビットWindowsアーキテクチャ用)
これらのファイルは、Goのsyscall
パッケージがWindows APIを呼び出すための定義やラッパー関数を含んでいます。
変更内容の具体例
-
syscall_windows.go
://sys GetCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId
この行は、Goのsyscall
パッケージがWindowsのkernel32.dll
にあるGetCurrentProcessId
関数をどのようにインポートするかを定義しています。この行が//sys getCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId
に変更され、Go側の関数名が小文字始まりになりました。func Getpid() (pid int) { return int(GetCurrentProcessId()) }
この行は、GoのGetpid
関数が、先ほど定義されたGetCurrentProcessId
(変更後はgetCurrentProcessId
)を呼び出すことを示しています。この行もfunc Getpid() (pid int) { return int(getCurrentProcessId()) }
に変更され、内部的な呼び出し先が非エクスポート関数になりました。
-
zsyscall_windows_386.go
およびzsyscall_windows_amd64.go
: これらのファイルは、Goのツールチェーンによって自動生成されるファイルで、syscall_windows.go
で定義された//sys
ディレクティブに基づいて、実際のシステムコール呼び出しを行うためのGoコードが含まれています。func GetCurrentProcessId() (pid uint32) { ... }
この関数定義がfunc getCurrentProcessId() (pid uint32) { ... }
に変更されました。これにより、この関数はGoの命名規則に従って非エクスポートとなり、syscall
パッケージの外部からは直接呼び出せなくなります。
変更の意図
この変更の主な意図は、GetCurrentProcessId
という低レベルなWindows APIのラッパー関数を、Goのsyscall
パッケージの内部実装に限定することです。これにより、以下の目的が達成されます。
- カプセル化の強化:
syscall
パッケージの内部詳細が外部に漏れるのを防ぎます。これにより、パッケージのAPIがよりクリーンになり、将来的な内部実装の変更が外部のコードに影響を与えるリスクが減少します。 - Goの設計原則への準拠: Goでは、プラットフォームに依存する低レベルな機能は、通常、より高レベルでプラットフォーム非依存な抽象化(例:
os
パッケージ)を通じて提供されるべきです。GetCurrentProcessId
を非エクスポートにすることで、この原則に沿った形になります。ユーザーはos.Getpid()
を使用すべきであり、syscall.GetCurrentProcessId()
を直接呼び出すべきではありません。 - 偶発的な利用の防止:
GetCurrentProcessId
がエクスポートされていると、開発者が誤ってこの低レベル関数を直接利用してしまう可能性があります。非エクスポートにすることで、このような誤用を防ぎ、os.Getpid()
のような適切な高レベルAPIの利用を促します。
この修正は、Goの標準ライブラリの堅牢性と一貫性を保つ上で重要な、細かではあるが意味のある変更と言えます。
コアとなるコードの変更箇所
src/pkg/syscall/syscall_windows.go
--- a/src/pkg/syscall/syscall_windows.go
+++ b/src/pkg/syscall/syscall_windows.go
@@ -201,7 +201,7 @@ func NewCallback(fn interface{}) uintptr
//sys RegQueryInfoKey(key Handle, class *uint16, classLen *uint32, reserved *uint32, subkeysLen *uint32, maxSubkeyLen *uint32, maxClassLen *uint32, valuesLen *uint32, maxValueNameLen *uint32, maxValueLen *uint32, saLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegQueryInfoKeyW
//sys RegEnumKeyEx(key Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, classLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegEnumKeyExW
//sys RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegQueryValueExW
-//sys GetCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId
+//sys getCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId
// syscall interface implementation for other packages
@@ -702,7 +702,7 @@ func SetsockoptIPMreq(fd Handle, level, opt int, mreq *IPMreq) (err error) {
}
func SetsockoptIPv6Mreq(fd Handle, level, opt int, mreq *IPv6Mreq) (err error) { return EWINDOWS }
-func Getpid() (pid int) { return int(GetCurrentProcessId()) }
+func Getpid() (pid int) { return int(getCurrentProcessId()) }
func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, err error) {
// NOTE(rsc): The Win32finddata struct is wrong for the system call:
src/pkg/syscall/zsyscall_windows_386.go
--- a/src/pkg/syscall/zsyscall_windows_386.go
+++ b/src/pkg/syscall/zsyscall_windows_386.go
@@ -1181,7 +1181,7 @@ func RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32
return
}
-func GetCurrentProcessId() (pid uint32) {
+func getCurrentProcessId() (pid uint32) {
r0, _, _ := Syscall(procGetCurrentProcessId.Addr(), 0, 0, 0, 0)
pid = uint32(r0)
return
src/pkg/syscall/zsyscall_windows_amd64.go
--- a/src/pkg/syscall/zsyscall_windows_amd64.go
+++ b/src/pkg/syscall/zsyscall_windows_amd64.go
@@ -1181,7 +1181,7 @@ func RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32
return
}
-func GetCurrentProcessId() (pid uint32) {
+func getCurrentProcessId() (pid uint32) {
r0, _, _ := Syscall(procGetCurrentProcessId.Addr(), 0, 0, 0, 0)
pid = uint32(r0)
return
コアとなるコードの解説
このコミットのコアとなる変更は、GetCurrentProcessId
という関数名の先頭文字を大文字のG
から小文字のg
に変更し、getCurrentProcessId
とすることです。
-
syscall_windows.go
における//sys
ディレクティブの変更:- 変更前:
//sys GetCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId
- 変更後:
//sys getCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId
この行は、Goのsyscall
パッケージがWindowsのkernel32.dll
からGetCurrentProcessId
という名前のAPI関数をインポートし、それをGoの関数としてGetCurrentProcessId
という名前で利用することを定義していました。この変更により、Go側で利用する関数名がgetCurrentProcessId
となり、Goの命名規則に従って「非エクスポート」関数として扱われるようになります。つまり、syscall
パッケージの外部からはこの関数を直接呼び出すことができなくなります。
- 変更前:
-
syscall_windows.go
におけるGetpid
関数の呼び出し箇所の変更:- 変更前:
func Getpid() (pid int) { return int(GetCurrentProcessId()) }
- 変更後:
func Getpid() (pid int) { return int(getCurrentProcessId()) }
Goのsyscall
パッケージには、現在のプロセスのIDを取得するためのGetpid
関数が定義されています。この関数は、内部的にWindows APIのラッパーであるGetCurrentProcessId
(変更後はgetCurrentProcessId
)を呼び出していました。この変更により、Getpid
関数は、非エクスポートとなったgetCurrentProcessId
を呼び出すようになります。Getpid
自体はエクスポートされた関数(大文字始まり)であるため、引き続き外部から利用可能ですが、その内部実装は非公開の関数に依存する形になります。
- 変更前:
-
zsyscall_windows_386.go
およびzsyscall_windows_amd64.go
における関数定義の変更:- 変更前:
func GetCurrentProcessId() (pid uint32) { ... }
- 変更後:
func getCurrentProcessId() (pid uint32) { ... }
これらのファイルは、//sys
ディレクティブに基づいてGoのツールチェーンが自動生成する、実際のシステムコール呼び出しを行うためのGoコードを含んでいます。ここでも、関数名がGetCurrentProcessId
からgetCurrentProcessId
に変更され、この関数が非エクスポート関数として定義されるようになりました。これにより、この低レベルなラッパー関数がsyscall
パッケージの内部でのみ利用されることが保証されます。
- 変更前:
これらの変更により、syscall.GetCurrentProcessId
という関数はGoの外部からは見えなくなり、syscall
パッケージの内部実装の詳細として適切にカプセル化されます。ユーザーは引き続きos.Getpid()
(またはsyscall.Getpid()
)を通じてプロセスIDを取得できますが、その基盤となるWindows APIのラッパー関数が誤って公開されることはなくなります。これは、Goのライブラリ設計における「内部実装の隠蔽」という重要な原則に沿った修正です。
関連リンク
- Go CL 6297065: https://golang.org/cl/6297065
参考にした情報源リンク
- Go言語のパッケージとエクスポート/非エクスポートのルールに関する一般的な知識
- Windows API
GetCurrentProcessId
に関する一般的な知識 - Go言語の
syscall
パッケージに関する一般的な知識 - Go言語の
os
パッケージに関する一般的な知識 - コミットメッセージと差分情報