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

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

このコミットは、Go言語のOpenBSDランタイムにおける重要な更新を扱っています。具体的には、OpenBSD 5.2で導入された新しいsys___tforkシステムコールへの対応と、スレッドローカルストレージ(TLS)の初期化メカニズムをsys_archからsys___set_tcbシステムコールへ切り替える変更が含まれています。これにより、GoランタイムがOpenBSDの最新のカーネルAPIに準拠し、より効率的かつ正確にスレッド管理とTLS設定を行えるようになります。

コミット

commit cd37fecffbc937432d729510c2dda7e61e39adfb
Author: Joel Sing <jsing@google.com>
Date:   Thu Nov 22 01:25:53 2012 +1100

    runtime: update openbsd runtime to use new tfork syscall
    
    Update OpenBSD runtime to use the new version of the sys___tfork
    syscall and switch TLS initialisation from sys_arch to sys___set_tcb
    (note that both of these syscalls are available in OpenBSD 5.2).
    
    R=golang-dev, minux.ma
    CC=golang-dev
    https://golang.org/cl/6843058

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

https://github.com/golang/go/commit/cd37fecffbc937432d729510c2dda7e61e39adfb

元コミット内容

runtime: update openbsd runtime to use new tfork syscall

OpenBSDランタイムを、sys___tforkシステムコールの新しいバージョンを使用するように更新し、TLS初期化をsys_archからsys___set_tcbに切り替える(これらのシステムコールは両方ともOpenBSD 5.2で利用可能)。

変更の背景

この変更の背景には、OpenBSDオペレーティングシステムの進化があります。OpenBSDはセキュリティとコードの簡潔さを重視するOSであり、そのカーネルAPIはバージョンアップに伴い変更されることがあります。OpenBSD 5.2では、スレッド生成に関連するtforkシステムコールと、スレッドローカルストレージ(TLS)の設定に関連するシステムコールに重要な変更が加えられました。

Go言語のランタイムは、OSの低レベルな機能(スレッド生成、メモリ管理、シグナルハンドリングなど)と直接対話するため、OSのAPI変更には迅速に対応する必要があります。特に、Goの並行処理モデル(ゴルーチン)はOSのスレッドの上に構築されており、新しいスレッドを効率的に生成するtforkのようなシステムコールはランタイムのパフォーマンスと安定性に直結します。

また、TLSはGoランタイムが各ゴルーチンやOSスレッドに固有のデータを効率的に管理するために不可欠です。以前使用されていたsys_archは汎用的なアーキテクチャ固有の操作を行うシステムコールでしたが、OpenBSD 5.2で導入されたsys___set_tcbは、より直接的かつセキュアにスレッド制御ブロック(TCB)を設定するための専用システムコールです。この新しいAPIを利用することで、TLSの初期化がより堅牢かつ効率的になることが期待されます。

したがって、このコミットは、GoランタイムがOpenBSD 5.2以降の環境で正しく動作し、その新しいAPIの利点を活用できるようにするための互換性および最適化の更新です。

前提知識の解説

