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

[インデックス 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 は、システムコール番号 331AX レジスタ(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 は、システムコール番号 331AX レジスタ(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 命令は、即値 $331AX レジスタに移動させます。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 機能を利用できるようになり、より効率的なゴルーチンのスケジューリングとシステムリソースの利用が可能になります。

関連リンク

参考にした情報源リンク

  • 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の教科書やオンラインリソース)