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

[インデックス 10727] ファイルの概要

このコミットは、Go言語のランタイムがNetBSDオペレーティングシステムをサポートするために必要な変更を導入するものです。具体的には、NetBSD上でのGoプログラムの実行を可能にするための、システムコールインターフェース、シグナルハンドリング、メモリ管理、スレッド作成などの低レベルなOS固有の機能が追加されています。

コミット

commit 26089cfe257dd84d293f63550b3b89351106c478
Author: Christopher Nielsen <m4dh4tt3r@gmail.com>
Date:   Mon Dec 12 18:10:11 2011 -0500

    runtime: Changes to the runtime to support NetBSD.
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/5477052

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/26089cfe257dd84d293f63550b3b89351106c478

元コミット内容

このコミットの元の内容は、GoランタイムがNetBSDをサポートするための変更です。具体的には、src/pkg/runtime/netbsd/ 以下に、386およびamd64アーキテクチャ向けのNetBSD固有のランタイムファイルが多数追加されています。これには、システムコールラッパー、シグナルハンドリング、メモリ管理、スレッド関連のコードが含まれます。

変更の背景

Go言語は、クロスプラットフォーム対応を重視しており、様々なオペレーティングシステム上で動作するように設計されています。このコミットが作成された2011年12月時点では、GoランタイムはまだNetBSDを完全にサポートしていませんでした。この変更の背景には、GoプログラムがNetBSD環境でネイティブに動作できるようにするための、基本的なOSインターフェースの実装が必要であったという点があります。これにより、NetBSDユーザーもGo言語を利用できるようになり、Goのエコシステムがさらに拡大することが期待されました。

前提知識の解説

このコミットを理解するためには、以下の前提知識が役立ちます。

  • Goランタイム (Go Runtime): Go言語のプログラムは、Goランタイムと呼ばれる軽量な実行環境上で動作します。ランタイムは、ガベージコレクション、スケジューリング、メモリ管理、システムコールインターフェース、シグナルハンドリングなど、プログラムの実行に必要な低レベルな機能を提供します。これらの機能はOSに依存するため、各OS向けに最適化された実装が必要です。
  • システムコール (System Call): オペレーティングシステムが提供するサービスをプログラムが利用するためのインターフェースです。ファイルI/O、メモリ割り当て、プロセス管理、ネットワーク通信など、OSのカーネルが提供する機能にアクセスするために使用されます。Goランタイムは、OS固有のシステムコールを直接呼び出すためのラッパーを提供します。
  • シグナル (Signal): オペレーティングシステムがプロセスに送信する非同期の通知です。プログラムの異常終了(セグメンテーション違反、浮動小数点例外など)、外部からの割り込み(Ctrl+C)、タイマーイベントなど、様々なイベントをプロセスに伝えるために使用されます。Goランタイムは、これらのシグナルを捕捉し、適切に処理するためのメカニズムを持っています。
  • メモリ管理 (Memory Management): プログラムがメモリを効率的に利用するための仕組みです。Goランタイムは独自のガベージコレクタを持ちますが、OSからメモリを確保したり解放したりする際には、mmapmunmapといったOSのメモリ管理機能を利用します。
  • スレッドとプロセス (Threads and Processes):
    • プロセス: 実行中のプログラムのインスタンスであり、独立したメモリ空間とリソースを持ちます。
    • スレッド: プロセス内で実行される実行単位であり、プロセス内のメモリ空間を共有します。Go言語のゴルーチンは、OSスレッド上で多重化されて実行される軽量なスレッドです。Goランタイムは、ゴルーチンをOSスレッドにマッピングし、スケジューリングする役割を担います。
  • NetBSD: オープンソースのUnix系オペレーティングシステムの一つで、移植性の高さが特徴です。様々なハードウェアアーキテクチャで動作します。
  • アセンブリ言語 (.sファイル): CPUが直接実行できる機械語に近い低レベルなプログラミング言語です。OSの起動処理やシステムコール呼び出しなど、OSに密接に関わる部分ではアセンブリ言語が使用されることがあります。Goランタイムも、OSとのインターフェース部分でアセンブリ言語を使用しています。
  • rfork_thread (NetBSD固有): NetBSDにおけるforkの拡張版で、プロセスやスレッドの作成をより細かく制御できるシステムコールです。特にRFTHREADフラグを使用することで、新しいスレッドを作成することができます。Goランタイムは、新しいOSスレッドを作成するためにこれを利用します。
  • sysctl (NetBSD固有): カーネルの様々なパラメータを読み書きするためのシステムコールです。このコミットでは、CPUの数を取得するためにhw.ncpuというパラメータを読み取るために使用されています。
  • mmap / munmap: メモリマップドファイルや匿名メモリ領域を操作するためのシステムコールです。mmapはメモリ領域をプロセスのアドレス空間にマッピングし、munmapはマッピングを解除します。Goランタイムは、ヒープ領域の確保などにこれらを使用します。
  • sigaction: シグナルハンドラを設定するためのシステムコールです。シグナルが発生した際にどの関数を呼び出すか、どのようなオプションで処理するかなどを指定します。