このコミットを理解するためには、以下の技術的な概念について理解しておく必要があります。

  1. Goランタイム (Go Runtime): Go言語のプログラムは、Goランタイムと呼ばれる軽量な実行環境上で動作します。Goランタイムは、ガベージコレクション、スケジューリング(ゴルーチンの管理)、メモリ割り当て、システムコールインターフェースなど、Goプログラムの実行に必要な低レベルな機能を提供します。OSと直接対話する部分が多く、OSのAPI変更に敏感です。

  2. システムコール (System Call): アプリケーションプログラムがオペレーティングシステム(OS)のカーネルが提供するサービスを利用するためのインターフェースです。ファイルI/O、プロセス管理、メモリ管理、ネットワーク通信など、OSの機能にアクセスする際に使用されます。システムコールは通常、アセンブリ言語で記述されたラッパー関数を介して呼び出されます。

  3. tfork システムコール (OpenBSD特有): OpenBSDにおけるスレッド(または軽量プロセス)生成のためのシステムコールです。一般的なUnix系OSのforkcloneに似ていますが、OpenBSDの設計思想に基づいた独自の実装です。Goランタイムは、新しいOSスレッド(M: Machine)を生成する際にこのtforkシステムコールを利用します。このコミットでは、このtforkシステムコールのAPIがOpenBSD 5.2で変更されたことに対応しています。

  4. スレッドローカルストレージ (TLS: Thread Local Storage): 各スレッドがそれぞれ独立したデータを持つためのメカニズムです。グローバル変数とは異なり、TLSに格納されたデータは同じプロセス内の他のスレッドからは直接アクセスできません。Goランタイムでは、現在のゴルーチン(G)やOSスレッド(M)に関する情報など、スレッド固有のデータをTLSに格納して高速にアクセスします。

  5. sys_arch システムコール (OpenBSD特有): OpenBSDにおける汎用的なアーキテクチャ固有の操作を行うためのシステムコールです。TLSベースレジスタの設定など、特定のCPUアーキテクチャに依存する低レベルな設定に使用されることがあります。

  6. sys___set_tcb システムコール (OpenBSD特有): OpenBSD 5.2で導入された、スレッド制御ブロック(TCB: Thread Control Block)を設定するための新しいシステムコールです。TCBは、スレッドの状態やコンテキスト、そしてTLSのベースアドレスなど、スレッドに関する重要な情報が格納されるメモリ領域です。sys___set_tcbは、TLSのベースアドレスをより直接的かつ効率的に設定するために設計されました。

  7. アセンブリ言語 (Assembly Language): Goランタイムの低レベルな部分、特にシステムコールを直接呼び出す部分や、パフォーマンスが非常に重要な部分では、C言語ではなくアセンブリ言語が使用されます。これは、OSのABI(Application Binary Interface)に厳密に準拠し、レジスタの使用やスタックフレームの管理を細かく制御するためです。このコミットでは、sys_openbsd_386.ssys_openbsd_amd64.sというアセンブリファイルが変更されています。

  8. struct Tfork: tforkシステムコールに渡されるパラメータを定義する構造体です。この構造体の定義がOSのAPI変更に合わせて更新されることがあります。

技術的詳細

このコミットの技術的な変更点は大きく分けて二つあります。

1. sys___tfork システムコールのAPI変更への対応

OpenBSD 5.2では、tforkシステムコールのインターフェースが変更されました。

  • Tfork構造体の変更: 以前はtf_flagsというフィールドが含まれていましたが、これがtf_stackというスタックポインタを直接渡すフィールドに置き換えられました。これは、tforkが新しいスレッドのスタックをより直接的に管理するようになったことを示唆しています。
    • src/pkg/runtime/defs_openbsd_386.h および src/pkg/runtime/defs_openbsd_amd64.h でこの変更が反映されています。
  • システムコール番号の変更: sys___tforkのシステムコール番号が、以前の328から8に変更されました。これは、OS側でシステムコールの定義が更新されたことを意味します。
    • src/pkg/runtime/sys_openbsd_386.s および src/pkg/runtime/sys_openbsd_amd64.s のアセンブリコードで、システムコール呼び出しの際に使用するレジスタ(AX)に設定する値が変更されています。
  • tfork関数の引数変更: Goランタイム内のruntime·tfork_thread関数(新しいコミットではruntime·tforkに改名)のシグネチャが変更されました。以前はスタックポインタを直接渡していましたが、新しいAPIではTfork構造体のサイズ(psize)を渡すようになりました。これは、tforkシステムコールが内部でTfork構造体全体を処理し、その中に含まれるtf_stackフィールドを利用するようになったためです。
    • src/pkg/runtime/thread_openbsd.cruntime·tforkの呼び出しがsizeof(param)を渡すように変更されています。
  • スタック切り替えロジックの削除: 以前のアセンブリコードには、子スレッドが新しいスタックに切り替えるための明示的な命令(MOVL SI, SPMOVQ SI, SP)が含まれていましたが、これが削除されました。これは、新しいtforkシステムコールがスタックの切り替えを内部で処理するようになったためと考えられます。これにより、Goランタイム側での低レベルなスタック管理の複雑さが軽減されます。

