[インデックス 12866] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにおいて、Windows環境でのGetpid()
関数のサポートを追加するものです。これにより、Windows上で実行されているGoプログラムが自身のプロセスIDを正しく取得できるようになります。
コミット
commit ae382129f1f32b91115ef7d60ce9ec4108253e5d
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Tue Apr 10 22:34:31 2012 +0800
syscall: support Getpid() on Windows
Fixes #3379.
R=golang-dev, alex.brainman, rsc, bradfitz
CC=golang-dev
https://golang.org/cl/5909043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ae382129f1f32b91115ef7d60ce9ec4108253e5d
元コミット内容
このコミットは、Go言語のsyscall
パッケージにおけるWindows環境でのGetpid()
関数の実装に関するものです。以前はWindows上でGetpid()
を呼び出すと常に-1
が返されていましたが、このコミットによってWindows APIのGetCurrentProcessId
関数を呼び出すように変更され、正しいプロセスIDが取得できるようになりました。これは、GoのIssue #3379を解決するためのものです。
変更の背景
Go言語のsyscall
パッケージは、オペレーティングシステムの低レベルなプリミティブ関数へのインターフェースを提供します。Getpid()
関数は、現在のプロセスのプロセスID(PID)を取得するために使用されます。しかし、このコミットが作成される以前のGoのバージョンでは、Windows環境においてGetpid()
が正しく実装されておらず、常に-1
という無効な値が返されていました。
この問題はGoのIssue #3379として報告されており、Windows上で動作するGoプログラムが自身のプロセスIDを必要とする場合に、この機能の欠如が問題となっていました。例えば、ログ記録、プロセス間通信、または特定のプロセス管理タスクにおいて、正確なプロセスIDは不可欠です。このコミットは、この機能的なギャップを埋め、GoプログラムがWindows環境でも期待通りに動作するようにするために導入されました。
前提知識の解説
プロセスID (PID)
プロセスID(PID)は、オペレーティングシステムが実行中の各プロセスに割り当てる一意の識別子です。これにより、システムは個々のプロセスを区別し、管理することができます。プログラムが自身のPIDを取得する機能は、以下のような様々なシナリオで利用されます。
- ログ記録: ログファイルにプロセスIDを含めることで、どのプロセスが特定のイベントを生成したかを追跡しやすくなります。
- デバッグ: 特定のプロセスにアタッチしてデバッグする際にPIDが必要になります。
- プロセス間通信 (IPC): 他のプロセスと通信する際に、ターゲットプロセスのPIDを指定することがあります。
- リソース管理: プロセスが使用しているリソースを監視したり、終了させたりする際にPIDが使われます。
Go言語の syscall
パッケージ
Go言語の標準ライブラリに含まれるsyscall
パッケージは、オペレーティングシステムの低レベルなシステムコールへのアクセスを提供します。これにより、GoプログラムはOSのカーネルと直接対話し、ファイルシステム操作、ネットワーク通信、プロセス管理など、OSが提供する基本的な機能を利用できます。
syscall
パッケージは、OSに依存するコードを抽象化し、異なるプラットフォーム(Linux, Windows, macOSなど)で同様の機能を提供するための基盤となります。しかし、OS固有の機能やAPIを呼び出す必要がある場合、プラットフォームごとに異なる実装が必要になります。
Windows API
Windows API (Application Programming Interface) は、Microsoft Windowsオペレーティングシステムが提供する関数、データ構造、およびプロトコルのセットです。アプリケーション開発者は、これらのAPIを呼び出すことで、Windowsの機能(ファイル操作、メモリ管理、プロセス管理、GUIなど)を利用できます。
このコミットで特に重要なのは、kernel32.dll
に含まれるGetCurrentProcessId
関数です。
GetCurrentProcessId
: このWindows API関数は、呼び出し元のプロセスのプロセスIDを取得します。これは、GoのGetpid()
関数がWindows上で実現したい機能と直接対応します。
//sys
ディレクティブと zsyscall_windows_*.go
ファイル
Goのsyscall
パッケージでは、//sys
という特殊なコメントディレクティブが使用されます。これは、Goの関数シグネチャをWindows API関数にマッピングし、システムコールスタブを自動生成するために利用されます。
例えば、//sys GetCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId
という行は、GoのGetCurrentProcessId
関数が、kernel32.dll
内の同名のWindows API関数に対応することを宣言しています。
src/pkg/syscall/zsyscall_windows_386.go
やsrc/pkg/syscall/zsyscall_windows_amd64.go
のようなファイルは、Goのビルドプロセス中に自動生成されるファイルです。これらは、//sys
ディレクティブに基づいて、実際のシステムコールを呼び出すための低レベルなコード(アセンブリコードやSyscall
関数の呼び出し)を含んでいます。これにより、開発者が手動でプラットフォーム固有のシステムコール呼び出しを記述する手間が省かれ、Goのクロスプラットフォーム性が維持されます。
技術的詳細
このコミットの技術的な核心は、Windows APIのGetCurrentProcessId
関数をGoのsyscall
パッケージに統合し、Getpid()
関数がこのAPIを呼び出すように変更した点にあります。
-
syscall_windows.go
へのGetCurrentProcessId
の宣言追加:src/pkg/syscall/syscall_windows.go
ファイルに、//sys GetCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId
という行が追加されました。この行は、Goのsyscall
パッケージがkernel32.dll
からGetCurrentProcessId
という関数をインポートし、それをGoのGetCurrentProcessId
関数として利用することをコンパイラに指示します。pid uint32
は、この関数が符号なし32ビット整数としてプロセスIDを返すことを示しています。 -
Getpid()
関数の実装変更: 同じくsrc/pkg/syscall/syscall_windows.go
内で、Getpid()
関数の実装が変更されました。 変更前:func Getpid() (pid int) { return -1 }
変更後:func Getpid() (pid int) { return int(GetCurrentProcessId()) }
これにより、Getpid()
が内部的に新しく宣言されたGetCurrentProcessId()
を呼び出し、その結果をint
型にキャストして返すようになりました。Windows APIのGetCurrentProcessId
はDWORD
(符号なし32ビット整数)を返すため、Goのint
型に変換されています。 -
zsyscall_windows_386.go
およびzsyscall_windows_amd64.go
へのGetCurrentProcessId
の実装追加: これらのファイルは、それぞれ32ビット(x86)と64ビット(AMD64)アーキテクチャ向けのWindowsシステムコール実装を自動生成するものです。このコミットでは、これらのファイルに以下の変更が加えられました。procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
:kernel32.dll
からGetCurrentProcessId
関数のエントリポイント(メモリアドレス)を取得し、procGetCurrentProcessId
変数に格納します。これは、後でシステムコールを呼び出す際に使用されます。func GetCurrentProcessId() (pid uint32) { r0, _, _ := Syscall(procGetCurrentProcessId.Addr(), 0, 0, 0, 0); pid = uint32(r0); return }
:この関数は、procGetCurrentProcessId
に格納されたアドレスを使用して、実際のWindows API関数GetCurrentProcessId
を呼び出します。Syscall
関数は、低レベルなシステムコールを実行するためのGoのプリミティブです。r0
にはシステムコールの戻り値が格納され、それがuint32
型に変換されてpid
として返されます。引数0, 0, 0, 0
は、GetCurrentProcessId
が引数を取らないことを示しています。
これらの変更により、Goのsyscall.Getpid()
関数がWindows環境で正しく機能するようになり、Goプログラムが自身のプロセスIDを正確に取得できるようになりました。
コアとなるコードの変更箇所
src/pkg/syscall/syscall_windows.go
--- a/src/pkg/syscall/syscall_windows.go
+++ b/src/pkg/syscall/syscall_windows.go
@@ -199,6 +199,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
// syscall interface implementation for other packages
@@ -681,9 +682,9 @@ func SetsockoptIPMreq(fd Handle, level, opt int, mreq *IPMreq) (err error) {
}
func SetsockoptIPv6Mreq(fd Handle, level, opt int, mreq *IPv6Mreq) (err error) { return EWINDOWS }
-// TODO(brainman): fix all needed for os
+func Getpid() (pid int) { return int(GetCurrentProcessId()) }
-func Getpid() (pid int) { return -1 }
+// TODO(brainman): fix all needed for os
func Getppid() (ppid int) { return -1 }
func Fchdir(fd Handle) (err error) { return EWINDOWS }
src/pkg/syscall/zsyscall_windows_386.go
--- a/src/pkg/syscall/zsyscall_windows_386.go
+++ b/src/pkg/syscall/zsyscall_windows_386.go
@@ -142,6 +142,7 @@ var (
procOpenProcessToken = modadvapi32.NewProc("OpenProcessToken")
procGetTokenInformation = modadvapi32.NewProc("GetTokenInformation")
procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW")
+ procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
)
func GetLastError() (lasterr error) {
@@ -1600,3 +1601,9 @@ func GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) {
}\n\treturn
}\n+\n+func GetCurrentProcessId() (pid uint32) {\n+\tr0, _, _ := Syscall(procGetCurrentProcessId.Addr(), 0, 0, 0, 0)\n+\tpid = uint32(r0)\n+\treturn
+}\n
src/pkg/syscall/zsyscall_windows_amd64.go
--- a/src/pkg/syscall/zsyscall_windows_amd64.go
+++ b/src/pkg/syscall/zsyscall_windows_amd64.go
@@ -142,6 +142,7 @@ var (
procOpenProcessToken = modadvapi32.NewProc("OpenProcessToken")
procGetTokenInformation = modadvapi32.NewProc("GetTokenInformation")
procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW")
+ procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
)
func GetLastError() (lasterr error) {
@@ -1600,3 +1601,9 @@ func GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) {
}\n\treturn
}\n+\n+func GetCurrentProcessId() (pid uint32) {\n+\tr0, _, _ := Syscall(procGetCurrentProcessId.Addr(), 0, 0, 0, 0)\n+\tpid = uint32(r0)\n+\treturn
+}\n
コアとなるコードの解説
src/pkg/syscall/syscall_windows.go
-
//sys GetCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId
: この行は、Goのsyscall
パッケージがWindowsのkernel32.dll
からGetCurrentProcessId
という関数をインポートし、Goの関数として利用できるようにするための宣言です。//sys
ディレクティブは、Goのビルドシステムがこの宣言を解析し、対応する低レベルなシステムコール呼び出しコードを自動生成するために使用されます。これにより、Goのコードから直接Windows APIを呼び出すための橋渡しがなされます。 -
func Getpid() (pid int) { return int(GetCurrentProcessId()) }
: この変更は、Getpid()
関数の実装を、新しく利用可能になったGetCurrentProcessId()
関数を呼び出すように修正したものです。以前は常に-1
を返していましたが、この修正により、Windows APIを通じて取得した実際のプロセスIDが返されるようになりました。GetCurrentProcessId()
が返すuint32
型の値をGoのint
型にキャストしています。
src/pkg/syscall/zsyscall_windows_386.go
および src/pkg/syscall/zsyscall_windows_amd64.go
これらのファイルは、Goのビルドプロセスによって自動生成される、特定のアーキテクチャ(386は32ビット、amd64は64ビット)向けのシステムコール実装を含んでいます。
-
procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
: この行は、kernel32.dll
からGetCurrentProcessId
という名前の関数のエントリポイント(メモリアドレス)を取得し、それをprocGetCurrentProcessId
という変数に格納しています。NewProc
は、DLL内の特定の関数へのポインタを取得するために使用されるsyscall
パッケージの関数です。これにより、GoのコードからこのWindows API関数を直接呼び出す準備が整います。 -
func GetCurrentProcessId() (pid uint32) { r0, _, _ := Syscall(procGetCurrentProcessId.Addr(), 0, 0, 0, 0); pid = uint32(r0); return }
: この関数は、実際にWindows APIのGetCurrentProcessId
を呼び出すための低レベルなラッパーです。Syscall(procGetCurrentProcessId.Addr(), 0, 0, 0, 0)
:Syscall
関数は、指定されたアドレス(procGetCurrentProcessId.Addr()
)にあるシステムコールを呼び出します。GetCurrentProcessId
は引数を取らないため、残りの引数はすべて0
です。r0, _, _
:Syscall
は複数の戻り値を返しますが、r0
にはシステムコールの主要な戻り値(この場合はプロセスID)が格納されます。残りの戻り値はここでは使用されないため、_
で無視されています。pid = uint32(r0)
:r0
に格納された戻り値(プロセスID)をuint32
型にキャストし、pid
変数に代入して返します。
これらの変更により、Goのsyscall
パッケージはWindows環境でGetpid()
を正しく実装し、Goプログラムが自身のプロセスIDを正確に取得できるようになりました。
関連リンク
- Go Issue #3379: https://github.com/golang/go/issues/3379
- Go Code Review 5909043: https://golang.org/cl/5909043
参考にした情報源リンク
- Microsoft Docs - GetCurrentProcessId function: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocessid
- Go
syscall
package documentation: https://pkg.go.dev/syscall - Go
syscall
package source code (for general understanding of//sys
andzsyscall
files): https://github.com/golang/go/tree/master/src/syscall - Go
syscall
package on Windows (specific to Windows implementation details): https://github.com/golang/go/tree/master/src/syscall/windows - Understanding Go's
syscall
package and//sys
directives (general concept): https://go.dev/blog/go1.1-syscall (Note: This blog post is for Go 1.1, but the core concepts ofsyscall
and//sys
remain relevant.) - Go
Syscall
function: https://pkg.go.dev/syscall#Syscall - Go
NewProc
function: https://pkg.go.dev/syscall#NewProc - Go
modkernel32
(internal module for kernel32.dll): (This is an internal detail of the Go runtime, not directly documented as a public API, but its usage is evident in thesyscall
source.)