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

[インデックス 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.gosrc/pkg/syscall/zsyscall_windows_amd64.goのようなファイルは、Goのビルドプロセス中に自動生成されるファイルです。これらは、//sysディレクティブに基づいて、実際のシステムコールを呼び出すための低レベルなコード(アセンブリコードやSyscall関数の呼び出し)を含んでいます。これにより、開発者が手動でプラットフォーム固有のシステムコール呼び出しを記述する手間が省かれ、Goのクロスプラットフォーム性が維持されます。

技術的詳細

このコミットの技術的な核心は、Windows APIのGetCurrentProcessId関数をGoのsyscallパッケージに統合し、Getpid()関数がこのAPIを呼び出すように変更した点にあります。

  1. 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を返すことを示しています。

  2. 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のGetCurrentProcessIdDWORD(符号なし32ビット整数)を返すため、Goのint型に変換されています。

  3. 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を正確に取得できるようになりました。

関連リンク

参考にした情報源リンク