2. TLS初期化メカニズムのsys_archからsys___set_tcbへの切り替え

  • システムコール番号の変更: TLSのベースアドレスを設定するために、以前はsys_sysarch(システムコール番号165)とI386_SET_GSBASEまたはAMD64_SET_FSBASEというサブコマンドを使用していました。このコミットでは、代わりにsys___set_tcb(システムコール番号329)を直接呼び出すように変更されました。
    • src/pkg/runtime/sys_openbsd_386.s および src/pkg/runtime/sys_openbsd_amd64.sruntime·settls関数でこの変更が確認できます。
  • TLSベースアドレスの調整: OpenBSDのELFバイナリでは、TLSのベースアドレスが特定のオフセット(386では-8(GS)、amd64では-16(FS))を考慮して設定される必要があります。このコミットでは、sys___set_tcbに渡す前にこのオフセットを調整するロジックが引き続き維持されています。これは、Goランタイムが期待するTLSレイアウトとOSが期待するレイアウトの間の整合性を保つためです。

これらの変更は、GoランタイムがOpenBSD 5.2以降のカーネルAPIに完全に準拠し、より効率的で安定したスレッドおよびTLS管理を実現するために不可欠です。特に、新しい専用のシステムコールを使用することで、よりセキュアで最適化された方法でOSリソースを扱うことが可能になります。

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

src/pkg/runtime/defs_openbsd_386.h および src/pkg/runtime/defs_openbsd_amd64.h

struct Tfork の定義変更:

