[インデックス 16864] ファイルの概要
このコミットは、Goランタイムの src/pkg/runtime/proc.c
ファイルに対する変更です。proc.c
はGoランタイムの中核をなすファイルの一つで、プロセスの初期化、ゴルーチンのスケジューリング、M(マシン)とP(プロセッサ)の管理、およびOSとの低レベルなインタラクションに関するコードが含まれています。特に、runtime·mstart
関数は、新しいM(OSスレッド)が起動する際に最初に実行されるコードパスであり、Goランタイムの初期化において重要な役割を担っています。
コミット
このコミットは、Windows 386アーキテクチャでのみ必要とされるStructured Exception Handling (SEH) の定義を、必要な場合にのみ行うように変更します。これにより、他のプラットフォームやアーキテクチャでのコンパイル時に不要なコードやデータ構造が生成されるのを防ぎ、コンパイル時間の短縮やバイナリサイズの削減に貢献します。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/3453a2204b07bfbc2ab42f2add7911f4c1bdb63c
元コミット内容
commit 3453a2204b07bfbc2ab42f2add7911f4c1bdb63c
Author: Keith Randall <khr@golang.org>
Date: Wed Jul 24 09:59:47 2013 -0700
runtime: only define SEH when we need it.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/11769043
---
src/pkg/runtime/proc.c | 4 ++++\n 1 file changed, 4 insertions(+)
変更の背景
Goランタイムは、様々なオペレーティングシステム(OS)とアーキテクチャ(CPU)をサポートするように設計されています。しかし、特定のOSやアーキテクチャに特有の機能やデータ構造が存在します。Structured Exception Handling (SEH) は、Microsoft Windowsオペレーティングシステムに固有の例外処理メカニズムです。
以前のコードでは、SEH seh;
という変数の定義が、Windows 386アーキテクチャ以外の環境でも無条件に行われていました。これは、クロスプラットフォーム開発において、不要な依存関係やコンパイル時のオーバーヘッドを生じさせる可能性があります。このコミットの目的は、SEHが実際に必要とされるWindows 386環境でのみ、この変数を定義するように条件付きコンパイルを導入することです。これにより、コードの移植性と効率性が向上します。
コミットメッセージにあるコメント「It is used by windows-386 only. Unfortunately, seh needs to be located on os stack, and mstart runs on os stack for both m0 and m.」は、SEHオブジェクトがOSスタック上に配置される必要があり、mstart
関数がm0(初期OSスレッド)とm(追加のOSスレッド)の両方でOSスタック上で実行されるため、この定義が必要であることを示唆しています。しかし、この要件はWindows 386に限定されるため、他の環境では不要な定義となります。
前提知識の解説
1. Goランタイム (Go Runtime)
Goランタイムは、Goプログラムの実行を管理するシステムです。これには、ガベージコレクション、ゴルーチン(軽量スレッド)のスケジューリング、メモリ管理、システムコールとのインタラクションなどが含まれます。Goプログラムは、OSによって直接実行されるのではなく、Goランタイム上で実行されます。
2. ゴルーチン (Goroutine), M (Machine), P (Processor)
Goの並行処理モデルは、ゴルーチン、M、Pという3つの主要な概念に基づいています。
- ゴルーチン (Goroutine): Goの軽量な並行実行単位です。数千、数百万のゴルーチンを同時に実行できます。
- M (Machine): OSスレッドを表します。Goランタイムは、OSスレッドをMとして抽象化し、その上でゴルーチンを実行します。
- P (Processor): 論理プロセッサを表します。MはPにアタッチされ、Pは実行可能なゴルーチンをMに提供します。これにより、ゴルーチンのスケジューリングが効率的に行われます。
runtime·mstart
関数は、新しいM(OSスレッド)が起動する際に、そのスレッドの初期化を行うために呼び出されます。
3. Structured Exception Handling (SEH)
Structured Exception Handling (SEH) は、Microsoft Windowsオペレーティングシステムに特有の例外処理メカニズムです。C/C++言語の try-catch
ブロックに似ていますが、より低レベルで、ハードウェア例外(例:ゼロ除算、無効なメモリアクセス)やソフトウェア例外(例:アプリケーション定義の例外)を捕捉し、処理することができます。SEHは、スタック上の特定のフレームに例外ハンドラを登録することで機能します。
4. 条件付きコンパイル (#ifdef
, #endif
)
C言語やC++言語では、プリプロセッサディレクティブ(#
で始まる命令)を使用して、コンパイル時に特定のコードブロックを含めるか除外するかを制御できます。
#ifdef マクロ名
:マクロ名
が定義されている場合に、その後のコードブロックをコンパイルに含めます。#ifndef マクロ名
:マクロ名
が定義されていない場合に、その後のコードブロックをコンパイルに含めます。#endif
:#ifdef
や#ifndef
で始まった条件付きコンパイルブロックの終わりを示します。
Goランタイムのビルドプロセスでは、ターゲットOSやアーキテクチャに応じて、GOOS_windows
や GOARCH_386
といったマクロが定義されます。
5. GOOS_windows
と GOARCH_386
これらはGoのビルドシステムによって定義されるプリプロセッサマクロです。
GOOS_windows
: ターゲットOSがWindowsである場合に定義されます。GOARCH_386
: ターゲットアーキテクチャがIntel 386(32ビット)である場合に定義されます。
これらのマクロを組み合わせることで、特定のOSとアーキテクチャの組み合わせに特化したコードを記述できます。
技術的詳細
このコミットの技術的な核心は、Windows 386アーキテクチャにおけるSEHの利用と、Goランタイムのmstart
関数の特性を考慮した条件付きコンパイルの適用です。
SEHとスタックフレーム
WindowsのSEHは、例外発生時にスタックを巻き戻し、適切な例外ハンドラを見つけることで機能します。SEHハンドラは通常、スタックフレームの一部として登録されます。コミットメッセージにある「seh needs to be located on os stack」という記述は、SEH関連のデータ構造(この場合はSEH seh;
変数)が、例外処理が正しく機能するために、OSスレッドのスタック上に配置される必要があることを示しています。
mstart
関数とOSスタック
runtime·mstart
関数は、GoランタイムがOSスレッドを起動する際に、そのスレッドの初期化を行うために呼び出されます。この関数は、Goランタイムが管理するゴルーチンのスタックではなく、OSが提供するネイティブなスタック(OSスタック)上で実行されます。これは、GoランタイムがOSスレッドのコンテキストを完全に制御する前の初期段階であるためです。
Windows 386の特殊性
なぜSEHの定義が特にWindows 386に限定されるのかは、当時のGoランタイムの実装や、Windows 386のABI(Application Binary Interface)や例外処理メカニズムの特定の側面に関連している可能性があります。
- スタックレイアウト: 32ビットアーキテクチャでは、スタックの管理や関数呼び出し規約が64ビットアーキテクチャとは異なる場合があります。SEHハンドラの登録方法や、スタック上の特定のオフセットにSEH情報が必要とされる場合、32ビット環境でより厳密な要件があった可能性があります。
- 例外処理の統合: Goランタイムは、OSの例外処理メカニズムと連携して、パニック(Goのランタイムエラー)やクラッシュを処理します。Windows 386環境では、SEHを明示的に利用して、Goランタイムが捕捉できない低レベルのOS例外を処理する必要があったのかもしれません。
- 歴史的経緯: 2013年時点のGoランタイムのWindowsサポートはまだ初期段階であり、特定のアーキテクチャでSEHを統合するための試行錯誤があった可能性があります。
条件付きコンパイルの利点
#ifdef GOOS_windows
と #ifdef GOARCH_386
を使用することで、以下の利点が得られます。
- クロスプラットフォーム互換性: Windows 386以外の環境(例:Linux AMD64、macOS ARM64)でコンパイルする際に、
SEH seh;
の定義が完全にスキップされます。これにより、これらの環境でSEH関連の型や構造体が未定義であってもコンパイルエラーが発生せず、コードベースの移植性が高まります。 - バイナリサイズの削減: 不要なデータ構造がバイナリに含まれなくなるため、最終的な実行ファイルのサイズがわずかに削減される可能性があります。
- コンパイル時間の短縮: プリプロセッサによって不要なコードが除外されるため、コンパイルプロセスがわずかに高速化される可能性があります。
- コードの明確化: コードを読む人にとって、
SEH seh;
が特定の環境でのみ意味を持つことが明確になります。
この変更は、Goランタイムが異なるプラットフォームの特性にどのように適応しているかを示す良い例です。
コアとなるコードの変更箇所
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -485,10 +485,14 @@ runtime·starttheworld(void)\n void\n runtime·mstart(void)\n {\n+#ifdef GOOS_windows\n+#ifdef GOARCH_386\n // It is used by windows-386 only. Unfortunately, seh needs\n // to be located on os stack, and mstart runs on os stack\n // for both m0 and m.\n SEH seh;\n+#endif\n+#endif\n \n if(g != m->g0)\n runtime·throw(\"bad runtime·mstart\");
コアとなるコードの解説
変更の中心は、runtime·mstart
関数内の SEH seh;
という行に、プリプロセッサディレクティブが追加されたことです。
変更前:
void
runtime·mstart(void)
{
// It is used by windows-386 only. Unfortunately, seh needs
// to be located on os stack, and mstart runs on os stack
// for both m0 and m.
SEH seh;
if(g != m->g0)
runtime·throw("bad runtime·mstart");
// ...
}
変更後:
void
runtime·mstart(void)
{
#ifdef GOOS_windows
#ifdef GOARCH_386
// It is used by windows-386 only. Unfortunately, seh needs
// to be located on os stack, and mstart runs on os stack
// for both m0 and m.
SEH seh;
#endif
#endif
if(g != m->g0)
runtime·throw("bad runtime·mstart");
// ...
}
この変更により、SEH seh;
という変数の宣言は、以下の両方の条件が満たされた場合にのみコンパイルに含まれるようになります。
GOOS_windows
マクロが定義されている(つまり、ターゲットOSがWindowsである)。GOARCH_386
マクロが定義されている(つまり、ターゲットアーキテクチャが32ビットIntel x86である)。
それ以外の組み合わせ(例:Linux AMD64、Windows AMD64、macOS ARM64など)では、この行はコンパイル時に完全に無視されます。これにより、SEHが不要な環境では、SEH
型が未定義であってもコンパイルエラーにならず、また不要なスタック領域の確保も行われなくなります。
コメントは、このSEH seh;
の定義がWindows 386でのみ使用され、SEHオブジェクトがOSスタック上に配置される必要があること、そしてmstart
関数がm0とmの両方でOSスタック上で実行されるという背景を説明しています。この変更は、この特定の要件を満たしつつ、他のプラットフォームでのビルドの健全性を保つためのものです。
関連リンク
- Go Code Review 11769043: https://golang.org/cl/11769043
- Go Runtime Documentation (公式ドキュメント): https://go.dev/doc/ (Goランタイムの一般的な情報については、公式ドキュメントを参照してください。)
参考にした情報源リンク
- Go言語の公式ドキュメント
- Goのソースコード(特に
src/pkg/runtime
ディレクトリ) - Structured Exception Handling (Windows API): https://learn.microsoft.com/en-us/windows/win32/debug/structured-exception-handling
- C/C++ プリプロセッサディレクティブに関する一般的な情報源 (例: cppreference.com, Microsoft Learn)
- Goのビルドシステムとクロスコンパイルに関する情報源 (例: Go公式ブログ、Goのツールチェーンに関する記事)