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

[インデックス 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固有の機能に直接アクセスするため、プラットフォームに依存するコードが多く含まれます。そのため、通常はアプリケーション開発者が直接利用するよりも、osnetといったより高レベルな標準ライブラリが内部的に利用することが多いです。

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つのファイルで行われています。

  1. src/pkg/syscall/syscall_windows.go
  2. src/pkg/syscall/zsyscall_windows_386.go (32ビットWindowsアーキテクチャ用)
  3. 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パッケージの内部実装に限定することです。これにより、以下の目的が達成されます。

  1. カプセル化の強化: syscallパッケージの内部詳細が外部に漏れるのを防ぎます。これにより、パッケージのAPIがよりクリーンになり、将来的な内部実装の変更が外部のコードに影響を与えるリスクが減少します。
  2. Goの設計原則への準拠: Goでは、プラットフォームに依存する低レベルな機能は、通常、より高レベルでプラットフォーム非依存な抽象化(例: osパッケージ)を通じて提供されるべきです。GetCurrentProcessIdを非エクスポートにすることで、この原則に沿った形になります。ユーザーはos.Getpid()を使用すべきであり、syscall.GetCurrentProcessId()を直接呼び出すべきではありません。
  3. 偶発的な利用の防止: 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とすることです。

  1. 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パッケージの外部からはこの関数を直接呼び出すことができなくなります。
  2. 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自体はエクスポートされた関数(大文字始まり)であるため、引き続き外部から利用可能ですが、その内部実装は非公開の関数に依存する形になります。
  3. 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言語のパッケージとエクスポート/非エクスポートのルールに関する一般的な知識
  • Windows API GetCurrentProcessIdに関する一般的な知識
  • Go言語のsyscallパッケージに関する一般的な知識
  • Go言語のosパッケージに関する一般的な知識
  • コミットメッセージと差分情報