--- a/src/pkg/runtime/defs_openbsd_386.h
+++ b/src/pkg/runtime/defs_openbsd_386.h
@@ -85,7 +85,7 @@ typedef struct Itimerval Itimerval;\
 struct Tfork {
  	byte	*tf_tcb;
  	int32	*tf_tid;
- 	int32	tf_flags;
+ 	byte	*tf_stack;
 };
 
 struct Sigaltstack {

(amd64版も同様にtf_flagsPad_cgo_0tf_stackに置き換えられています。)

src/pkg/runtime/sys_openbsd_386.s および src/pkg/runtime/sys_openbsd_amd64.s

runtime·tfork (旧 runtime·tfork_thread) の変更点(386版の抜粋):

--- a/src/pkg/runtime/sys_openbsd_386.s
+++ b/src/pkg/runtime/sys_openbsd_386.s
@@ -190,12 +190,14 @@ TEXT runtime·sigtramp(SB),7,$44
  	MOVL	$0xf1, 0xf1		// crash
  	RET
 
-// int32 tfork_thread(void *param, void *stack, M *m, G *g, void (*fn)(void));
-TEXT runtime·tfork_thread(SB),7,$8
+// int32 tfork(void *param, uintptr psize, M *m, G *g, void (*fn)(void));
+TEXT runtime·tfork(SB),7,$12
 
-	// Copy m, g, fn off parent stack and onto the child stack.
-	MOVL	stack+8(FP), CX
+	// Copy m, g and fn from the parent stack onto the child stack.
+	MOVL	params+4(FP), AX
+	MOVL	8(AX), CX		// tf_stack
  	SUBL	$16, CX
+	MOVL	CX, 8(AX)
  	MOVL	mm+12(FP), SI
  	MOVL	SI, 0(CX)
  	MOVL	gg+16(FP), SI
@@ -203,12 +205,13 @@ TEXT runtime·tfork_thread(SB),7,$8
  	MOVL	fn+20(FP), SI
  	MOVL	SI, 8(CX)
  	MOVL	$1234, 12(CX)
-	MOVL	CX, SI
 
  	MOVL	$0, 0(SP)		// syscall gap
  	MOVL	params+4(FP), AX
  	MOVL	AX, 4(SP)		// arg 1 - param
-	MOVL	$328, AX		// sys___tfork
+	MOVL	psize+8(FP), AX
+	MOVL	AX, 8(SP)		// arg 2 - psize
+	MOVL	$8, AX			// sys___tfork
  	INT	$0x80
 
  	// Return if tfork syscall failed.
@@ -225,9 +228,6 @@ TEXT runtime·tfork_thread(SB),7,$8
  	MOVL	AX, 0(DX)
  	RET
 
-	// In child, switch to new stack.
-	MOVL    SI, SP
-
  	// Paranoia: check that SP is as we expect.
  	MOVL	12(SP), BP
  	CMPL	BP, $1234

runtime·settls の変更点(386版の抜粋):

--- a/src/pkg/runtime/sys_openbsd_386.s
+++ b/src/pkg/runtime/sys_openbsd_386.s
@@ -278,22 +278,20 @@ TEXT runtime·sigaltstack(SB),7,$-8
  	INT	$3
  	RET
 
-TEXT runtime·setldt(SB),7,$8
+TEXT runtime·setldt(SB),7,$4
  	// Under OpenBSD we set the GS base instead of messing with the LDT.
-\tMOVL\t16(SP), AX\t\t// tls0
+\tMOVL\ttls0+4(FP), AX
  	MOVL	AX, 0(SP)
  	CALL	runtime·settls(SB)
  	RET
 
-TEXT runtime·settls(SB),7,$16
+TEXT runtime·settls(SB),7,$8
  	// adjust for ELF: wants to use -8(GS) and -4(GS) for g and m
-\tMOVL\t20(SP), CX
+\tMOVL\ttlsbase+0(FP), CX
  	ADDL	$8, CX
-\tMOVL\tCX, 0(CX)
  	MOVL	$0, 0(SP)		// syscall gap
-\tMOVL\t$9, 4(SP)		// I386_SET_GSBASE (machine/sysarch.h)
-\tMOVL\tCX, 8(SP)		// pointer to base
-\tMOVL\t$165, AX		// sys_sysarch
+\tMOVL\tCX, 4(SP)		// arg 1 - tcb
+\tMOVL\t$329, AX		// sys___set_tcb
  	INT	$0x80
  	JCC	2(PC)
  	MOVL	$0xf1, 0xf1		// crash

src/pkg/runtime/thread_openbsd.c

runtime·newosproc 関数内の tfork 呼び出しの変更:

--- a/src/pkg/runtime/thread_openbsd.c
+++ b/src/pkg/runtime/thread_openbsd.c
@@ -23,7 +23,7 @@ extern SigTab runtime·sigtab[];
 static Sigset sigset_all = ~(Sigset)0;\
 static Sigset sigset_none;\
 \
-extern int64 runtime·tfork_thread(void *param, void *stack, M *m, G *g, void (*fn)(void));\
+extern int64 runtime·tfork(void *param, uintptr psize, M *m, G *g, void (*fn)(void));\
 extern int32 runtime·thrsleep(void *ident, int32 clock_id, void *tsp, void *lock, const int32 *abort);\
 extern int32 runtime·thrwakeup(void *ident, int32 n);\
 \
@@ -139,10 +139,10 @@ runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void))\
 \
 	param.tf_tcb = (byte*)&m->tls[0];\
 	param.tf_tid = (int32*)&m->procid;\
-	param.tf_flags = (int32)0;\
+	param.tf_stack = stk;\
 \
 	oset = runtime·sigprocmask(SIG_SETMASK, sigset_all);\
