[インデックス 13703] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにおけるWindows固有の実装に関するものです。具体的には、syscall_windows.go
ファイル内にコメントアウトされていたWindows API呼び出しのサンプルコードを、syscall_windows_test.go
に独立したテスト可能なExample関数として抽出し、同時に古くなったコメントを修正しています。
コミット
commit f78ead3ca461e2e3bd003e11c49eae610a10ab97
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Wed Aug 29 21:44:46 2012 +0800
syscall: extract an ExampleLoadLibrary from comment
while we are at it, fix some out-of-date comments.
R=golang-dev, dave, r
CC=golang-dev
https://golang.org/cl/6498054
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/f78ead3ca461e2e3bd003e11c49eae610a10ab97
元コミット内容
このコミットの目的は以下の通りです。
src/pkg/syscall/syscall_windows.go
内のコメントアウトされたWindows API呼び出しのデモコードを、src/pkg/syscall/syscall_windows_test.go
にExampleLoadLibrary
という名前のExample関数として抽出する。src/pkg/syscall/asm_windows_386.s
とsrc/pkg/syscall/asm_windows_amd64.s
内の古くなったコメントを修正する。
変更の背景
Go言語の標準ライブラリでは、コードの利用方法を示すためにExample関数が推奨されています。Example関数は、go test
コマンドで実行可能であり、godoc
によって自動的にドキュメントとして生成されます。これにより、ユーザーは実際のコード例を通じてAPIの利用方法を理解しやすくなります。
このコミット以前は、Windows APIのLoadLibrary
、GetProcAddress
、GetVersion
といった重要な関数群の利用例が、syscall_windows.go
ファイル内の大きなコメントブロックとして存在していました。コメント内のコードはテストやドキュメント生成の対象とならないため、その有用性が限定的でした。
この変更の背景には、以下の意図が考えられます。
- ドキュメンテーションの改善: コメント内のコードをExample関数として抽出することで、
godoc
を通じて公式ドキュメントに組み込まれ、ユーザーがsyscall
パッケージのWindows固有の機能を利用する際の参考になる。 - テスト可能性の向上: Example関数は
go test
によって実行されるため、サンプルコードが常に動作することを保証できる。これにより、APIの変更や環境の変化によってサンプルコードが古くなることを防ぐ。 - コードベースの整理: 不要な大きなコメントブロックを削除し、関連するテストファイルにコード例を移動することで、コードの可読性と保守性を向上させる。
- コメントの正確性の維持: アセンブリファイル内の古くなったコメントを修正することで、コードベース全体の正確性を保つ。特に、システムコールが実装されている場所に関する記述の更新は重要です。
前提知識の解説
このコミットを理解するためには、以下の知識が役立ちます。
1. Go言語のsyscall
パッケージ
syscall
パッケージは、Goプログラムからオペレーティングシステム(OS)のプリミティブな機能(システムコール)にアクセスするためのインターフェースを提供します。ファイル操作、ネットワーク通信、プロセス管理など、OSレベルの低レベルな操作を行う際に使用されます。OSごとに異なる実装を持つため、syscall_windows.go
のようにOS固有のファイルが存在します。
2. Windows API (Win32 API)
Windows APIは、Microsoft Windowsオペレーティングシステムが提供するアプリケーションプログラミングインターフェースの集合体です。C言語で記述されており、DLL(Dynamic Link Library)として提供されます。Go言語のsyscall
パッケージは、このWindows APIをGoから呼び出すためのラッパーを提供します。
LoadLibrary
: 指定されたモジュール(DLL)を呼び出し元のプロセスのアドレス空間にロードします。これにより、そのDLL内の関数を利用できるようになります。GetProcAddress
: ロードされたモジュール(DLL)から、指定された関数名に対応する関数のエントリポイント(アドレス)を取得します。GetVersion
: 現在実行中のWindowsオペレーティングシステムのバージョン情報を取得する関数です。メジャーバージョン、マイナーバージョン、ビルド番号などが含まれます。Syscall
: Go言語から直接システムコールを呼び出すための汎用関数です。引数として関数のエントリポイント(uintptr
型)と、システムコールに渡す引数を取ります。
3. Go言語のExample関数
Go言語のテストパッケージ(testing
)には、Example
というプレフィックスを持つ関数を記述する慣習があります。これらの関数は、通常のテスト関数(Test
プレフィックス)とは異なり、コードの動作例を示すために使用されます。
- 自動実行:
go test
コマンドを実行すると、Example関数も自動的に実行されます。 - 出力検証: Example関数のコメントに
Output:
という行を記述することで、そのExample関数が標準出力に出力する内容を検証できます。これにより、サンプルコードが期待通りの出力を生成するかどうかをテストできます。 - ドキュメント生成:
godoc
ツールは、Example関数を自動的に抽出し、パッケージのドキュメントに含めます。これにより、ユーザーはgodoc
を通じてコード例を参照できます。
4. Go言語のアセンブリファイル (.s
ファイル)
Go言語のランタイムや一部の低レベルな処理は、アセンブリ言語で記述されています。src/pkg/syscall/asm_windows_386.s
やsrc/pkg/syscall/asm_windows_amd64.s
は、それぞれ32ビットおよび64ビットWindows環境におけるシステムコール関連のアセンブリコードを含んでいます。これらのファイル内のコメントは、そのアセンブリコードがGoランタイムのどの部分と連携しているかを示す重要な情報を含んでいます。
技術的詳細
このコミットの技術的な変更点は以下の通りです。
1. syscall_windows.go
からのコメントブロック削除
src/pkg/syscall/syscall_windows.go
から、約30行にわたるWindowsバージョン検出のデモコードがコメントアウトされた状態で削除されました。このコードは、kernel32.dll
をロードし、GetVersion
関数を呼び出してWindowsのバージョン情報を取得し、それを標準出力に表示するものでした。
削除されたコメントブロックの冒頭部分:
/*
small demo to detect version of windows you are running:
package main
import (
"syscall"
)
func abort(funcname string, err error) {
panic(funcname + " failed: " + err.Error())
}
func print_version(v uint32) {
major := byte(v)
minor := uint8(v >> 8)
build := uint16(v >> 16)
print("windows version ", major, ".", minor, " (Build ", build, ")\\n")
}
func main() {
h, err := syscall.LoadLibrary("kernel32.dll")
if err != nil {
abort("LoadLibrary", err)
}
defer syscall.FreeLibrary(h)
proc, err := syscall.GetProcAddress(h, "GetVersion")
if err != nil {
abort("GetProcAddress", err)
}
r, _, _ := syscall.Syscall(uintptr(proc), 0, 0, 0, 0)
print_version(uint32(r))
}
*/
2. syscall_windows_test.go
へのExample関数の追加
削除されたコメントブロックのコードは、src/pkg/syscall/syscall_windows_test.go
にExampleLoadLibrary
という新しいExample関数として追加されました。この関数は、元のデモコードとほぼ同じロジックを持ちますが、GoのExample関数の慣習に従って記述されています。
func ExampleLoadLibrary() {
h, err := syscall.LoadLibrary("kernel32.dll")
if err != nil {
abort("LoadLibrary", err)
}
defer syscall.FreeLibrary(h)
proc, err := syscall.GetProcAddress(h)
if err != nil {
abort("GetProcAddress", err)
}
r, _, _ := syscall.Syscall(uintptr(proc), 0, 0, 0, 0)
major := byte(r)
minor := uint8(r >> 8)
build := uint16(r >> 16)
print("windows version ", major, ".", minor, " (Build ", build, ")\\n")
}
注釈: GetProcAddress
の引数が元のコメントではGetProcAddress(h, "GetVersion")
でしたが、コミット後のコードではGetProcAddress(h)
となっています。これは、GetProcAddress
のシグネチャが変更されたか、またはこのExample関数が簡略化されたことを示唆しています。しかし、Go 1.0のsyscall
パッケージのGetProcAddress
はfunc GetProcAddress(handle Handle, procname string) (proc uintptr, err error)
というシグネチャなので、これはコミットログの表示上の問題か、あるいはGoのバージョンアップに伴う変更の可能性が高いです。実際のGo 1.0のコードではGetProcAddress(h, "GetVersion")
が正しいです。このコミットは2012年のものであり、Go 1.0がリリースされた直後であるため、このような過渡期の変更が見られる可能性があります。
3. アセンブリファイル内のコメント修正
src/pkg/syscall/asm_windows_386.s
とsrc/pkg/syscall/asm_windows_amd64.s
のコメントが修正されました。
変更前:
// System calls for 386, Windows are implemented in ../runtime/windows/syscall.goc
変更後:
// System calls for 386, Windows are implemented in ../runtime/syscall_windows.goc
この変更は、システムコールが実装されているGoランタイムのソースファイルのパスが、../runtime/windows/syscall.goc
から../runtime/syscall_windows.goc
に変わったことを反映しています。これは、Goの内部構造の変更に伴うパスの更新であり、コメントの正確性を保つための重要な修正です。.goc
ファイルは、GoのコンパイラがC言語のコードをGoのオブジェクトファイルに変換する際に使用する中間ファイルのようなものです。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は以下のファイルです。
src/pkg/syscall/syscall_windows.go
: 大規模なコメントブロックの削除。src/pkg/syscall/syscall_windows_test.go
:ExampleLoadLibrary
関数の新規追加。src/pkg/syscall/asm_windows_386.s
: コメントの修正。src/pkg/syscall/asm_windows_amd64.s
: コメントの修正。
コアとなるコードの解説
syscall_windows.go
からのコメント削除
このファイルから削除されたコメントブロックは、Windows APIのLoadLibrary
、GetProcAddress
、Syscall
を使ってkernel32.dll
からGetVersion
関数を呼び出し、Windowsのバージョン情報を取得する具体的な手順を示していました。このコードは完全に機能するものでしたが、コメント内に埋め込まれていたため、Goのツールチェーン(go test
やgodoc
)による恩恵を受けることができませんでした。この削除は、コード例をより適切な場所(テストファイル内のExample関数)に移動するための前段階です。
syscall_windows_test.go
へのExampleLoadLibrary
追加
このファイルに追加されたExampleLoadLibrary
関数は、削除されたコメントブロックのコードをExample関数として再構築したものです。
func ExampleLoadLibrary() {
// kernel32.dllをロード
h, err := syscall.LoadLibrary("kernel32.dll")
if err != nil {
// エラーが発生した場合、パニック
abort("LoadLibrary", err)
}
// 関数終了時にFreeLibraryを呼び出してDLLをアンロード
defer syscall.FreeLibrary(h)
// ロードしたDLLから"GetVersion"関数のアドレスを取得
// 注: コミットログの表示では引数が省略されているが、実際には "GetVersion" が必要
proc, err := syscall.GetProcAddress(h, "GetVersion") // 正しい呼び出し方
if err != nil {
abort("GetProcAddress", err)
}
// GetVersion関数をシステムコールとして呼び出す
// GetVersionは引数を取らないため、0, 0, 0, 0 を渡す
r, _, _ := syscall.Syscall(uintptr(proc), 0, 0, 0, 0)
// 戻り値 (uint32) からメジャーバージョン、マイナーバージョン、ビルド番号を抽出
major := byte(r) // 下位8ビットがメジャーバージョン
minor := uint8(r >> 8) // 次の8ビットがマイナーバージョン
build := uint16(r >> 16) // 次の16ビットがビルド番号
// 抽出したバージョン情報を標準出力に表示
print("windows version ", major, ".", minor, " (Build ", build, ")\\n")
}
// abort関数は、エラー発生時にパニックを発生させるヘルパー関数
func abort(funcname string, err error) {
panic(funcname + " failed: " + err.Error())
}
このExample関数は、Goのsyscall
パッケージを使ってWindows APIを呼び出す典型的なパターンを示しています。
syscall.LoadLibrary
で必要なDLLをロードします。defer syscall.FreeLibrary(h)
で、関数が終了する際にDLLが適切にアンロードされるようにします。これはリソースリークを防ぐための重要なパターンです。syscall.GetProcAddress
で、ロードしたDLLから特定の関数のアドレスを取得します。syscall.Syscall
を使って、取得した関数アドレスを直接呼び出します。Syscall
は可変引数を取りますが、ここではGetVersion
が引数を取らないため、ダミーの0を渡しています。GetVersion
の戻り値(uint32
)をビットシフトと型変換で解析し、メジャーバージョン、マイナーバージョン、ビルド番号を抽出しています。
このExample関数がsyscall_windows_test.go
に配置されたことで、go test
実行時にこのコードが実際に動作するか検証され、godoc
によってGoの公式ドキュメントに組み込まれるようになりました。
アセンブリファイル内のコメント修正
asm_windows_386.s
とasm_windows_amd64.s
のコメント修正は、Goランタイムの内部構造の変更を反映したものです。Goのシステムコール実装が、以前は../runtime/windows/syscall.goc
というパスにあったものが、../runtime/syscall_windows.goc
というパスに移動したことを示しています。これは、Goのビルドシステムやランタイムのディレクトリ構造が進化する中で行われた、パスの正規化または再編成の一環と考えられます。このようなコメントの修正は、コードベースの正確性と、将来の開発者がシステムコールの実装を追跡する際の助けとなります。
関連リンク
- Go言語の
syscall
パッケージのドキュメント: https://pkg.go.dev/syscall - Go言語の
testing
パッケージのドキュメント(Example関数について): https://pkg.go.dev/testing - Windows API
LoadLibrary
(Microsoft Learn): https://learn.microsoft.com/ja-jp/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya - Windows API
GetProcAddress
(Microsoft Learn): https://learn.microsoft.com/ja-jp/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress - Windows API
GetVersion
(Microsoft Learn): https://learn.microsoft.com/ja-jp/windows/win32/api/sysinfoapi/nf-sysinfoapi-getversion
参考にした情報源リンク
- Go言語の公式ドキュメント
- Microsoft Learn (Windows APIドキュメント)
- Go言語のソースコード (GitHub)
- Go言語のExample関数に関するブログ記事やチュートリアル (一般的な知識として)