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

[インデックス 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.csrc/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とその対応する引数fnprintf呼び出しから削除することで、このコンパイルエラーを解消しています。 修正後のコード(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つのファイルが変更されています。

  1. src/pkg/runtime/thread_openbsd.c
  2. 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のコンパイラやリンカの挙動の違い、あるいは過去のコード変更の残骸が原因で発生する可能性のある、典型的なビルドエラーの修正例と言えます。

関連リンク

参考にした情報源リンク