[インデックス 15535] ファイルの概要
このコミットは、Go言語のランタイムにおけるOpenBSDおよびPlan 9オペレーティングシステム上でのビルド問題を修正するものです。具体的には、デバッグ用のruntime·printf
呼び出し内で、存在しない、またはアクセスできないfn
(関数ポインタ)変数を参照していた箇所を削除することで、これらのプラットフォームでのコンパイルエラーを解消しています。この変更は、if(0)
ブロック内にあったため、実際のランタイムの動作には影響を与えません。
コミット
commit 08a1631cda0e94188e584fe846a93d05ef2528df
Author: Russ Cox <rsc@golang.org>
Date: Fri Mar 1 11:57:50 2013 -0500
runtime: fix build on openbsd, plan9
R=golang-dev
CC=golang-dev
https://golang.org/cl/7438052
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/08a1631cda0e94188e584fe846a93d05ef2528df
元コミット内容
runtime: fix build on openbsd, plan9
変更の背景
このコミットの背景には、GoランタイムがOpenBSDおよびPlan 9環境でビルドに失敗するという問題がありました。Goのランタイムは、各オペレーティングシステムに特化した低レベルのコードを含んでおり、OS固有のシステムコールやスレッド管理を扱います。
問題の原因は、src/pkg/runtime/thread_openbsd.c
とsrc/pkg/runtime/thread_plan9.c
内のruntime·newosproc
関数におけるデバッグ用のruntime·printf
呼び出しにありました。このprintf
文は、fn=%p
というフォーマット指定子を使用して関数ポインタfn
の値を表示しようとしていましたが、このfn
変数が現在のコンテキストで利用できない、または定義されていない状態になっていました。
このような状況は、コードのリファクタリングや変更によって、以前は存在した変数が削除されたり、スコープ外になったりした場合に発生しがちです。特にif(0)
ブロック内のコードは、コンパイル時に常に実行されないことが保証されるため、開発者がデバッグ目的で一時的に追加し、その後更新を忘れることがあります。しかし、C言語のコンパイラは、たとえ実行されないコードブロック内であっても、参照されている変数が定義されているか、型が一致しているかなどをチェックするため、この未定義のfn
変数の参照がビルドエラーを引き起こしていました。
このコミットは、このビルドエラーを解消し、GoランタイムがOpenBSDとPlan 9で正常にコンパイルされるようにすることを目的としています。
前提知識の解説
Goランタイム (Go Runtime)
Goランタイムは、Goプログラムの実行を管理する低レベルのコンポーネント群です。これには、ガベージコレクタ、スケジューラ(ゴルーチンとOSスレッドのマッピング)、メモリ管理、チャネルの実装、システムコールインターフェースなどが含まれます。Goプログラムは、OSが提供するスレッドの上に、Goランタイムが管理する軽量な並行処理単位である「ゴルーチン」を多重化して実行します。
M/P/Gモデル (Goroutine, Machine, Processor)
Goスケジューラは、M/P/Gモデルに基づいて動作します。
- G (Goroutine): Go言語の軽量な実行単位。関数呼び出しとして表現され、独自のスタックを持ちます。
- M (Machine): オペレーティングシステムのスレッドを表します。Goランタイムは、OSスレッドをMとして抽象化し、その上でGを実行します。MはOSスケジューラによって管理されます。
- P (Processor): 論理プロセッサを表します。Goスケジューラは、Pの数(通常はCPUコア数)に基づいて、MにGを割り当てます。Pは、MがGを実行するために必要なリソース(実行キューなど)を提供します。
runtime·newosproc
関数は、新しいOSスレッド(M)を作成するGoランタイムの内部関数です。
OpenBSDとPlan 9
- OpenBSD: セキュリティを重視したUNIX系オペレーティングシステムです。堅牢性とコードの品質に重点を置いており、多くのセキュリティ機能が組み込まれています。システムプログラミングにおいては、標準的なPOSIXインターフェースを提供しつつも、独自のシステムコールやライブラリの挙動を持つことがあります。
- Plan 9 from Bell Labs: ベル研究所で開発された分散オペレーティングシステムです。UNIXの設計思想をさらに推し進め、すべてのリソースをファイルとして扱うという徹底した設計が特徴です。UNIXとは異なる独自のシステムコールやファイルシステムインターフェースを持ち、Go言語の開発者の一部(特にRob PikeやRuss Cox)はPlan 9の設計思想に深く影響を受けています。そのため、GoランタイムはPlan 9への対応も初期から行われています。
runtime·printf
runtime·printf
は、Goランタイム内部で使用される低レベルのprintf
関数です。通常のC標準ライブラリのprintf
とは異なり、Goランタイムの初期化段階やデバッグ目的で、よりプリミティブな方法でコンソール出力を行うために使用されます。これは、標準ライブラリが完全に利用可能になる前にデバッグ情報を出力する必要がある場合や、ランタイムのコア部分で依存関係を最小限に抑える必要がある場合に特に重要です。
if(0)
ブロック
C言語においてif(0)
(またはif(false)
)ブロックは、その中のコードがコンパイル時に常に実行されないことを意味します。これは、デバッグ目的で一時的にコードを無効化したり、将来の参照のためにコードスニペットを残しておいたりする際によく使われるイディオムです。コンパイラは通常、このような「デッドコード」を最適化によって削除しますが、構文エラーや未定義変数の参照など、コンパイル時エラーを引き起こす問題は検出します。
技術的詳細
このコミットの技術的な詳細は、C言語のコンパイルプロセスとGoランタイムのクロスプラットフォーム対応の複雑さに関連しています。
runtime·newosproc
関数は、Goランタイムが新しいOSスレッド(M)を起動する際に呼び出されます。この関数は、OS固有のシステムコール(OpenBSDではpthread_create
に相当する低レベルの呼び出し、Plan 9ではrfork
)を使用して、新しいスレッドを作成します。
問題のprintf
文は、新しいOSスレッドが作成される際のデバッグ情報を出力するために存在していました。
元のコードは以下のようになっています(OpenBSDの例):
runtime·printf(
"newosproc stk=%p m=%p g=%p fn=%p id=%d/%d ostk=%p\n",
stk, mp, mp->g0, fn, mp->id, (int32)mp->tls[0], &mp);
ここで、fn
という変数が%p
フォーマット指定子に対応して渡されています。しかし、何らかの理由(例えば、newosproc
関数のシグネチャや内部実装の変更により、fn
という名前の変数が削除された、またはアクセス可能なスコープに存在しなくなった)で、このfn
変数がコンパイル時に見つからなくなっていました。
C言語のコンパイラは、if(0)
ブロック内のコードであっても、構文的に正しいか、参照されている変数が定義されているか、型が一致しているかなどをチェックします。fn
が未定義であるため、コンパイラは「未定義の識別子」エラーを報告し、ビルドが失敗していました。
このコミットでは、fn=%p
とその対応する引数fn
をprintf
呼び出しから削除することで、このコンパイルエラーを解消しています。
修正後のコード(OpenBSDの例):
runtime·printf(
"newosproc stk=%p m=%p g=%p id=%d/%d ostk=%p\n",
stk, mp, mp->g0, mp->id, (int32)mp->tls[0], &mp);
この変更により、printf
文はもはや存在しないfn
変数を参照しなくなり、コンパイルが成功するようになります。if(0)
ブロック内のコードであるため、この変更はGoランタイムの実行時の動作には一切影響を与えません。これは、デバッグコードのクリーンアップと、特定のプラットフォームでのビルドの安定化を目的とした、安全かつ必要な修正です。
コアとなるコードの変更箇所
このコミットでは、以下の2つのファイルが変更されています。
src/pkg/runtime/thread_openbsd.c
src/pkg/runtime/thread_plan9.c
それぞれのファイルで、runtime·newosproc
関数内のデバッグ用runtime·printf
呼び出しから、fn=%p
というフォーマット指定子とその対応する引数fn
が削除されています。
src/pkg/runtime/thread_openbsd.c
の変更点
--- a/src/pkg/runtime/thread_openbsd.c
+++ b/src/pkg/runtime/thread_openbsd.c
@@ -131,8 +131,8 @@ runtime·newosproc(M *mp, void *stk)
if(0) {
runtime·printf(
- "newosproc stk=%p m=%p g=%p fn=%p id=%d/%d ostk=%p\n",
- stk, mp, mp->g0, fn, mp->id, (int32)mp->tls[0], &mp);
+ "newosproc stk=%p m=%p g=%p id=%d/%d ostk=%p\n",
+ stk, mp, mp->g0, mp->id, (int32)mp->tls[0], &mp);
}
mp->tls[0] = mp->id; // so 386 asm can find it
src/pkg/runtime/thread_plan9.c
の変更点
--- a/src/pkg/runtime/thread_plan9.c
+++ b/src/pkg/runtime/thread_plan9.c
@@ -227,8 +227,8 @@ runtime·newosproc(M *mp, void *stk)
{
mp->tls[0] = mp->id; // so 386 asm can find it
if(0){
- runtime·printf("newosproc stk=%p m=%p g=%p fn=%p rfork=%p id=%d/%d ostk=%p\n",
- stk, mp, mp->g0, fn, runtime·rfork, mp->id, (int32)mp->tls[0], &mp);
+ runtime·printf("newosproc stk=%p m=%p g=%p rfork=%p id=%d/%d ostk=%p\n",
+ stk, mp, mp->g0, runtime·rfork, mp->id, (int32)mp->tls[0], &mp);
}
if(runtime·rfork(RFPROC|RFMEM|RFNOWAIT, stk, mp, mp->g0, runtime·mstart) < 0)
コアとなるコードの解説
変更されたコードは、Goランタイムのruntime·newosproc
関数内にあります。この関数は、Goスケジューラが新しいOSスレッド(M)を起動する際に呼び出される、OSに依存する部分です。
変更の対象となっているのは、if(0)
という条件付きコンパイルブロック内にあるruntime·printf
の呼び出しです。
if(0)
: この条件は常に偽であるため、このブロック内のコードはコンパイル時に実行可能コードとしては生成されません。これは、デバッグ目的で一時的にコードを記述し、後で削除する代わりに無効化しておく一般的な手法です。runtime·printf(...)
: Goランタイム内部で使用される低レベルのプリント関数です。通常、Goランタイムの初期化フェーズや、より高度なI/O機能が利用できない状況でのデバッグ出力に使用されます。
元のprintf
文には、fn=%p
というフォーマット指定子が含まれており、これに対応する引数としてfn
という変数が渡されていました。しかし、このfn
変数が、現在のruntime·newosproc
関数の実装において、定義されていないか、アクセス可能なスコープに存在しない状態になっていました。
C言語のコンパイラは、たとえif(0)
ブロック内のデッドコードであっても、構文エラーや未定義変数の参照を検出します。そのため、fn
が未定義であるというコンパイルエラーが発生し、OpenBSDとPlan 9でのGoのビルドが失敗していました。
このコミットでは、printf
のフォーマット文字列からfn=%p
を、そして引数リストからfn
を削除することで、このコンパイルエラーを解消しています。これにより、printf
文はもはや存在しない変数を参照しなくなり、ビルドが正常に完了するようになります。
この修正は、実行時の動作に影響を与えることなく、単にビルドの問題を解決するためのクリーンアップ作業です。これは、Goランタイムのクロスプラットフォーム対応において、各OSのコンパイラやリンカの挙動の違い、あるいは過去のコード変更の残骸が原因で発生する可能性のある、典型的なビルドエラーの修正例と言えます。
関連リンク
- Go Change List (CL) for this commit: https://golang.org/cl/7438052
参考にした情報源リンク
- Go言語の公式ドキュメント (Go Runtime, Scheduler): https://go.dev/doc/
- OpenBSD 公式サイト: https://www.openbsd.org/
- Plan 9 from Bell Labs 公式サイト: https://9p.io/plan9/
- C言語
printf
フォーマット指定子に関する情報 (一般的なC言語の知識): https://en.cppreference.com/w/c/io/fprintf - Go言語のM/P/Gモデルに関する解説 (例: Goスケジューラに関するブログ記事やドキュメント)
- The Go scheduler: https://go.dev/blog/go15scheduler
- Go's work-stealing scheduler: https://rakyll.org/go-scheduler/
- Goランタイムのソースコード (特に
src/runtime
ディレクトリ): https://github.com/golang/go/tree/master/src/runtime