-\tret = runtime·tfork_thread((byte*)&param, stk, m, g, fn);\
+\tret = runtime·tfork((byte*)&param, sizeof(param), m, g, fn);\
 	runtime·sigprocmask(SIG_SETMASK, oset);\
 \
 	if(ret < 0) {

コアとなるコードの解説

defs_openbsd_*.h の変更

struct Tfork の定義変更は、OpenBSDカーネルがtforkシステムコールに期待する引数の構造が変更されたことを直接的に反映しています。

  • tf_flags (フラグ) から tf_stack (スタックポインタ) への変更は、tforkシステムコールが、新しいスレッドのスタックをより直接的に、かつ明示的に指定するようになったことを示します。これにより、Goランタイムは新しいスレッドのスタックアドレスを直接カーネルに渡すことができ、カーネル側でスタックの初期設定をより効率的に行えるようになります。

sys_openbsd_*.s の変更

アセンブリコードの変更は、GoランタイムがOpenBSDカーネルと直接対話する部分の核心です。

  • runtime·tfork (旧 runtime·tfork_thread):
    • 関数名の変更は、Goランタイム内部での命名規則の統一またはAPIの明確化を示唆しています。
    • システムコール番号が328から8に変更されたのは、OpenBSD 5.2でsys___tforkのシステムコール番号が更新されたためです。Goランタイムは、この新しい番号を使用してカーネルの正しいエントリポイントを呼び出す必要があります。
    • 引数の渡し方が変更され、psize (パラメータ構造体のサイズ) が渡されるようになったのは、tforkシステムコールがTfork構造体全体を読み取り、その中のtf_stackフィールドを利用してスタックを設定するようになったためです。
    • 子スレッドでのスタック切り替えロジック(MOVL SI, SPなど)が削除されたのは、新しいtforkシステムコールが、子スレッドの実行開始時に既に適切なスタックが設定されていることを保証するようになったためです。これにより、Goランタイムは低レベルなスタック管理の負担を軽減できます。
  • runtime·settls:
    • TLS初期化のシステムコールがsys_sysarch(番号165)からsys___set_tcb(番号329)に変更されました。これは、OpenBSD 5.2でTLS設定のためのより専用かつ効率的なシステムコールが導入されたためです。sys___set_tcbは、スレッド制御ブロック(TCB)のベースアドレスを直接設定することを目的としており、TLSの初期化をよりセキュアかつ直接的に行えます。
    • sys_sysarchは汎用的なシステムコールであり、TLS設定はその機能の一部に過ぎませんでした。sys___set_tcbへの移行は、より適切なAPIを使用することで、コードの意図を明確にし、将来的な互換性やパフォーマンスの向上に寄与します。

thread_openbsd.c の変更

C言語のコードの変更は、アセンブリレベルの変更をGoランタイムのより高レベルな部分に統合する役割を果たします。

  • param.tf_flags = (int32)0;param.tf_stack = stk; に変更されたのは、Tfork構造体の定義変更に直接対応しています。これにより、Goランタイムは新しいスレッドのスタックポインタをTfork構造体を通じてカーネルに渡すことができます。
  • runtime·tfork_thread((byte*)&param, stk, m, g, fn);runtime·tfork((byte*)&param, sizeof(param), m, g, fn); に変更されたのは、新しいtforkシステムコールのAPIに合わせた引数の変更です。sizeof(param)を渡すことで、カーネルはTfork構造体の正確なサイズを知ることができ、安全にデータを処理できます。

これらの変更は、GoランタイムがOpenBSDの最新のカーネルAPIと密接に連携し、その進化の恩恵を受けるための基盤を構築しています。

関連リンク

  • OpenBSD 5.2 Release: https://www.openbsd.org/52.html (OpenBSD 5.2のリリースノートで、システムコールに関する変更点が含まれている可能性があります。)
  • Go CL 6843058: https://golang.org/cl/6843058 (このコミットに対応するGoの変更リスト(Code Review)ページ。より詳細な議論や背景情報が含まれている可能性があります。)

参考にした情報源リンク

  • OpenBSD man pages (tfork(2), set_tcb(2), sysarch(2) など): OpenBSDの公式ドキュメントは、システムコールの詳細な動作とAPIについて最も正確な情報源です。
  • Go言語のランタイムに関するドキュメントやソースコード: Goのランタイムの内部動作を理解するために参照しました。
  • ELF ABI for various architectures: TLSのオフセットやレジスタの使用に関する情報は、ELF(Executable and Linkable Format)のABI仕様に記載されています。
  • GoのIssueトラッカーやメーリングリストのアーカイブ: 過去の議論から、特定のOS対応の背景や技術的課題について情報を得ることがあります。