[インデックス 12088] ファイルの概要
このコミットは、Go言語のランタイムにおいて、FreeBSDオペレーティングシステム上の386およびamd64アーキテクチャ向けに runtime.osyield
関数を実装するものです。osyield
は、現在のゴルーチン(Goの軽量スレッド)がCPUの実行を他のゴルーチンに譲るためのメカニズムを提供します。これにより、協調的マルチタスク環境でのスケジューリング効率が向上します。
コミット
commit dff5535aaa41970d79bb4179af8108f09fc4d869
Author: Devon H. O'Dell <devon.odell@gmail.com>
Date: Tue Feb 21 07:32:20 2012 +0900
runtime: implement runtime.osyield on FreeBSD 386, amd64
R=rsc, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/5689046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/dff5535aaa41970d79bb4179af8108f09fc4d869
元コミット内容
runtime: implement runtime.osyield on FreeBSD 386, amd64
R=rsc, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/5689046
変更の背景
Goランタイムは、ゴルーチンと呼ばれる軽量な並行処理単位を管理し、OSのスレッド上でこれらをスケジューリングします。runtime.osyield
は、GoランタイムがOSに対して、現在のスレッドが一時的にCPUの実行権を放棄し、他のスレッド(またはプロセス)に譲ることを要求するための関数です。これは、ビジーループ(busy-loop)などでCPUを占有しすぎないようにするためや、他の準備ができたスレッドにCPUを譲ることで、システム全体の応答性を向上させるために使用されます。
このコミット以前は、FreeBSDの386およびamd64アーキテクチャにおいて runtime.osyield
の具体的な実装が欠けており、thread_freebsd.c
内に「TODO: fill this in properly.」というコメントと共に空の関数が置かれていました。このコミットは、そのTODOを解消し、FreeBSD上で sched_yield
システムコールを呼び出すことで、osyield
の機能を提供することを目的としています。これにより、GoランタイムがFreeBSD環境でより効率的にゴルーチンをスケジューリングできるようになります。
前提知識の解説
1. システムコール (System Call)
システムコールは、ユーザー空間で動作するプログラムが、カーネル空間で提供されるサービス(ファイルI/O、メモリ管理、プロセス管理など)を利用するためのインターフェースです。プログラムは直接ハードウェアにアクセスできないため、OSカーネルが提供するシステムコールを介してこれらの操作を行います。システムコールは通常、特定の番号(システムコール番号)によって識別され、アセンブリ言語の INT
命令(x86系)や syscall
命令(x86-64系)などを用いて呼び出されます。
2. アセンブリ言語 (Assembly Language)
アセンブリ言語は、CPUが直接理解できる機械語にほぼ1対1で対応する低レベルプログラミング言語です。特定のCPUアーキテクチャ(例: x86, ARM)に依存し、レジスタ操作、メモリアクセス、算術演算、分岐などの基本的な命令を直接記述します。Goランタイムのような低レベルのコードでは、OSとのインターフェースやパフォーマンスが重要な部分でアセンブリ言語が使用されることがあります。
- x86 (386) アーキテクチャ: 32ビットのIntel互換プロセッサアーキテクチャ。システムコールは通常、システムコール番号を
EAX
レジスタに格納し、INT 0x80
命令を実行することで呼び出されます。 - x86-64 (amd64) アーキテクチャ: 64ビットのIntel互換プロセッサアーキテクチャ。システムコールは通常、システムコール番号を
RAX
レジスタに格納し、syscall
命令を実行することで呼び出されます。ただし、古いシステムや互換性のためにINT 0x80
が使われることもあります。このコミットではINT 0x80
が使用されています。
3. sched_yield
システムコール
sched_yield
は、POSIX標準で定義されているシステムコールの一つで、現在のスレッドがCPUの実行を自発的に放棄し、同じ優先度の他のスレッドにCPUを譲ることをOSスケジューラに示します。これにより、ビジーループによるCPUの無駄な消費を防ぎ、他のスレッドが実行される機会を増やし、システム全体の応答性を向上させることができます。OSスケジューラは、このヒントを元に、次に実行するスレッドを選択します。
4. Goランタイム (Go Runtime)
Goランタイムは、Goプログラムの実行を管理する重要なコンポーネントです。これには、ゴルーチンのスケジューリング、ガベージコレクション、メモリ管理、システムコールインターフェースなどが含まれます。Goの並行処理モデルは、OSスレッドの上にゴルーチンを多重化することで実現されており、ランタイムがゴルーチンとOSスレッド間のマッピングを効率的に行います。
技術的詳細
このコミットの核心は、FreeBSDにおける sched_yield
システムコールの呼び出しを、Goランタイムのアセンブリコードに組み込むことです。
FreeBSDでは、sched_yield
システムコールはシステムコール番号 331
に割り当てられています。
-
386アーキテクチャ (
sys_freebsd_386.s
):TEXT runtime·osyield(SB),7,$-4
は、runtime·osyield
というGo関数に対応するアセンブリコードの開始を示します。SB
はスタックベースポインタ、7
はフレームサイズ、$-4
は引数のサイズを示します。MOVL $331, AX
は、システムコール番号331
をAX
レジスタ(32ビットモードではEAX
の下位16ビット、ここではEAX
全体として扱われる)に移動させます。INT $0x80
は、ソフトウェア割り込み0x80
を発生させ、これによりカーネルがシステムコールを処理します。RET
は、関数から戻ります。
-
amd64アーキテクチャ (
sys_freebsd_amd64.s
):TEXT runtime·osyield(SB),7,$-4
は、runtime·osyield
というGo関数に対応するアセンブリコードの開始を示します。MOVL $331, AX
は、システムコール番号331
をAX
レジスタ(64ビットモードではRAX
の下位16ビット、ここではRAX
全体として扱われる)に移動させます。INT $0x80
は、ソフトウェア割り込み0x80
を発生させ、これによりカーネルがシステムコールを処理します。RET
は、関数から戻ります。
両アーキテクチャで INT $0x80
が使用されているのは、当時のGoのFreeBSDランタイムが、32ビット互換のシステムコールインターフェースを使用していたためと考えられます。現代の64ビットLinuxなどでは syscall
命令が一般的ですが、FreeBSDでは INT 0x80
も引き続きサポートされています。
thread_freebsd.c
からの runtime·osyield
の削除は、C言語で書かれた空のプレースホルダー実装が、アセンブリ言語による実際のシステムコール呼び出しに置き換えられたことを意味します。
コアとなるコードの変更箇所
src/pkg/runtime/sys_freebsd_386.s
--- a/src/pkg/runtime/sys_freebsd_386.s
+++ b/src/pkg/runtime/sys_freebsd_386.s
@@ -299,5 +299,9 @@ TEXT runtime·sysctl(SB),7,$28
MOVL $0, AX
RET
+TEXT runtime·osyield(SB),7,$-4
+\tMOVL $331, AX // sys_sched_yield
+\tINT $0x80
+\tRET
GLOBL runtime·tlsoffset(SB),$4
src/pkg/runtime/sys_freebsd_amd64.s
--- a/src/pkg/runtime/sys_freebsd_amd64.s
+++ b/src/pkg/runtime/sys_freebsd_amd64.s
@@ -229,3 +229,7 @@ TEXT runtime·sysctl(SB),7,$0
MOVL $0, AX
RET
+TEXT runtime·osyield(SB),7,$-4
+\tMOVL $331, AX // sys_sched_yield
+\tINT $0x80
+\tRET
src/pkg/runtime/thread_freebsd.c
--- a/src/pkg/runtime/thread_freebsd.c
+++ b/src/pkg/runtime/thread_freebsd.c
@@ -154,9 +154,3 @@ runtime·sigpanic(void)\n \t}\n \truntime·panicstring(runtime·sigtab[g->sig].name);\n }\n-\n-// TODO: fill this in properly.\n-void\n-runtime·osyield(void)\n-{\n-}\
コアとなるコードの解説
このコミットの主要な変更は、runtime.osyield
関数をアセンブリ言語で実装したことです。
-
sys_freebsd_386.s
およびsys_freebsd_amd64.s
への追加:TEXT runtime·osyield(SB),7,$-4
: これは、Goのruntime.osyield
関数に対応するアセンブリコードのエントリポイントを定義しています。Goのコンパイラは、Goコード内のruntime.osyield()
呼び出しを、このアセンブリコードへのジャンプに変換します。MOVL $331, AX
:MOVL
命令は、即値$331
をAX
レジスタに移動させます。331
はFreeBSDにおけるsched_yield
システムコールの番号です。システムコールを呼び出す際には、通常、システムコール番号を特定のレジスタ(x86/x86-64ではAX
/RAX
)に格納する必要があります。INT $0x80
: これはソフトウェア割り込み命令です。x86およびx86-64アーキテクチャにおいて、INT 0x80
はLinuxやFreeBSDなどのUnix系OSでシステムコールを呼び出すための一般的なメカニズムとして使用されます。この命令が実行されると、CPUはカーネルモードに切り替わり、AX
レジスタに格納されたシステムコール番号に対応するカーネル内の関数が実行されます。この場合、sched_yield
システムコールが実行され、現在のスレッドがCPUを他のスレッドに譲ることをOSに通知します。RET
: システムコールが完了した後、RET
命令はアセンブリ関数から呼び出し元(Goランタイムの他の部分)に戻ります。
-
thread_freebsd.c
からの削除:thread_freebsd.c
には、以前はruntime·osyield
の空のC言語実装が含まれていました。これは、機能がまだ実装されていないことを示すプレースホルダーでした。// TODO: fill this in properly.
というコメントと共に、void runtime·osyield(void) { }
という空の関数が定義されていました。- このコミットにより、アセンブリ言語で実際のシステムコール呼び出しが実装されたため、このC言語のプレースホルダーは不要となり、削除されました。
この変更により、GoランタイムはFreeBSD上で runtime.osyield
を呼び出す際に、OSの sched_yield
機能を利用できるようになり、より効率的なゴルーチンのスケジューリングとシステムリソースの利用が可能になります。
関連リンク
- Go言語のランタイムに関するドキュメント: https://go.dev/doc/go1.1 (Go 1.1のリリースノートなど、当時の情報源を探すのが良いでしょう)
- FreeBSDのシステムコールに関する情報: https://www.freebsd.org/cgi/man.cgi?query=syscall&sektion=2
sched_yield
のPOSIX標準: https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_yield.html
参考にした情報源リンク
- FreeBSD
sched_yield
manページ: https://www.freebsd.org/cgi/man.cgi?query=sched_yield&sektion=2 - Goのソースコードリポジトリ: https://github.com/golang/go
- Goのコードレビューシステム (Gerrit): https://go-review.googlesource.com/ (コミットメッセージに記載されている
https://golang.org/cl/5689046
は、このGerritインスタンスへのリンクです) - x86アセンブリ言語の基本(一般的な情報源)
- x86-64アセンブリ言語の基本(一般的な情報源)
- システムコール呼び出しメカニズムに関する情報(OSの教科書やオンラインリソース)