技術的詳細

このコミットは、GoランタイムがNetBSD上で動作するために必要な、以下の主要な技術的側面をカバーしています。

  1. OS固有の定数とデータ構造の定義 (defs.h, defs.go):

    • src/pkg/runtime/netbsd/defs.go は、Cgoを使用してNetBSDのシステムヘッダから必要な定数(PROT_NONE, MAP_ANON, SA_SIGINFOなど)やデータ構造(Sigaltstack, Siginfo, Sigcontext, Timespec, Timeval, Itimervalなど)をGoの型として定義しています。
    • これらのGoの定義は、godefsツールによってsrc/pkg/runtime/netbsd/386/defs.h および src/pkg/runtime/netbsd/amd64/defs.h というCヘッダファイルに変換されます。これらのヘッダファイルは、C言語で書かれたランタイムコードやアセンブリコードからNetBSD固有の構造体や定数にアクセスするために使用されます。
  2. ランタイムのエントリポイント (rt0.s):

    • src/pkg/runtime/netbsd/386/rt0.ssrc/pkg/runtime/netbsd/amd64/rt0.s は、GoプログラムがNetBSD上で起動する際のエントリポイントとなるアセンブリコードです。これらは、Goランタイムの初期化ルーチンである_rt0_386または_rt0_amd64にジャンプする役割を担います。OSがプログラムをロードした後、最初に実行されるコードであり、Goランタイムの起動処理を開始します。
  3. システムコールインターフェース (sys.s):

    • src/pkg/runtime/netbsd/386/sys.ssrc/pkg/runtime/netbsd/amd64/sys.s は、GoランタイムがNetBSDのシステムコールを呼び出すためのアセンブリラッパーを提供します。
    • 例えば、runtime·exit (プログラム終了), runtime·write (標準出力への書き込み), runtime·mmap (メモリマップ), runtime·munmap (メモリマップ解除), runtime·sigaction (シグナルハンドラ設定), runtime·rfork_thread (スレッド作成), runtime·sysctl (カーネルパラメータ取得) など、GoランタイムがOSと対話するために必要な基本的なシステムコールが実装されています。
    • これらのラッパーは、適切なシステムコール番号をレジスタに設定し、INT $0x80 (386) または SYSCALL (amd64) 命令を使用してカーネルに制御を渡します。
  4. シグナルハンドリング (signal.c, signals.h):

    • src/pkg/runtime/netbsd/signal.c は、NetBSD上でのシグナル処理の核心部分です。
    • runtime·sighandler は、OSからシグナルが配送された際に呼び出される主要なハンドラです。この関数は、シグナルの種類に応じて、Goのパニック処理 (runtime·sigpanic) をトリガーしたり、プロファイリング情報を収集したり、特定のシグナルを無視したりします。
    • runtime·initsig は、Goランタイムが起動する際に、Goが処理すべきシグナル(例えば、SIGSEGVSIGBUSSIGFPEなどのパニックを引き起こすシグナルや、SIGPROFなどのプロファイリング用シグナル)に対してruntime·sighandlerを登録します。
    • src/pkg/runtime/netbsd/signals.h は、各シグナルの名前と、Goランタイムがそのシグナルをどのように扱うべきかを示すフラグ(SigCatch, SigIgnore, SigPanic, SigQueue, SigRestart)を定義したSigTab構造体を定義しています。
  5. メモリ管理 (mem.c):

    • src/pkg/runtime/netbsd/mem.c は、Goランタイムのメモリ管理サブシステムがNetBSDのメモリ管理機能と連携するためのインターフェースを提供します。
    • runtime·SysAlloc は、OSから実行可能なメモリ領域を割り当てるためにmmapを使用します。
    • runtime·SysFree は、割り当てられたメモリを解放するためにmunmapを使用します。
    • runtime·SysReserveruntime·SysMap は、Goのヒープ管理におけるメモリの予約とマッピングのフェーズをNetBSDのmmapと連携させます。特に64ビットシステムでは、アドレス空間の予約と実際のマッピングを分離して、より効率的なメモリ利用を目指しています。
  6. スレッド管理 (thread.c):

    • src/pkg/runtime/netbsd/thread.c は、Goランタイムが新しいOSスレッドを作成し、管理するためのNetBSD固有のロジックを含んでいます。
    • runtime·newosproc は、GoのM (Machine) を表現する新しいOSスレッドを作成するために、NetBSD固有のrfork_threadシステムコールを使用します。RFPROC | RFTHREAD | RFMEM | RFNOWAITといったフラグを設定することで、新しいスレッドが親プロセスとメモリ空間を共有し、かつ親が子スレッドの終了を待つ必要がないように設定されます。
    • runtime·semasleepruntime·semawakeup は、Goのスケジューラがゴルーチンをブロックしたり、起こしたりするために使用するセマフォ操作を、NetBSDのthrsleepthrwakeupシステムコールを介して実装しています。
    • getncpu 関数は、sysctlシステムコールを使用してシステムのCPU数を取得し、Goランタイムのスケジューラが利用可能なCPUリソースを把握できるようにします。

