[インデックス 16501] ファイルの概要
このコミットは、Goランタイムの386、AMD64、ARMアーキテクチャ向けアセンブリコード内のコメントを修正するものです。具体的には、以前gobufと呼ばれていた構造体がschedに名称変更されたにもかかわらず、コメントが更新されていなかった箇所を修正しています。これにより、コードの可読性と正確性が向上し、Goスケジューラの内部動作に関する理解が深まります。
コミット
commit 528534c1d4901f832e1ac6b5e2b8a56071ff8427
Author: Russ Cox <rsc@golang.org>
Date: Wed Jun 5 07:16:53 2013 -0400
runtime: fix comments (g->gobuf became g->sched long ago)
Should reduce size of CL 9868044.
R=golang-dev, ality
CC=golang-dev
https://golang.org/cl/10045043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/528534c1d4901f832e1ac6b5e2b8a56071ff8427
元コミット内容
runtime: fix comments (g->gobuf became g->sched long ago)
このコミットは、Goランタイムのアセンブリコード内のコメントを修正するものです。以前g->gobufと記述されていた箇所が、実際にはg->schedに名称変更されて久しいにもかかわらず、コメントが古いままになっていた点を修正しています。
変更の背景
Goランタイムの内部では、ゴルーチン(g)の状態を保存・復元するための構造体が使用されています。かつてこの構造体はgobufと呼ばれていましたが、Goのスケジューラ(sched)の進化に伴い、その役割と命名がより正確に反映されるようにschedへと名称変更されました。しかし、コード本体の変更は行われたものの、関連するアセンブリコード内のコメントがこの変更に追従していませんでした。
このコメントの不一致は、コードの理解を妨げ、特にGoランタイムの低レベルな動作をデバッグしたり、分析したりする際に混乱を招く可能性がありました。このコミットは、このような古いコメントを新しいg->schedという名称に更新することで、コードの正確性と可読性を向上させることを目的としています。コミットメッセージにある「Should reduce size of CL 9868044」は、おそらく別の大きな変更セット(CL 9868044)の一部としてこのコメント修正が含まれる予定だったが、独立したコミットとして切り出されたことを示唆しています。これにより、主要な変更のレビューがコメントの修正によって複雑になるのを避ける意図があったと考えられます。
前提知識の解説
このコミットを理解するためには、Goランタイムの基本的な概念と、アセンブリ言語の知識が必要です。
-
ゴルーチン (Goroutine): Go言語における軽量な実行スレッドです。OSのスレッドよりもはるかに軽量で、数百万のゴルーチンを同時に実行できます。Goランタイムがゴルーチンのスケジューリングを管理します。
-
Goスケジューラ: Goランタイムに組み込まれているスケジューラは、ゴルーチンをOSスレッド(M: Machine)にマッピングし、実行を管理します。Goスケジューラは、G-M-Pモデル(Goroutine-Machine-Processor)に基づいて動作します。
G(Goroutine): 実行されるゴルーチン。M(Machine): OSスレッド。ゴルーチンを実行する実際のOSスレッド。P(Processor): 論理プロセッサ。MがGを実行するために必要なコンテキスト。
-
g構造体: Goランタイム内部でゴルーチンを表す構造体です。各ゴルーチンは独自のg構造体を持っています。この構造体には、ゴルーチンのスタック情報、現在の状態、そしてスケジューラ関連の情報などが含まれます。 -
gobufとsched:gobuf: かつてGoランタイムで使用されていた、ゴルーチンの実行コンテキスト(プログラムカウンタ、スタックポインタ、レジスタ値など)を保存するための構造体でした。これは、ゴルーチンの切り替え(コンテキストスイッチ)時に、現在のゴルーチンの状態を保存し、次に実行するゴルーチンの状態を復元するために使用されました。sched:gobufの機能を引き継ぎ、より広範なスケジューラ関連の情報を保持するように拡張された構造体です。gobufが単なるレジスタの保存場所であったのに対し、schedはゴルーチンのスケジューリングに必要なより多くのメタデータを含むようになりました。この名称変更は、単なるレジスタの保存だけでなく、ゴルーチンのスケジューリング全体におけるその構造体の重要性を反映しています。
-
コンテキストスイッチ: 実行中のゴルーチンから別のゴルーチンへ制御を切り替えるプロセスです。これには、現在のゴルーチンのレジスタ状態(プログラムカウンタ、スタックポインタなど)を保存し、次に実行するゴルーチンのレジスタ状態を復元する操作が含まれます。この操作は非常に低レベルであり、通常はアセンブリ言語で実装されます。
-
アセンブリ言語: CPUが直接理解できる機械語に非常に近い低レベルのプログラミング言語です。Goランタイムのコンテキストスイッチや、OSとのインタラクションなど、パフォーマンスが非常に重要で、ハードウェアに密接に関わる部分はアセンブリ言語で記述されています。
asm_386.s,asm_amd64.s,asm_arm.sはそれぞれ、Intel 386互換CPU、AMD64アーキテクチャ、ARMアーキテクチャ向けのアセンブリコードファイルです。
技術的詳細
このコミットの技術的な核心は、Goランタイムのコンテキストスイッチ処理におけるコメントの正確性です。Goランタイムは、複数のゴルーチンを効率的に切り替えるために、各ゴルーチンの実行状態(レジスタ、スタックポインタ、プログラムカウンタなど)を保存・復元する必要があります。この状態は、g構造体内の特定のフィールドに格納されます。
歴史的に、このフィールドはgobufという名前で参照されていましたが、Goスケジューラの進化と機能拡張に伴い、その役割がより明確になるようにschedという名前に変更されました。この変更は、単なる名前の変更以上の意味を持ちます。schedフィールドは、単にレジスタの値を保存するだけでなく、ゴルーチンのスケジューリングに必要なより多くの情報(例えば、スタックの境界、現在のMへのポインタなど)を保持するようになりました。
アセンブリコードは、これらの低レベルな操作を直接CPU命令で記述するため、コメントはコードの意図を理解する上で非常に重要です。古いコメントがg->gobufを参照していると、コードを読んだ開発者は、現在のGoランタイムの設計と異なる古い概念に基づいてコードを解釈してしまう可能性があります。これは、デバッグの困難さや、将来の変更における誤解につながる可能性があります。
このコミットは、src/pkg/runtime/asm_386.s、src/pkg/runtime/asm_amd64.s、src/pkg/runtime/asm_arm.sの3つのアセンブリファイルにわたって、g->gobufという記述をg->schedに修正しています。これは、コードの動作自体を変更するものではなく、あくまでコメントの修正ですが、Goランタイムの内部構造に関する最新の知識を反映させることで、コードベース全体の整合性と保守性を高める重要な変更です。特に、runtime·mcall(GoルーチンからMスタックへの切り替え)やruntime·cgocallbackg(Cgoコールバック処理)のような、コンテキストスイッチが頻繁に発生する重要なパスでのコメント修正は、その影響が大きいです。
コアとなるコードの変更箇所
変更は、src/pkg/runtime/asm_386.s、src/pkg/runtime/asm_amd64.s、src/pkg/runtime/asm_arm.sの3つのファイルにわたっています。それぞれのファイルで、g->gobufという文字列がg->schedに置き換えられています。
例 (src/pkg/runtime/asm_386.s):
--- a/src/pkg/runtime/asm_386.s
+++ b/src/pkg/runtime/asm_386.s
@@ -194,7 +194,7 @@ TEXT runtime·mcall(SB), 7, $0
MOVL fn+0(FP), DI
get_tls(CX)
- MOVL g(CX), AX // save state in g->gobuf
+ MOVL g(CX), AX // save state in g->sched
MOVL 0(SP), BX // caller's PC
MOVL BX, (g_sched+gobuf_pc)(AX)
LEAL 4(SP), BX // caller's SP
@@ -208,7 +208,7 @@ TEXT runtime·mcall(SB), 7, $0
JNE 2(PC)
CALL runtime·badmcall(SB)
MOVL SI, g(CX) // g = m->g0
- MOVL (g_sched+gobuf_sp)(SI), SP // sp = m->g0->gobuf.sp
+ MOVL (g_sched+gobuf_sp)(SI), SP // sp = m->g0->sched.sp
PUSHL AX
CALL DI
POPL AX
@@ -566,11 +566,11 @@ havem:
// Switch to m->curg stack and call runtime.cgocallbackg
// with the three arguments. Because we are taking over
// the execution of m->curg but *not* resuming what had
- // been running, we need to save that information (m->curg->gobuf)
+ // been running, we need to save that information (m->curg->sched)
// so that we can restore it when we're done.
- // We can restore m->curg->gobuf.sp easily, because calling
+ // We can restore m->curg->sched.sp easily, because calling
// runtime.cgocallbackg leaves SP unchanged upon return.
- // To save m->curg->gobuf.pc, we push it onto the stack.
+ // To save m->curg->sched.pc, we push it onto the stack.
// This has the added benefit that it looks to the traceback
// routine like cgocallbackg is going to return to that
// PC (because we defined cgocallbackg to have
@@ -602,7 +602,7 @@ havem:\n MOVL DI, SP\n CALL runtime·cgocallbackg(SB)\n \n- // Restore g->gobuf (== m->curg->gobuf) from saved values.\n+ // Restore g->sched (== m->curg->sched) from saved values.\
get_tls(CX)\n MOVL g(CX), SI\n MOVL 12(SP), BP
同様の変更がsrc/pkg/runtime/asm_amd64.sとsrc/pkg/runtime/asm_arm.sにも適用されています。
コアとなるコードの解説
これらのアセンブリコードスニペットは、Goランタイムにおけるゴルーチンのコンテキストスイッチの重要な部分を示しています。
-
TEXT runtime·mcall(SB), 7, $0:runtime·mcall関数は、現在のゴルーチン(g)のコンテキストを保存し、OSスレッド(m)のスタックに切り替えるために使用されます。これは、例えばシステムコールを呼び出す前や、Cgo呼び出しを行う前など、GoランタイムがOSスレッドのスタックで実行する必要がある場合に発生します。MOVL g(CX), AX // save state in g->gobufがMOVL g(CX), AX // save state in g->schedに変更されています。これは、現在のゴルーチンgのレジスタ状態(特にプログラムカウンタPCとスタックポインタSP)が、g構造体内のschedフィールドに保存されることを示しています。MOVL (g_sched+gobuf_sp)(SI), SP // sp = m->g0->gobuf.spがMOVL (g_sched+gobuf_sp)(SI), SP // sp = m->g0->sched.spに変更されています。これは、m->g0(現在のOSスレッドに紐付けられた特別なゴルーチン、スケジューラが使用する)のschedフィールドからスタックポインタを復元し、OSスレッドのスタックに切り替える操作を示しています。
-
havem:ラベル周辺のコード: このセクションは、Cgoコールバック(C言語からGo言語の関数を呼び出す)の処理に関連しています。Cgoコールバックは、CコードからGoランタイムに制御が戻る際に、Goスケジューラがゴルーチンを適切に再開できるように、コンテキストを管理する必要があります。// ... save that information (m->curg->gobuf)が// ... save that information (m->curg->sched)に変更されています。これは、Cgoコールバックが実行される前に、現在のゴルーチン(m->curg)のコンテキストがschedフィールドに保存されることを示しています。これにより、コールバックが完了した後に元のゴルーチンに正しく戻ることができます。// We can restore m->curg->gobuf.sp easily, because callingが// We can restore m->curg->sched.sp easily, because callingに、そして// To save m->curg->gobuf.pc, we push it onto the stack.が// To save m->curg->sched.pc, we push it onto the stack.に変更されています。これらは、スタックポインタ(sp)とプログラムカウンタ(pc)がschedフィールドを通じて管理されることを明確にしています。// Restore g->gobuf (== m->curg->gobuf) from saved values.が// Restore g->sched (== m->curg->sched) from saved values.に変更されています。これは、Cgoコールバックが完了した後、保存されたschedフィールドの値からゴルーチンのコンテキストが復元されることを示しています。
これらの変更は、アセンブリコードの動作自体には影響を与えませんが、コードの意図を正確に反映し、Goランタイムの現在の設計に合致させることで、将来のメンテナンスやデバッグを容易にします。特に、低レベルなコンテキストスイッチのメカニズムを理解しようとする開発者にとって、これらのコメントの正確性は非常に重要です。
関連リンク
- Go CL 10045043: https://golang.org/cl/10045043
参考にした情報源リンク
- Go言語の公式ドキュメント (Goスケジューラに関する情報)
- Go言語のソースコード (特に
src/runtimeディレクトリ) - Go言語のコンテキストスイッチに関する技術記事 (一般的な概念理解のため)
- アセンブリ言語の基本的な概念に関する資料 (386, AMD64, ARMアーキテクチャ)