このコミットは、Go言語の移植性を高め、NetBSDという特定のOS環境でGoプログラムが安定して動作するための基盤を構築する上で不可欠なものです。

コアとなるコードの変更箇所

このコミットは、既存のファイルを変更するのではなく、NetBSDサポートのために新しいファイルを大量に追加しています。そのため、特定の「変更箇所」というよりは、追加されたファイル群全体がコアとなります。

特に重要なファイルは以下の通りです。

  • src/pkg/runtime/netbsd/386/defs.h および src/pkg/runtime/netbsd/amd64/defs.h: NetBSD固有の定数とデータ構造の定義。
  • src/pkg/runtime/netbsd/386/rt0.s および src/pkg/runtime/netbsd/amd64/rt0.s: GoプログラムのNetBSD上でのエントリポイント。
  • src/pkg/runtime/netbsd/386/signal.c および src/pkg/runtime/netbsd/amd64/signal.c: NetBSD固有のシグナルハンドリングロジック。
  • src/pkg/runtime/netbsd/386/sys.s および src/pkg/runtime/netbsd/amd64/sys.s: NetBSD固有のシステムコールラッパー。
  • src/pkg/runtime/netbsd/defs.go: defs.hを生成するためのGoの定義。
  • src/pkg/runtime/netbsd/mem.c: NetBSD固有のメモリ管理の実装。
  • src/pkg/runtime/netbsd/os.h: NetBSD固有のOS関連関数の宣言。
  • src/pkg/runtime/netbsd/signals.h: シグナル定義とGoランタイムでの処理フラグ。
  • src/pkg/runtime/netbsd/thread.c: NetBSD固有のスレッドおよびセマフォ管理の実装。

これらのファイルは、GoランタイムがNetBSDのカーネルと直接対話するための低レベルなインターフェースを提供し、Goの並行処理モデルやメモリモデルをNetBSD上で実現するために不可欠です。

コアとなるコードの解説

ここでは、特に重要な機能を提供するファイルから、代表的なコードスニペットとその解説を行います。

src/pkg/runtime/netbsd/386/sys.s (システムコールラッパーの例)

TEXT runtime·mmap(SB),7,$36
	LEAL	arg0+0(FP), SI
	LEAL	4(SP), DI
	CLD
	MOVSL				// arg 1 - addr
	MOVSL				// arg 2 - len
	MOVSL				// arg 3 - prot
	MOVSL				// arg 4 - flags
	MOVSL				// arg 5 - fd
	MOVL	$0, AX
	STOSL				// arg 6 - pad
	MOVSL				// arg 7 - offset
	MOVL	$0, AX			// top 64 bits of file offset
	STOSL
	MOVL	$197, AX		// sys_mmap
	INT	$0x80
	JCC	2(PC)
	NEGL	AX
	RET

このアセンブリコードは、mmapシステムコールを呼び出すためのGoランタイムのラッパーです。

  • TEXT runtime·mmap(SB),7,$36: runtime·mmapという関数を定義しています。$36はスタックフレームのサイズを示します。
  • LEAL arg0+0(FP), SI など: 関数引数をスタックフレームからレジスタにロードしています。mmapaddr, len, prot, flags, fd, offsetなどの引数を取ります。
  • MOVL $197, AX: AXレジスタにmmapシステムコールの番号(NetBSDの386アーキテクチャでは197)を設定します。
  • INT $0x80: ソフトウェア割り込み0x80を発生させ、カーネルにシステムコールを実行するよう要求します。
  • JCC 2(PC): システムコールが成功したかどうかをチェックします。成功した場合(キャリーフラグがクリア)、次の命令にジャンプします。
  • NEGL AX: システムコールが失敗した場合、AXレジスタにエラーコードが設定されているため、それを負の値に変換して返します(Unix系システムコールの慣習)。

src/pkg/runtime/netbsd/thread.c (スレッド作成の例)

void
runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void))
{
	int32 flags;
	int32 ret;

	flags = RFPROC | RFTHREAD | RFMEM | RFNOWAIT;

	// ... (デバッグ出力など)

	m->tls[0] = m->id;	// so 386 asm can find it

	if((ret = runtime·rfork_thread(flags, stk, m, g, fn)) < 0) {
		runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\\n", runtime·mcount() - 1, -ret);
		if (ret == -ENOTSUP)
			runtime·printf("runtime: is kern.rthreads disabled?\\n");
		runtime·throw("runtime.newosproc");
	}
}

このCコードは、Goランタイムが新しいOSスレッドを作成する際に呼び出されるruntime·newosproc関数です。

  • flags = RFPROC | RFTHREAD | RFMEM | RFNOWAIT;: rfork_threadシステムコールに渡すフラグを設定しています。
    • RFPROC: 新しいプロセスを作成する(この場合はスレッドだが、rforkの基本フラグ)。
    • RFTHREAD: 新しいスレッドを作成する。
    • RFMEM: 親とメモリ空間を共有する。
    • RFNOWAIT: 親プロセスは子スレッドの終了を待つ必要がない。
  • runtime·rfork_thread(flags, stk, m, g, fn): NetBSD固有のrfork_threadシステムコールを呼び出し、新しいOSスレッドを作成します。このスレッドは、stkで指定されたスタックを使用し、fnで指定された関数(通常はGoランタイムのスケジューラ関連の関数)を実行します。mgは、新しいスレッドに渡されるGoのMとG(ゴルーチン)のポインタです。
  • エラーハンドリング: rfork_threadが失敗した場合、エラーメッセージを出力し、runtime·throwを呼び出してパニックを発生させます。特に-ENOTSUPエラーの場合、kern.rthreadsが有効になっているか確認するよう促しています。

src/pkg/runtime/netbsd/signal.c (シグナルハンドラの例)

void
runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
{
	Sigcontext *r = context;
	uintptr *sp;

	if(sig == SIGPROF) {
		runtime·sigprof((uint8*)r->sc_eip, (uint8*)r->sc_esp, nil, gp);
		return;
	}

	if(gp != nil && (runtime·sigtab[sig].flags & SigPanic)) {
		// Make it look like a call to the signal func.
		// ...
		if(r->sc_eip != 0) {
			sp = (uintptr*)r->sc_esp;
			*--sp = r->sc_eip;
			r->sc_esp = (uintptr)sp;
		}
		r->sc_eip = (uintptr)runtime·sigpanic;
		return;
	}

	// ... (その他のシグナル処理、パニック時のトレースバックなど)
}

このCコードは、Goランタイムの主要なシグナルハンドラであるruntime·sighandler関数の一部です。

  • Sigcontext *r = context;: context引数からシグナル発生時のCPUレジスタの状態(Sigcontext構造体)を取得します。これにより、シグナル発生時のプログラムカウンタ(sc_eipまたはsc_rip)やスタックポインタ(sc_espまたはsc_rsp)にアクセスできます。
  • if(sig == SIGPROF): SIGPROF(プロファイリングタイマー)シグナルの処理です。CPUプロファイリングのためにruntime·sigprofを呼び出します。
  • if(gp != nil && (runtime·sigtab[sig].flags & SigPanic)): SigPanicフラグが設定されているシグナル(例: SIGSEGV, SIGBUS, SIGFPE)がGoルーチン内で発生した場合の処理です。
    • このブロックでは、シグナル発生時の実行コンテキストを操作し、runtime·sigpanic関数が呼び出されたかのように見せかけます。具体的には、現在のプログラムカウンタ(r->sc_eip)をスタックにプッシュし、r->sc_eipruntime·sigpanicのアドレスに書き換えます。これにより、シグナルハンドラから戻った際にruntime·sigpanicが実行され、Goのパニック処理が開始されます。これは、Goのランタイムがシグナルを捕捉し、Goのパニックメカニズムに変換するための重要な部分です。

これらのコードスニペットは、GoランタイムがNetBSDの低レベルなOS機能とどのように連携し、Goプログラムの実行環境を構築しているかを示しています。

関連リンク

参考にした情報源リンク

  • Goのソースコード(特にsrc/pkg/runtimeディレクトリ)
  • NetBSDのシステムコールに関するドキュメント(例: man 2 syscalls
  • NetBSDのカーネルソースコード(特にsys/kernsys/archディレクトリ)
  • Goのコミット履歴と関連するコードレビュー(https://golang.org/cl/5477052
  • Goのランタイムに関する一般的な知識と、OSの低レベルプログラミングに関する知識。
  • Goのgodefsツールの機能に関する情報。
  • Unix系OSにおけるシグナル、メモリ管理、プロセス/スレッド管理の概念。
  • アセンブリ言語(x86/x64)の基本的な知識。